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>
243 lines
8.3 KiB
Markdown
243 lines
8.3 KiB
Markdown
# 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
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
# 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:
|
|
|
|
```python
|
|
RESERVED_SUBDOMAINS = {"www", "admin", "api", "store", "portal"}
|
|
```
|
|
|
|
## Usage
|
|
|
|
### In Middleware/Routes
|
|
|
|
```python
|
|
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)
|
|
|
|
```python
|
|
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:
|
|
|
|
```python
|
|
request.state.frontend_type # FrontendType enum value
|
|
```
|
|
|
|
This is used by:
|
|
- `LanguageMiddleware` - to determine language resolution strategy
|
|
- `ErrorRenderer` - to select appropriate error templates
|
|
- `ExceptionHandler` - 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 tests
|
|
- `tests/unit/middleware/test_frontend_type.py` - Middleware tests
|
|
|
|
### Running Tests
|
|
|
|
```bash
|
|
# 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
|
|
|
|
1. **Use `get_frontend_type(request)`** in route handlers
|
|
2. **Use `FrontendDetector.detect()`** when you have host/path but no request
|
|
3. **Use convenience methods** like `is_admin()`, `is_storefront()` for boolean checks
|
|
4. **Import from the correct location:**
|
|
```python
|
|
from app.modules.enums import FrontendType
|
|
from middleware.frontend_type import get_frontend_type
|
|
from app.core.frontend_detector import FrontendDetector
|
|
```
|
|
|
|
### DON'T
|
|
|
|
1. **Don't duplicate path detection logic** - use FrontendDetector
|
|
2. **Don't hardcode path patterns** in middleware - they're centralized in FrontendDetector
|
|
3. **Don't check `request.state.context_type`** - use `request.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](middleware.md) - Overall middleware architecture
|
|
- [Request Flow](request-flow.md) - How requests are processed
|
|
- [URL Routing](url-routing/overview.md) - URL structure and routing patterns
|
|
- [Multi-Tenant Architecture](multi-tenant.md) - Tenant detection and isolation
|