refactor: migrate vendor APIs to token-based context and consolidate architecture

## Vendor-in-Token Architecture (Complete Migration)
- Migrate all vendor API endpoints from require_vendor_context() to token_vendor_id
- Update permission dependencies to extract vendor from JWT token
- Add vendor exceptions: VendorAccessDeniedException, VendorOwnerOnlyException,
  InsufficientVendorPermissionsException
- Shop endpoints retain require_vendor_context() for URL-based detection
- Add AUTH-004 architecture rule enforcing vendor context patterns
- Fix marketplace router missing /marketplace prefix

## Exception Pattern Fixes (API-003/API-004)
- Services raise domain exceptions, endpoints let them bubble up
- Add code_quality and content_page exception modules
- Move business logic from endpoints to services (admin, auth, content_page)
- Fix exception handling in admin, shop, and vendor endpoints

## Tailwind CSS Consolidation
- Consolidate CSS to per-area files (admin, vendor, shop, platform)
- Remove shared/cdn-fallback.html and shared/css/tailwind.min.css
- Update all templates to use area-specific Tailwind output files
- Remove Node.js config (package.json, postcss.config.js, tailwind.config.js)

## Documentation & Cleanup
- Update vendor-in-token-architecture.md with completed migration status
- Update architecture-rules.md with new rules
- Move migration docs to docs/development/migration/
- Remove duplicate/obsolete documentation files
- Merge pytest.ini settings into pyproject.toml

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-04 22:24:45 +01:00
parent 76f8a59954
commit 8a367077e1
85 changed files with 21787 additions and 134978 deletions

View File

@@ -136,32 +136,36 @@ async def create_vendor(vendor: VendorCreate, db: Session = Depends(get_db)):
- `db.commit()`
- `db.query(`
#### API-003: Catch Service Exceptions
#### API-003: No HTTPException in Endpoints
**Severity:** Error
API endpoints must catch domain exceptions from services and convert them to appropriate HTTPException with proper status codes.
API endpoints must NOT raise HTTPException directly. Instead, let domain exceptions bubble up to the global exception handler which converts them to appropriate HTTP responses.
```python
# ✅ Good
# ✅ Good - Let domain exceptions bubble up
@router.post("/vendors")
async def create_vendor(vendor: VendorCreate, db: Session = Depends(get_db)):
try:
result = vendor_service.create_vendor(db, vendor)
return result
except VendorAlreadyExistsError as e:
raise HTTPException(status_code=409, detail=str(e))
except Exception as e:
logger.error(f"Unexpected error: {e}")
raise HTTPException(status_code=500, detail="Internal server error")
# Service raises VendorAlreadyExistsException if duplicate
# Global handler converts to 409 Conflict
return vendor_service.create_vendor(db, vendor)
# ❌ Bad - Don't raise HTTPException directly
@router.post("/vendors")
async def create_vendor(vendor: VendorCreate, db: Session = Depends(get_db)):
if vendor_service.exists(db, vendor.subdomain):
raise HTTPException(status_code=409, detail="Vendor exists") # BAD!
return vendor_service.create_vendor(db, vendor)
```
**Pattern:** Services raise domain exceptions → Global handler converts to HTTP responses
#### API-004: Proper Authentication
**Severity:** Warning
Protected endpoints must use Depends() for authentication.
```python
# ✅ Good
# ✅ Good - Protected endpoint with authentication
@router.post("/vendors")
async def create_vendor(
vendor: VendorCreate,
@@ -171,6 +175,31 @@ async def create_vendor(
pass
```
**Auto-Excluded Files:**
The validator automatically skips API-004 checks for authentication endpoint files (`*/auth.py`) since login, logout, and registration endpoints are intentionally public.
**Marking Public Endpoints:**
For other intentionally public endpoints (webhooks, health checks, etc.), use a comment marker:
```python
# ✅ Good - Webhook endpoint marked as public
# public - Stripe webhook receives external callbacks
@router.post("/webhook/stripe")
def stripe_webhook(request: Request):
...
# ✅ Good - Using noqa style
@router.post("/health") # noqa: API-004
def health_check():
...
```
**Recognized markers:**
- `# public` - descriptive marker for intentionally unauthenticated endpoints
- `# noqa: API-004` - standard noqa style to suppress the warning
#### API-005: Multi-Tenant Scoping
**Severity:** Error
@@ -542,12 +571,41 @@ async loadData() {
#### TPL-001: Admin Templates Extend admin/base.html
**Severity:** Error
All admin templates must extend the base template for consistent layout (sidebar, navigation, etc.).
```jinja
{% extends "admin/base.html" %}
❌ No extends directive
{# ✅ Good - Extends base template #}
{% extends "admin/base.html" %}
{% block content %}
...
{% endblock %}
```
Exceptions: `base.html` itself, files in `partials/`
**Auto-Excluded Files:**
The validator automatically skips TPL-001 checks for:
- `login.html` - Standalone login page (no sidebar/navigation needed)
- `errors/*.html` - Error pages extend `errors/base.html` instead
- `test-*.html` - Test/development templates
- `base.html` - The base template itself
- `partials/*.html` - Partial templates included in other templates
**Marking Standalone Templates:**
For other templates that intentionally don't extend base.html, use a comment marker in the first 5 lines:
```jinja
{# standalone - Minimal monitoring page without admin chrome #}
<!DOCTYPE html>
<html>
...
```
**Recognized markers:**
- `{# standalone #}` - Jinja comment style
- `{# noqa: TPL-001 #}` - Standard noqa style
- `<!-- standalone -->` - HTML comment style
#### TPL-002: Vendor Templates Extend vendor/base.html
**Severity:** Error
@@ -789,12 +847,13 @@ Before committing code:
| Violation | Quick Fix |
|-----------|-----------|
| HTTPException in service | Create custom exception in `app/exceptions/` |
| HTTPException in endpoint | Let domain exceptions bubble up to global handler |
| Business logic in endpoint | Move to service layer |
| No exception handling | Add try/except, convert to HTTPException |
| console.log in JS | Use `window.LogConfig.createLogger()` |
| Missing ...data() | Add spread operator in component return |
| Bare except clause | Specify exception type |
| Raw dict return | Create Pydantic response model |
| Template not extending base | Add `{% extends %}` or `{# standalone #}` marker |
---
@@ -834,5 +893,5 @@ All rules are defined in `.architecture-rules.yaml`. To modify rules:
---
**Last Updated:** 2025-11-28
**Version:** 2.0
**Last Updated:** 2025-12-04
**Version:** 2.2

View File

@@ -324,5 +324,5 @@ Migration failures will halt deployment to prevent data corruption.
## Further Reading
- [Alembic Official Documentation](https://alembic.sqlalchemy.org/)
- [Database Setup Guide](../getting-started/database-setup-guide.md)
- [Deployment Guide](../deployment/production.md)
- [Database Seeder Documentation](../database-seeder/database-seeder-documentation.md)
- [Database Init Guide](../database-seeder/database-init-guide.md)

View File

@@ -0,0 +1,402 @@
# Tailwind CSS Migration Plan: v1.4/v2.2 → v4.0 (Standalone CLI)
**Created:** December 2024
**Completed:** December 2024
**Status:** COMPLETE
**Goal:** Eliminate Node.js dependency, use Tailwind Standalone CLI
> **Migration Complete!** All frontends now use Tailwind CSS v4 with the standalone CLI.
> See [Tailwind CSS Build Guide](../../frontend/tailwind-css.md) for current documentation.
---
## Current State
### Two Tailwind Setups (Before Migration)
| Component | Version | Source | Purpose |
|-----------|---------|--------|---------|
| Base styles | 2.2.19 | CDN + local fallback | Core Tailwind utilities for all frontends |
| Custom overrides | 1.4.6 | npm build | Windmill Dashboard theme (admin/vendor) |
### Current Files (Before Migration)
```
package.json # tailwindcss: 1.4.6 (TO BE REMOVED)
package-lock.json # (TO BE REMOVED)
node_modules/ # (TO BE REMOVED)
tailwind.config.js # v1.4 format config (TO BE UPDATED)
postcss.config.js # (TO BE REMOVED)
static/shared/css/tailwind.min.css # CDN fallback v2.2.19 (TO BE REMOVED)
static/admin/css/tailwind.output.css # Built overrides (TO BE REBUILT)
static/vendor/css/tailwind.output.css # Built overrides (TO BE REBUILT)
```
### Current Plugins (TO BE REPLACED)
```json
{
"@tailwindcss/custom-forms": "0.2.1", // DEPRECATED - replaced by @tailwindcss/forms
"tailwindcss-multi-theme": "1.0.3" // DEPRECATED - native darkMode in v3+
}
```
---
## Migration Goals
1. ✅ Eliminate Node.js dependency entirely
2. ✅ Use Tailwind Standalone CLI (single binary, no npm)
3. ✅ Upgrade to Tailwind v4.0 (latest)
4. ✅ Remove CDN dependency (all CSS built locally)
5. ✅ Update config to v4 format
6. ✅ Replace deprecated plugins with native features
7. ✅ Consolidate to single Tailwind version
8. ✅ Verify all frontends work correctly
---
## Step-by-Step Migration
### Phase 1: Install Tailwind Standalone CLI
The standalone CLI bundles Tailwind + Node.js runtime into a single binary.
```bash
# Download latest standalone CLI for Linux x64
curl -sLO https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-linux-x64
chmod +x tailwindcss-linux-x64
sudo mv tailwindcss-linux-x64 /usr/local/bin/tailwindcss
# Verify installation
tailwindcss --version
```
**For other platforms:**
- macOS (Intel): `tailwindcss-macos-x64`
- macOS (Apple Silicon): `tailwindcss-macos-arm64`
- Windows: `tailwindcss-windows-x64.exe`
- Linux ARM: `tailwindcss-linux-arm64`
### Phase 2: Update tailwind.config.js
Replace the entire config file with v4-compatible format:
```javascript
// tailwind.config.js - Tailwind CSS v4.0 (Standalone CLI)
/** @type {import('tailwindcss').Config} */
module.exports = {
// v4: 'content' replaces 'purge'
content: [
'app/templates/**/*.html',
'static/**/*.js',
],
// v4: safelist for dynamic classes (Alpine.js)
safelist: [
'bg-orange-600',
'bg-green-600',
'bg-red-600',
'bg-blue-600',
'bg-purple-600',
'hover:bg-orange-700',
'hover:bg-green-700',
'hover:bg-red-700',
'hover:bg-blue-700',
'hover:bg-purple-700',
// Status colors
'text-green-600',
'text-red-600',
'text-yellow-600',
'text-blue-600',
'bg-green-100',
'bg-red-100',
'bg-yellow-100',
'bg-blue-100',
],
// v4: darkMode replaces tailwindcss-multi-theme
darkMode: 'class',
theme: {
extend: {
// Custom colors from Windmill Dashboard
colors: {
gray: {
50: '#f9fafb',
100: '#f4f5f7',
200: '#e5e7eb',
300: '#d5d6d7',
400: '#9e9e9e',
500: '#707275',
600: '#4c4f52',
700: '#24262d',
800: '#1a1c23',
900: '#121317',
},
purple: {
50: '#f6f5ff',
100: '#edebfe',
200: '#dcd7fe',
300: '#cabffd',
400: '#ac94fa',
500: '#9061f9',
600: '#7e3af2',
700: '#6c2bd9',
800: '#5521b5',
900: '#4a1d96',
},
},
fontFamily: {
sans: ['Inter', 'system-ui', '-apple-system', 'sans-serif'],
},
maxHeight: {
'xl': '36rem',
},
},
},
plugins: [
// Note: Standalone CLI includes @tailwindcss/forms by default in v4
// No external plugins needed
],
}
```
### Phase 3: Update CSS Source Files
Update `static/admin/css/tailwind.css`:
```css
/* Tailwind CSS v4 directives */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Custom component classes */
@layer components {
.form-input {
@apply block w-full rounded-md border-gray-300 shadow-sm
focus:border-purple-500 focus:ring-purple-500;
}
.form-select {
@apply block w-full rounded-md border-gray-300 shadow-sm
focus:border-purple-500 focus:ring-purple-500;
}
.form-checkbox {
@apply rounded border-gray-300 text-purple-600
focus:ring-purple-500;
}
.form-textarea {
@apply block w-full rounded-md border-gray-300 shadow-sm
focus:border-purple-500 focus:ring-purple-500;
}
}
/* Dark mode base overrides */
@layer base {
.dark body {
@apply bg-gray-900 text-gray-200;
}
}
```
### Phase 4: Add Makefile Targets
Add these targets to your Makefile (replaces npm scripts):
```makefile
# =============================================================================
# Tailwind CSS (Standalone CLI - No Node.js Required)
# =============================================================================
.PHONY: tailwind-install tailwind-dev tailwind-build tailwind-watch
# Install Tailwind standalone CLI
tailwind-install:
@echo "Installing Tailwind CSS standalone CLI..."
curl -sLO https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-linux-x64
chmod +x tailwindcss-linux-x64
sudo mv tailwindcss-linux-x64 /usr/local/bin/tailwindcss
@echo "Tailwind CLI installed: $$(tailwindcss --version)"
# Development build (includes all classes, faster)
tailwind-dev:
@echo "Building Tailwind CSS (development)..."
tailwindcss -i static/admin/css/tailwind.css -o static/admin/css/tailwind.output.css
tailwindcss -i static/admin/css/tailwind.css -o static/vendor/css/tailwind.output.css
@echo "CSS built successfully"
# Production build (purged and minified)
tailwind-build:
@echo "Building Tailwind CSS (production)..."
tailwindcss -i static/admin/css/tailwind.css -o static/admin/css/tailwind.output.css --minify
tailwindcss -i static/admin/css/tailwind.css -o static/vendor/css/tailwind.output.css --minify
@echo "CSS built and minified successfully"
# Watch mode for development
tailwind-watch:
@echo "Watching for CSS changes..."
tailwindcss -i static/admin/css/tailwind.css -o static/admin/css/tailwind.output.css --watch
```
### Phase 5: Update Dark Mode Implementation
**Old approach (tailwindcss-multi-theme):**
```html
<body class="theme-dark">
<div class="bg-white dark:bg-gray-800">
```
**New approach (Tailwind v4 native):**
```html
<html class="dark">
<body>
<div class="bg-white dark:bg-gray-800">
```
**Files to update:**
1. `app/templates/admin/base.html` - Add `dark` class to `<html>` element
2. `app/templates/vendor/base.html` - Add `dark` class to `<html>` element
3. `static/admin/js/init-alpine.js` - Update dark mode toggle logic
4. `static/vendor/js/init-alpine.js` - Update dark mode toggle logic
**JavaScript update:**
```javascript
// Old
document.body.classList.toggle('theme-dark');
// New
document.documentElement.classList.toggle('dark');
```
### Phase 6: Update Base Templates
**Remove CDN loading, use only local built CSS:**
```html
<!-- OLD: CDN with fallback (REMOVE THIS) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css"
onerror="this.onerror=null; this.href='{{ url_for('static', path='shared/css/tailwind.min.css') }}';">
<link rel="stylesheet" href="{{ url_for('static', path='admin/css/tailwind.output.css') }}" />
<!-- NEW: Local only (v4) -->
<link rel="stylesheet" href="{{ url_for('static', path='admin/css/tailwind.output.css') }}" />
```
### Phase 7: Build and Test
```bash
# Build CSS
make tailwind-build
# Start development server
make dev
# Test all frontends:
# - http://localhost:8000/admin/dashboard
# - http://localhost:8000/vendor/{code}/dashboard
# - http://localhost:8000/ (shop)
```
### Phase 8: Remove Node.js Artifacts
After successful testing, remove all Node.js related files:
```bash
# Remove Node.js files
rm -rf node_modules/
rm package.json
rm package-lock.json
rm postcss.config.js
# Remove unused CDN fallback
rm static/shared/css/tailwind.min.css
```
---
## Verification Checklist
- [x] Tailwind CLI installed and working (`tailwindcss --version`)
- [x] Admin dashboard loads correctly
- [x] Vendor dashboard loads correctly
- [x] Shop frontend loads correctly
- [x] Platform pages load correctly
- [x] Dark mode toggle works
- [x] All buttons have correct colors
- [x] Forms display correctly (inputs, selects, checkboxes)
- [x] Responsive design works (mobile/tablet/desktop)
- [x] No console errors
- [x] Production build works (`make tailwind-build`)
- [x] node_modules removed
- [x] package.json removed
- [x] Each frontend has its own CSS source file
- [x] Vendor theming (CSS variables) still works
---
## Breaking Changes Reference
### Class Name Changes (v2 → v4)
| v2.x | v4.x | Notes |
|------|------|-------|
| `overflow-ellipsis` | `text-ellipsis` | Renamed |
| `flex-grow-0` | `grow-0` | Shortened |
| `flex-shrink-0` | `shrink-0` | Shortened |
| `decoration-clone` | `box-decoration-clone` | Renamed |
### Plugin Changes
| Old Plugin | New Approach | Notes |
|------------|--------------|-------|
| `@tailwindcss/custom-forms` | Built-in form styles | Native in v4 |
| `tailwindcss-multi-theme` | `darkMode: 'class'` | Native support |
### Config Changes
| v1.x/v2.x | v4.x |
|-----------|------|
| `purge: [...]` | `content: [...]` |
| `variants: {...}` | Removed (JIT generates all) |
| `mode: 'jit'` | Default (not needed) |
---
## Rollback Plan
If issues arise, the old setup can be restored:
```bash
# Reinstall Node dependencies
npm install
# Rebuild with old config
npm run build
```
---
## Benefits of This Migration
| Aspect | Before | After |
|--------|--------|-------|
| **Node.js required** | Yes | No |
| **Dependencies** | ~200MB node_modules | ~50MB single binary |
| **Build speed** | Slower (npm + PostCSS) | Faster (standalone) |
| **Tailwind versions** | 2 (v1.4 + v2.2) | 1 (v4.0) |
| **CSS loading** | CDN + local | Local only |
| **Maintenance** | npm updates, security patches | Single binary updates |
---
## Resources
- [Tailwind CSS Standalone CLI](https://tailwindcss.com/blog/standalone-cli)
- [Tailwind CSS v4 Documentation](https://tailwindcss.com/docs)
- [Dark Mode in Tailwind](https://tailwindcss.com/docs/dark-mode)
- [Standalone CLI Releases](https://github.com/tailwindlabs/tailwindcss/releases)