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