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:
2026-02-03 16:15:19 +01:00
parent e77535e2cd
commit b769f5a047
17 changed files with 1393 additions and 915 deletions

View File

@@ -66,51 +66,57 @@ ASGI middleware that wraps VendorContextManager for FastAPI integration.
---
## Request Context Detection
## Frontend Type Detection
### RequestContext
### FrontendType
Enum defining all possible request context types in the application.
Enum defining all possible frontend types in the application.
::: middleware.context.RequestContext
::: app.modules.enums.FrontendType
options:
show_source: false
heading_level: 4
show_root_heading: false
members:
- API
- PLATFORM
- ADMIN
- VENDOR_DASHBOARD
- SHOP
- FALLBACK
- VENDOR
- STOREFRONT
### ContextManager
### FrontendDetector
Detects the type of request (API, Admin, Vendor Dashboard, Shop) based on URL patterns.
Centralized class for detecting which frontend a request targets based on URL patterns.
**Context Detection Rules:**
- `/api/` → API context
- `/admin/` → Admin context
- `/vendor/` → Vendor Dashboard context
- `/shop/` → Shop context
- Default → Fallback context
**Detection Rules (Priority Order):**
1. Admin subdomain (`admin.*`) → ADMIN
2. Path-based detection:
- `/admin/*`, `/api/v1/admin/*` → ADMIN
- `/vendor/*`, `/api/v1/vendor/*` → VENDOR
- `/storefront/*`, `/shop/*`, `/vendors/*` → STOREFRONT
- `/api/v1/platform/*` → PLATFORM
3. Vendor subdomain → STOREFRONT
4. Vendor context set → STOREFRONT
5. Default → PLATFORM
::: middleware.context.ContextManager
::: app.core.frontend_detector.FrontendDetector
options:
show_source: false
heading_level: 4
show_root_heading: false
### ContextMiddleware
### FrontendTypeMiddleware
ASGI middleware for context detection. Must run AFTER VendorContextMiddleware.
ASGI middleware for frontend type detection. Must run AFTER VendorContextMiddleware.
::: middleware.context.ContextMiddleware
::: middleware.frontend_type.FrontendTypeMiddleware
options:
show_source: false
heading_level: 4
show_root_heading: false
> **Note**: The old `RequestContext` enum and `ContextMiddleware` are deprecated.
> See [Frontend Detection Architecture](../architecture/frontend-detection.md) for migration guide.
---
## Theme Management
@@ -235,20 +241,24 @@ The middleware stack must be configured in the correct order for proper function
```mermaid
graph TD
A[Request] --> B[LoggingMiddleware]
B --> C[VendorContextMiddleware]
C --> D[ContextMiddleware]
D --> E[ThemeContextMiddleware]
E --> F[Application Routes]
F --> G[Response]
B --> C[PlatformContextMiddleware]
C --> D[VendorContextMiddleware]
D --> E[FrontendTypeMiddleware]
E --> F[LanguageMiddleware]
F --> G[ThemeContextMiddleware]
G --> H[Application Routes]
H --> I[Response]
```
**Critical Dependencies:**
1. **LoggingMiddleware** runs first for request timing
2. **VendorContextMiddleware** detects vendor and sets clean_path
3. **ContextMiddleware** detects context type (API/Admin/Vendor/Shop)
4. **ThemeContextMiddleware** loads vendor theme based on context
2. **PlatformContextMiddleware** detects platform and sets platform context
3. **VendorContextMiddleware** detects vendor and sets clean_path
4. **FrontendTypeMiddleware** detects frontend type (ADMIN/VENDOR/STOREFRONT/PLATFORM)
5. **LanguageMiddleware** resolves language based on frontend type
6. **ThemeContextMiddleware** loads vendor theme based on context
**Note:** Path-based routing (e.g., `/vendors/{code}/shop/*`) is handled by double router mounting in `main.py`, not by middleware.
**Note:** Path-based routing (e.g., `/vendors/{code}/storefront/*`) is handled by double router mounting in `main.py`, not by middleware.
---
@@ -258,22 +268,28 @@ Middleware components inject the following variables into `request.state`:
| Variable | Set By | Type | Description |
|----------|--------|------|-------------|
| `platform` | PlatformContextMiddleware | Platform | Current platform object |
| `vendor` | VendorContextMiddleware | Vendor | Current vendor object |
| `vendor_id` | VendorContextMiddleware | int | Current vendor ID |
| `clean_path` | VendorContextMiddleware | str | Path without vendor prefix |
| `context_type` | ContextMiddleware | RequestContext | Request context (API/Admin/Vendor/Shop) |
| `frontend_type` | FrontendTypeMiddleware | FrontendType | Frontend type (ADMIN/VENDOR/STOREFRONT/PLATFORM) |
| `language` | LanguageMiddleware | str | Detected language code |
| `theme` | ThemeContextMiddleware | dict | Vendor theme configuration |
**Usage in Routes:**
```python
from fastapi import Request
from app.modules.enums import FrontendType
from middleware.frontend_type import get_frontend_type
@app.get("/shop/products")
@app.get("/storefront/products")
async def get_products(request: Request):
vendor = request.state.vendor
context = request.state.context_type
frontend_type = get_frontend_type(request)
theme = request.state.theme
return {"vendor": vendor.name, "context": context}
if frontend_type == FrontendType.STOREFRONT:
return {"vendor": vendor.name, "frontend": frontend_type.value}
```
---
@@ -308,6 +324,8 @@ For testing examples, see the [Testing Guide](../testing/testing-guide.md).
## Related Documentation
- [Frontend Detection Architecture](../architecture/frontend-detection.md) - Frontend type detection system
- [Middleware Architecture](../architecture/middleware.md) - Middleware stack overview
- [Authentication Guide](../api/authentication.md) - User authentication and JWT tokens
- [RBAC Documentation](../api/rbac.md) - Role-based access control
- [Error Handling](../api/error-handling.md) - Exception handling patterns