# middleware/context_middleware.py """ Context Detection Middleware Detects the request context type (API, Admin, Vendor Dashboard, Shop, or Fallback) and injects it into request.state for use by error handlers and other components. This middleware runs independently and complements vendor_context_middleware. """ import logging from enum import Enum from fastapi import Request logger = logging.getLogger(__name__) class RequestContext(str, Enum): """Request context types for the application.""" API = "api" ADMIN = "admin" VENDOR_DASHBOARD = "vendor" SHOP = "shop" FALLBACK = "fallback" class ContextManager: """Manages context detection for multi-area application.""" @staticmethod def detect_context(request: Request) -> RequestContext: """ Detect the request context type. Priority order: 1. API → /api/* paths (highest priority, always JSON) 2. Admin → /admin/* paths or admin.* subdomain 3. Vendor Dashboard → /vendor/* paths (vendor management area) 4. Shop → Vendor storefront (custom domain, subdomain, or shop paths) 5. Fallback → Unknown/generic context Args: request: FastAPI request object Returns: RequestContext enum value """ path = request.url.path host = request.headers.get("host", "") # Remove port from host if present if ":" in host: host = host.split(":")[0] # 1. API context (highest priority) if path.startswith("/api/"): return RequestContext.API # 2. Admin context if ContextManager._is_admin_context(request, host, path): return RequestContext.ADMIN # 3. Vendor Dashboard context (vendor management area) if ContextManager._is_vendor_dashboard_context(path): return RequestContext.VENDOR_DASHBOARD # 4. Shop context (vendor storefront) # Check if vendor context exists (set by vendor_context_middleware) if hasattr(request.state, 'vendor') and request.state.vendor: # If we have a vendor and it's not admin or vendor dashboard, it's shop return RequestContext.SHOP # Also check shop-specific paths if path.startswith("/shop/"): return RequestContext.SHOP # 5. Fallback for unknown contexts return RequestContext.FALLBACK @staticmethod def _is_admin_context(request: Request, host: str, path: str) -> bool: """Check if request is in admin context.""" # Admin subdomain (admin.platform.com) if host.startswith("admin."): return True # Admin path (/admin/*) if path.startswith("/admin"): return True return False @staticmethod def _is_vendor_dashboard_context(path: str) -> bool: """Check if request is in vendor dashboard context.""" # Vendor dashboard paths (/vendor/*) # Note: This is the vendor management area, not the shop if path.startswith("/vendor/"): return True return False async def context_middleware(request: Request, call_next): """ Middleware to detect and inject request context into request.state. This should run AFTER vendor_context_middleware to have access to vendor information if available. Injects: request.state.context_type: RequestContext enum value """ # Detect context context_type = ContextManager.detect_context(request) # Inject into request state request.state.context_type = context_type # Log context detection (debug level) logger.debug( f"[CONTEXT] Request context detected: {context_type.value}", extra={ "path": request.url.path, "host": request.headers.get("host", ""), "context": context_type.value, } ) # Continue processing response = await call_next(request) return response def get_request_context(request: Request) -> RequestContext: """ Helper function to get current request context. Args: request: FastAPI request object Returns: RequestContext enum value (defaults to FALLBACK if not set) """ return getattr(request.state, "context_type", RequestContext.FALLBACK)