Files
orion/middleware/context_middleware.py

145 lines
4.3 KiB
Python

# 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)