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:
@@ -18,6 +18,7 @@ from starlette.middleware.base import BaseHTTPMiddleware
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import Response
|
||||
|
||||
from app.modules.enums import FrontendType
|
||||
from app.utils.i18n import (
|
||||
DEFAULT_LANGUAGE,
|
||||
SUPPORTED_LANGUAGES,
|
||||
@@ -45,9 +46,8 @@ class LanguageMiddleware(BaseHTTPMiddleware):
|
||||
|
||||
async def dispatch(self, request: Request, call_next) -> Response:
|
||||
"""Process the request and set language."""
|
||||
# Get context type from previous middleware
|
||||
context_type = getattr(request.state, "context_type", None)
|
||||
context_value = context_type.value if context_type else None
|
||||
# Get frontend type from FrontendTypeMiddleware
|
||||
frontend_type = getattr(request.state, "frontend_type", None)
|
||||
|
||||
# Get vendor from previous middleware (if available)
|
||||
vendor = getattr(request.state, "vendor", None)
|
||||
@@ -59,13 +59,13 @@ class LanguageMiddleware(BaseHTTPMiddleware):
|
||||
accept_language = request.headers.get("accept-language")
|
||||
browser_language = parse_accept_language(accept_language)
|
||||
|
||||
# Resolve language based on context
|
||||
if context_value == "admin":
|
||||
# Resolve language based on frontend type
|
||||
if frontend_type == FrontendType.ADMIN:
|
||||
# Admin dashboard: English only (for now)
|
||||
# TODO: Implement admin language support later
|
||||
language = "en"
|
||||
|
||||
elif context_value == "vendor_dashboard":
|
||||
elif frontend_type == FrontendType.VENDOR:
|
||||
# Vendor dashboard
|
||||
user_preferred = self._get_user_language_from_token(request)
|
||||
vendor_dashboard = vendor.dashboard_language if vendor else None
|
||||
@@ -75,7 +75,7 @@ class LanguageMiddleware(BaseHTTPMiddleware):
|
||||
vendor_dashboard=vendor_dashboard,
|
||||
)
|
||||
|
||||
elif context_value == "shop":
|
||||
elif frontend_type == FrontendType.STOREFRONT:
|
||||
# Storefront
|
||||
customer_preferred = self._get_customer_language_from_token(request)
|
||||
vendor_storefront = vendor.storefront_language if vendor else None
|
||||
@@ -89,12 +89,12 @@ class LanguageMiddleware(BaseHTTPMiddleware):
|
||||
enabled_languages=enabled_languages,
|
||||
)
|
||||
|
||||
elif context_value == "api":
|
||||
# API requests: Use Accept-Language or cookie
|
||||
elif frontend_type == FrontendType.PLATFORM:
|
||||
# Platform marketing pages: Use cookie, browser, or default
|
||||
language = cookie_language or browser_language or DEFAULT_LANGUAGE
|
||||
|
||||
else:
|
||||
# Fallback: Use cookie, browser, or default
|
||||
# Fallback (API or unknown): Use Accept-Language or cookie
|
||||
language = cookie_language or browser_language or DEFAULT_LANGUAGE
|
||||
|
||||
# Validate language is supported
|
||||
@@ -109,13 +109,14 @@ class LanguageMiddleware(BaseHTTPMiddleware):
|
||||
"code": language,
|
||||
"cookie": cookie_language,
|
||||
"browser": browser_language,
|
||||
"context": context_value,
|
||||
"frontend_type": frontend_type.value if frontend_type else None,
|
||||
}
|
||||
|
||||
# Log language detection for debugging
|
||||
frontend_value = frontend_type.value if frontend_type else "unknown"
|
||||
logger.debug(
|
||||
f"Language detected: {language} "
|
||||
f"(context={context_value}, cookie={cookie_language}, browser={browser_language})"
|
||||
f"(frontend={frontend_value}, cookie={cookie_language}, browser={browser_language})"
|
||||
)
|
||||
|
||||
# Process request
|
||||
|
||||
Reference in New Issue
Block a user