refactor: centralize frontend detection with FrontendDetector
Major architecture change to unify frontend detection: ## Problem Solved - Eliminated code duplication across 3 middleware files - Fixed incomplete path detection (now detects /api/v1/admin/*) - Unified on FrontendType enum (deprecates RequestContext) - Added request.state.frontend_type for all requests ## New Components - app/core/frontend_detector.py: Centralized FrontendDetector class - middleware/frontend_type.py: FrontendTypeMiddleware (replaces ContextMiddleware) - docs/architecture/frontend-detection.md: Complete architecture documentation ## Changes - main.py: Use FrontendTypeMiddleware instead of ContextMiddleware - middleware/context.py: Deprecated (kept for backwards compatibility) - middleware/platform_context.py: Use FrontendDetector.is_admin() - middleware/vendor_context.py: Use FrontendDetector.is_admin() - middleware/language.py: Use FrontendType instead of context_value - app/exceptions/handler.py: Use FrontendType.STOREFRONT - app/exceptions/error_renderer.py: Use FrontendType - Customer routes: Cookie path changed from /shop to /storefront ## Documentation - docs/architecture/frontend-detection.md: New comprehensive docs - docs/architecture/middleware.md: Updated for new system - docs/architecture/request-flow.md: Updated for FrontendType - docs/backend/middleware-reference.md: Updated API reference ## Tests - tests/unit/core/test_frontend_detector.py: 37 new tests - tests/unit/middleware/test_frontend_type.py: 11 new tests - tests/unit/middleware/test_context.py: Updated for compatibility Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -147,27 +147,25 @@ app.include_router(shop_pages.router, prefix="/vendors/{vendor_code}/shop")
|
||||
**Detection Logic**:
|
||||
|
||||
```python
|
||||
host = request.headers.get("host", "")
|
||||
path = request.state.clean_path # "/shop/products"
|
||||
has_vendor = hasattr(request.state, 'vendor') and request.state.vendor
|
||||
|
||||
if path.startswith("/api/"):
|
||||
context = RequestContext.API
|
||||
elif path.startswith("/admin/"):
|
||||
context = RequestContext.ADMIN
|
||||
elif path.startswith("/vendor/"):
|
||||
context = RequestContext.VENDOR_DASHBOARD
|
||||
elif hasattr(request.state, 'vendor') and request.state.vendor:
|
||||
context = RequestContext.SHOP # ← Our example
|
||||
else:
|
||||
context = RequestContext.FALLBACK
|
||||
# FrontendDetector handles all detection logic centrally
|
||||
frontend_type = FrontendDetector.detect(host, path, has_vendor)
|
||||
# Returns: FrontendType.STOREFRONT # ← Our example
|
||||
|
||||
request.state.context_type = context
|
||||
request.state.frontend_type = frontend_type
|
||||
```
|
||||
|
||||
**Request State After**:
|
||||
```python
|
||||
request.state.context_type = RequestContext.SHOP
|
||||
request.state.frontend_type = FrontendType.STOREFRONT
|
||||
```
|
||||
|
||||
> **Note**: Detection logic is centralized in `app/core/frontend_detector.py`.
|
||||
> See [Frontend Detection Architecture](frontend-detection.md) for details.
|
||||
|
||||
### 6. ThemeContextMiddleware
|
||||
|
||||
**What happens**:
|
||||
@@ -363,7 +361,7 @@ sequenceDiagram
|
||||
Context->>Router: Route request
|
||||
Router->>Handler: Call API handler
|
||||
Handler->>DB: Query products
|
||||
DB-->>Handler: Product data
|
||||
DB-->>Handler: Product data
|
||||
Handler-->>Router: JSON response
|
||||
Router-->>Client: {products: [...]}
|
||||
```
|
||||
@@ -390,7 +388,7 @@ sequenceDiagram
|
||||
Context->>Theme: Pass request
|
||||
Note over Theme: Skip theme<br/>(No vendor)
|
||||
Theme->>Router: Route request
|
||||
Router->>Handler: Call handler
|
||||
Router->>Handler: Call handler
|
||||
Handler->>Template: Render admin template
|
||||
Template-->>Client: Admin HTML page
|
||||
```
|
||||
@@ -423,7 +421,7 @@ sequenceDiagram
|
||||
Context->>Theme: Pass request
|
||||
Theme->>DB: Query theme
|
||||
DB-->>Theme: Theme config
|
||||
Note over Theme: Set theme in request.state
|
||||
Note over Theme: Set theme in request.state
|
||||
Theme->>Router: Route request
|
||||
Router->>Handler: Call handler
|
||||
Handler->>DB: Query products for vendor
|
||||
@@ -450,12 +448,12 @@ After VendorContextMiddleware:
|
||||
{
|
||||
vendor: <Vendor: Wizamart>,
|
||||
vendor_id: 1,
|
||||
clean_path: "/shop/products",
|
||||
clean_path: "/shop/products",
|
||||
frontend_type: FrontendType.STOREFRONT
|
||||
}
|
||||
|
||||
After ThemeContextMiddleware:
|
||||
{
|
||||
{
|
||||
vendor: <Vendor: Wizamart>,
|
||||
vendor_id: 1,
|
||||
clean_path: "/shop/products",
|
||||
@@ -463,7 +461,7 @@ After ThemeContextMiddleware:
|
||||
theme: {
|
||||
primary_color: "#3B82F6",
|
||||
secondary_color: "#10B981",
|
||||
logo_url: "/static/vendors/wizamart/logo.png",
|
||||
logo_url: "/static/vendors/wizamart/logo.png",
|
||||
custom_css: "..."
|
||||
}
|
||||
}
|
||||
@@ -481,7 +479,7 @@ Typical request timings:
|
||||
| - ThemeContextMiddleware | 2ms | 1% |
|
||||
| Database Queries | 15ms | 10% |
|
||||
| Business Logic | 50ms | 35% |
|
||||
| Template Rendering | 75ms | 52% |
|
||||
| Template Rendering | 75ms | 52% |
|
||||
| **Total** | **145ms** | **100%** |
|
||||
|
||||
## Error Handling in Flow
|
||||
@@ -550,7 +548,7 @@ async def debug_state(request: Request):
|
||||
"has_theme": bool(getattr(request.state, 'theme', None))
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Check Middleware Order
|
||||
|
||||
In `main.py`, middleware registration order is critical:
|
||||
@@ -562,7 +560,8 @@ In `main.py`, middleware registration order is critical:
|
||||
app.add_middleware(LanguageMiddleware) # Runs fifth
|
||||
app.add_middleware(FrontendTypeMiddleware) # Runs fourth
|
||||
app.add_middleware(VendorContextMiddleware) # Runs second
|
||||
app.add_middleware(ThemeContextMiddleware) # Runs fifth
|
||||
app.add_middleware(ContextDetectionMiddleware) # Runs fourth
|
||||
```
|
||||
app.add_middleware(LanguageMiddleware) # Runs fifth
|
||||
app.add_middleware(FrontendTypeMiddleware) # Runs fourth
|
||||
app.add_middleware(VendorContextMiddleware) # Runs second
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user