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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user