Some checks failed
- Add Development URL Quick Reference section to url-routing overview with all login URLs, entry points, and full examples - Replace /shop/ path segments with /storefront/ across 50 docs files - Update file references: shop_pages.py → storefront_pages.py, templates/shop/ → templates/storefront/, api/v1/shop/ → api/v1/storefront/ - Preserve domain references (orion.shop) and /store/ staff dashboard paths - Archive docs left unchanged (historical) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
243 lines
8.4 KiB
Markdown
243 lines
8.4 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.omsflow.lu/*` |
|
|
| **STORE** | Store dashboard | `/store/*`, `/api/v1/store/*` |
|
|
| **STOREFRONT** | Customer-facing shop | `/storefront/*`, `/stores/*`, `orion.omsflow.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.omsflow.lu) → ADMIN
|
|
2. Path-based detection:
|
|
- /admin/* or /api/v1/admin/* → ADMIN
|
|
- /store/* or /api/v1/store/* → STORE
|
|
- /storefront/*, /stores/* → STOREFRONT
|
|
- /api/v1/platform/* → PLATFORM
|
|
3. Store subdomain (orion.omsflow.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 (redirects to /storefront)
|
|
"/api/v1/shop", # Legacy support (redirects to /api/v1/storefront)
|
|
"/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.omsflow.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.omsflow.lu | /dashboard | ADMIN |
|
|
| Store subdomain | orion.omsflow.lu | /products | STOREFRONT |
|
|
| Custom domain | mybakery.lu | /products | STOREFRONT |
|
|
| Platform root | omsflow.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
|