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