Replace all ~1,086 occurrences of Wizamart/wizamart/WIZAMART/WizaMart with Orion/orion/ORION across 184 files. This includes database identifiers, email addresses, domain references, R2 bucket names, DNS prefixes, encryption salt, Celery app name, config defaults, Docker configs, CI configs, documentation, seed data, and templates. Renames homepage-wizamart.html template to homepage-orion.html. Fixes duplicate file_pattern key in api.yaml architecture rule. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
8.3 KiB
8.3 KiB
Frontend Detection Architecture
This document describes the centralized frontend detection system that identifies which frontend (ADMIN, STORE, STOREFRONT, or PLATFORM) a request targets.
Overview
The application serves multiple frontends from a single codebase:
| Frontend | Description | Example URLs |
|---|---|---|
| ADMIN | Platform administration | /admin/*, /api/v1/admin/*, admin.oms.lu/* |
| STORE | Store dashboard | /store/*, /api/v1/store/* |
| STOREFRONT | Customer-facing shop | /storefront/*, /stores/*, orion.oms.lu/* |
| PLATFORM | Marketing pages | /, /pricing, /about |
The FrontendDetector class provides centralized, consistent detection of which frontend a request targets.
Architecture
Components
┌─────────────────────────────────────────────────────────────────┐
│ Request Processing │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. PlatformContextMiddleware → Sets request.state.platform │
│ │
│ 2. StoreContextMiddleware → Sets request.state.store │
│ │
│ 3. FrontendTypeMiddleware → Sets request.state.frontend_type│
│ │ │
│ └──→ Uses FrontendDetector.detect() │
│ │
│ 4. LanguageMiddleware → Uses frontend_type for language │
│ │
│ 5. ThemeContextMiddleware → Uses frontend_type for theming │
│ │
│ 6. FastAPI Router → Handles request │
│ │
└─────────────────────────────────────────────────────────────────┘
Key Files
| File | Purpose |
|---|---|
app/core/frontend_detector.py |
Centralized detection logic |
middleware/frontend_type.py |
Middleware that sets request.state.frontend_type |
app/modules/enums.py |
Defines FrontendType enum |
FrontendType Enum
class FrontendType(str, Enum):
PLATFORM = "platform" # Marketing pages (/, /pricing, /about)
ADMIN = "admin" # Admin panel (/admin/*)
STORE = "store" # Store dashboard (/store/*)
STOREFRONT = "storefront" # Customer shop (/storefront/*, /stores/*)
Detection Priority
The FrontendDetector uses the following priority order:
1. Admin subdomain (admin.oms.lu) → ADMIN
2. Path-based detection:
- /admin/* or /api/v1/admin/* → ADMIN
- /store/* or /api/v1/store/* → STORE
- /storefront/*, /shop/*, /stores/* → STOREFRONT
- /api/v1/platform/* → PLATFORM
3. Store subdomain (orion.oms.lu) → STOREFRONT
4. Store context set by middleware → STOREFRONT
5. Default → PLATFORM
Path Patterns
# Admin paths
ADMIN_PATH_PREFIXES = ("/admin", "/api/v1/admin")
# Store dashboard paths
STORE_PATH_PREFIXES = ("/store/", "/api/v1/store")
# Storefront paths
STOREFRONT_PATH_PREFIXES = (
"/storefront",
"/api/v1/storefront",
"/shop", # Legacy support
"/api/v1/shop", # Legacy support
"/stores/", # Path-based store access
)
# Platform paths
PLATFORM_PATH_PREFIXES = ("/api/v1/platform",)
Reserved Subdomains
These subdomains are NOT treated as store storefronts:
RESERVED_SUBDOMAINS = {"www", "admin", "api", "store", "portal"}
Usage
In Middleware/Routes
from middleware.frontend_type import get_frontend_type
from app.modules.enums import FrontendType
@router.get("/some-route")
async def some_route(request: Request):
frontend_type = get_frontend_type(request)
if frontend_type == FrontendType.ADMIN:
# Admin-specific logic
pass
elif frontend_type == FrontendType.STOREFRONT:
# Storefront-specific logic
pass
Direct Detection (without request)
from app.core.frontend_detector import FrontendDetector
from app.modules.enums import FrontendType
# Full detection
frontend_type = FrontendDetector.detect(
host="orion.oms.lu",
path="/products",
has_store_context=True
)
# Returns: FrontendType.STOREFRONT
# Convenience methods
if FrontendDetector.is_admin(host, path):
# Admin logic
pass
if FrontendDetector.is_storefront(host, path, has_store_context=True):
# Storefront logic
pass
Detection Scenarios
Development Mode (localhost)
| Request | Host | Path | Frontend |
|---|---|---|---|
| Admin page | localhost | /admin/stores | ADMIN |
| Admin API | localhost | /api/v1/admin/users | ADMIN |
| Store dashboard | localhost | /store/settings | STORE |
| Store API | localhost | /api/v1/store/products | STORE |
| Storefront | localhost | /storefront/products | STOREFRONT |
| Storefront (path-based) | localhost | /stores/orion/products | STOREFRONT |
| Marketing | localhost | /pricing | PLATFORM |
Production Mode (domains)
| Request | Host | Path | Frontend |
|---|---|---|---|
| Admin subdomain | admin.oms.lu | /dashboard | ADMIN |
| Store subdomain | orion.oms.lu | /products | STOREFRONT |
| Custom domain | mybakery.lu | /products | STOREFRONT |
| Platform root | oms.lu | /pricing | PLATFORM |
Request State
After FrontendTypeMiddleware runs, the following is available:
request.state.frontend_type # FrontendType enum value
This is used by:
LanguageMiddleware- to determine language resolution strategyErrorRenderer- to select appropriate error templatesExceptionHandler- to redirect to correct login page- Route handlers - for frontend-specific logic
Testing
Unit Tests
Tests are located in:
tests/unit/core/test_frontend_detector.py- FrontendDetector teststests/unit/middleware/test_frontend_type.py- Middleware tests
Running Tests
# Run all frontend detection tests
pytest tests/unit/core/test_frontend_detector.py tests/unit/middleware/test_frontend_type.py -v
# Run with coverage
pytest tests/unit/core/test_frontend_detector.py tests/unit/middleware/test_frontend_type.py --cov=app.core.frontend_detector --cov=middleware.frontend_type
Best Practices
DO
- Use
get_frontend_type(request)in route handlers - Use
FrontendDetector.detect()when you have host/path but no request - Use convenience methods like
is_admin(),is_storefront()for boolean checks - Import from the correct location:
from app.modules.enums import FrontendType from middleware.frontend_type import get_frontend_type from app.core.frontend_detector import FrontendDetector
DON'T
- Don't duplicate path detection logic - use FrontendDetector
- Don't hardcode path patterns in middleware - they're centralized in FrontendDetector
- Don't check
request.state.context_type- userequest.state.frontend_type
Architecture Rules
These rules are enforced by scripts/validate/validate_architecture.py:
| Rule | Description |
|---|---|
| MID-001 | Use FrontendDetector for frontend detection |
| MID-002 | Don't hardcode path patterns in middleware |
| MID-003 | Use FrontendType enum, not RequestContext |
Related Documentation
- Middleware Stack - Overall middleware architecture
- Request Flow - How requests are processed
- URL Routing - URL structure and routing patterns
- Multi-Tenant Architecture - Tenant detection and isolation