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:
@@ -22,6 +22,8 @@ from fastapi import Request
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.core.frontend_detector import FrontendDetector
|
||||
from app.modules.enums import FrontendType
|
||||
from app.modules.tenancy.models import Platform
|
||||
|
||||
# Note: We use pure ASGI middleware (not BaseHTTPMiddleware) to enable path rewriting
|
||||
@@ -61,7 +63,7 @@ class PlatformContextManager:
|
||||
host_without_port = host.split(":")[0] if ":" in host else host
|
||||
|
||||
# Skip platform detection for admin routes - admin is global
|
||||
if PlatformContextManager.is_admin_request(request):
|
||||
if FrontendDetector.is_admin(host, path):
|
||||
return None
|
||||
|
||||
# Method 1: Domain-based detection (production)
|
||||
@@ -208,17 +210,15 @@ class PlatformContextManager:
|
||||
|
||||
@staticmethod
|
||||
def is_admin_request(request: Request) -> bool:
|
||||
"""Check if request is for admin interface."""
|
||||
"""
|
||||
Check if request is for admin interface.
|
||||
|
||||
DEPRECATED: Use FrontendDetector.is_admin() instead.
|
||||
Kept for backwards compatibility.
|
||||
"""
|
||||
host = request.headers.get("host", "")
|
||||
path = request.url.path
|
||||
|
||||
if ":" in host:
|
||||
host = host.split(":")[0]
|
||||
|
||||
if host.startswith("admin."):
|
||||
return True
|
||||
|
||||
return path.startswith("/admin")
|
||||
return FrontendDetector.is_admin(host, path)
|
||||
|
||||
@staticmethod
|
||||
def is_static_file_request(request: Request) -> bool:
|
||||
@@ -299,7 +299,7 @@ class PlatformContextMiddleware:
|
||||
return
|
||||
|
||||
# Skip for admin requests
|
||||
if self._is_admin_request(path, host):
|
||||
if FrontendDetector.is_admin(host, path):
|
||||
scope["state"]["platform"] = None
|
||||
scope["state"]["platform_context"] = None
|
||||
scope["state"]["platform_clean_path"] = path
|
||||
@@ -427,11 +427,13 @@ class PlatformContextMiddleware:
|
||||
return "favicon.ico" in path_lower
|
||||
|
||||
def _is_admin_request(self, path: str, host: str) -> bool:
|
||||
"""Check if request is for admin interface."""
|
||||
host_without_port = host.split(":")[0] if ":" in host else host
|
||||
if host_without_port.startswith("admin."):
|
||||
return True
|
||||
return path.startswith("/admin")
|
||||
"""
|
||||
Check if request is for admin interface.
|
||||
|
||||
DEPRECATED: Use FrontendDetector.is_admin() instead.
|
||||
Kept for backwards compatibility.
|
||||
"""
|
||||
return FrontendDetector.is_admin(host, path)
|
||||
|
||||
|
||||
def get_current_platform(request: Request) -> Platform | None:
|
||||
|
||||
Reference in New Issue
Block a user