refactor: centralize frontend detection with FrontendDetector

Major architecture change to unify frontend detection:

## Problem Solved
- Eliminated code duplication across 3 middleware files
- Fixed incomplete path detection (now detects /api/v1/admin/*)
- Unified on FrontendType enum (deprecates RequestContext)
- Added request.state.frontend_type for all requests

## New Components
- app/core/frontend_detector.py: Centralized FrontendDetector class
- middleware/frontend_type.py: FrontendTypeMiddleware (replaces ContextMiddleware)
- docs/architecture/frontend-detection.md: Complete architecture documentation

## Changes
- main.py: Use FrontendTypeMiddleware instead of ContextMiddleware
- middleware/context.py: Deprecated (kept for backwards compatibility)
- middleware/platform_context.py: Use FrontendDetector.is_admin()
- middleware/vendor_context.py: Use FrontendDetector.is_admin()
- middleware/language.py: Use FrontendType instead of context_value
- app/exceptions/handler.py: Use FrontendType.STOREFRONT
- app/exceptions/error_renderer.py: Use FrontendType
- Customer routes: Cookie path changed from /shop to /storefront

## Documentation
- docs/architecture/frontend-detection.md: New comprehensive docs
- docs/architecture/middleware.md: Updated for new system
- docs/architecture/request-flow.md: Updated for FrontendType
- docs/backend/middleware-reference.md: Updated API reference

## Tests
- tests/unit/core/test_frontend_detector.py: 37 new tests
- tests/unit/middleware/test_frontend_type.py: 11 new tests
- tests/unit/middleware/test_context.py: Updated for compatibility

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-03 16:15:19 +01:00
parent e77535e2cd
commit b769f5a047
17 changed files with 1393 additions and 915 deletions

View File

@@ -0,0 +1,278 @@
# Frontend Detection Architecture
This document describes the centralized frontend detection system that identifies which frontend (ADMIN, VENDOR, 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/*` |
| **VENDOR** | Vendor dashboard | `/vendor/*`, `/api/v1/vendor/*` |
| **STOREFRONT** | Customer-facing shop | `/storefront/*`, `/vendors/*`, `wizamart.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. VendorContextMiddleware → Sets request.state.vendor │
│ │
│ 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/*)
VENDOR = "vendor" # Vendor dashboard (/vendor/*)
STOREFRONT = "storefront" # Customer shop (/storefront/*, /vendors/*)
```
## 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
- /vendor/* or /api/v1/vendor/* → VENDOR
- /storefront/*, /shop/*, /vendors/* → STOREFRONT
- /api/v1/platform/* → PLATFORM
3. Vendor subdomain (wizamart.oms.lu) → STOREFRONT
4. Vendor context set by middleware → STOREFRONT
5. Default → PLATFORM
```
### Path Patterns
```python
# Admin paths
ADMIN_PATH_PREFIXES = ("/admin", "/api/v1/admin")
# Vendor dashboard paths
VENDOR_PATH_PREFIXES = ("/vendor/", "/api/v1/vendor")
# Storefront paths
STOREFRONT_PATH_PREFIXES = (
"/storefront",
"/api/v1/storefront",
"/shop", # Legacy support
"/api/v1/shop", # Legacy support
"/vendors/", # Path-based vendor access
)
# Platform paths
PLATFORM_PATH_PREFIXES = ("/api/v1/platform",)
```
### Reserved Subdomains
These subdomains are NOT treated as vendor storefronts:
```python
RESERVED_SUBDOMAINS = {"www", "admin", "api", "vendor", "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="wizamart.oms.lu",
path="/products",
has_vendor_context=True
)
# Returns: FrontendType.STOREFRONT
# Convenience methods
if FrontendDetector.is_admin(host, path):
# Admin logic
pass
if FrontendDetector.is_storefront(host, path, has_vendor_context=True):
# Storefront logic
pass
```
## Detection Scenarios
### Development Mode (localhost)
| Request | Host | Path | Frontend |
|---------|------|------|----------|
| Admin page | localhost | /admin/vendors | ADMIN |
| Admin API | localhost | /api/v1/admin/users | ADMIN |
| Vendor dashboard | localhost | /vendor/settings | VENDOR |
| Vendor API | localhost | /api/v1/vendor/products | VENDOR |
| Storefront | localhost | /storefront/products | STOREFRONT |
| Storefront (path-based) | localhost | /vendors/wizamart/products | STOREFRONT |
| Marketing | localhost | /pricing | PLATFORM |
### Production Mode (domains)
| Request | Host | Path | Frontend |
|---------|------|------|----------|
| Admin subdomain | admin.oms.lu | /dashboard | ADMIN |
| Vendor subdomain | wizamart.oms.lu | /products | STOREFRONT |
| Custom domain | mybakery.lu | /products | STOREFRONT |
| Platform root | oms.lu | /pricing | PLATFORM |
## Migration from RequestContext
The previous `RequestContext` enum is deprecated. Here's the mapping:
| Old (RequestContext) | New (FrontendType) |
|---------------------|-------------------|
| `API` | Use `FrontendDetector.is_api_request()` + FrontendType |
| `ADMIN` | `FrontendType.ADMIN` |
| `VENDOR_DASHBOARD` | `FrontendType.VENDOR` |
| `SHOP` | `FrontendType.STOREFRONT` |
| `FALLBACK` | `FrontendType.PLATFORM` |
### Code Migration
**Before (deprecated):**
```python
from middleware.context import RequestContext, get_request_context
context = get_request_context(request)
if context == RequestContext.SHOP:
# Storefront logic
pass
```
**After:**
```python
from middleware.frontend_type import get_frontend_type
from app.modules.enums import FrontendType
frontend_type = get_frontend_type(request)
if frontend_type == FrontendType.STOREFRONT:
# Storefront logic
pass
```
## 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 use `RequestContext`** - it's deprecated
2. **Don't duplicate path detection logic** - use FrontendDetector
3. **Don't hardcode path patterns** in middleware - they're centralized in FrontendDetector
4. **Don't check `request.state.context_type`** - use `request.state.frontend_type`
## Architecture Rules
These rules are enforced by `scripts/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

View File

@@ -120,35 +120,35 @@ Injects: request.state.vendor = <Vendor object>
**Note on Path-Based Routing:** Previous implementations used a `PathRewriteMiddleware` to rewrite paths at runtime. This has been replaced with **double router mounting** in `main.py`, where shop routes are registered twice with different prefixes (`/shop` and `/vendors/{vendor_code}/shop`). This approach is simpler and uses FastAPI's native routing capabilities.
### 3. Context Detection Middleware
### 3. Frontend Type Detection Middleware
**Purpose**: Determine the type/context of the request
**Purpose**: Determine which frontend the request targets
**What it does**:
- Analyzes the request path (using clean_path)
- Determines which interface is being accessed:
- `API` - `/api/*` paths
- `ADMIN` - `/admin/*` paths or `admin.*` subdomain
- `VENDOR_DASHBOARD` - `/vendor/*` paths (management area)
- `SHOP` - Storefront pages (has vendor + not admin/vendor/API)
- `FALLBACK` - Unknown context
- Injects `request.state.context_type`
- Uses centralized `FrontendDetector` class for all detection logic
- Determines which frontend is being accessed:
- `ADMIN` - `/admin/*`, `/api/v1/admin/*` paths or `admin.*` subdomain
- `VENDOR` - `/vendor/*`, `/api/v1/vendor/*` paths (management area)
- `STOREFRONT` - Customer shop pages (`/storefront/*`, `/vendors/*`, vendor subdomains)
- `PLATFORM` - Marketing pages (`/`, `/pricing`, `/about`)
- Injects `request.state.frontend_type` (FrontendType enum)
**Detection Rules**:
**Detection Priority** (handled by `FrontendDetector`):
```python
if path.startswith("/api/"):
context = API
elif path.startswith("/admin/") or host.startswith("admin."):
context = ADMIN
elif path.startswith("/vendor/"):
context = VENDOR_DASHBOARD
elif request.state.vendor exists:
context = SHOP
else:
context = FALLBACK
1. Admin subdomain (admin.oms.lu) ADMIN
2. Path-based detection:
- /admin/* or /api/v1/admin/* ADMIN
- /vendor/* or /api/v1/vendor/* VENDOR
- /storefront/*, /shop/*, /vendors/* STOREFRONT
- /api/v1/platform/* PLATFORM
3. Vendor subdomain (wizamart.oms.lu) STOREFRONT
4. Vendor context set by middleware STOREFRONT
5. Default PLATFORM
```
**Why it's useful**: Error handlers and templates adapt based on context
**Why it's useful**: Error handlers, templates, and language detection adapt based on frontend type
**See**: [Frontend Detection Architecture](frontend-detection.md) for complete details
### 4. Theme Context Middleware
@@ -221,15 +221,18 @@ Each middleware file contains one primary class or a tightly related set of clas
class LoggingMiddleware(BaseHTTPMiddleware):
"""Request/response logging middleware"""
# middleware/context.py
class ContextManager: # Business logic
class ContextMiddleware: # ASGI wrapper
class RequestContext(Enum): # Related enum
# middleware/frontend_type.py
class FrontendTypeMiddleware: # ASGI wrapper for frontend detection
# Uses FrontendDetector from app/core/frontend_detector.py
# middleware/auth.py
class AuthManager: # Authentication logic
```
> **Note**: The old `middleware/context.py` with `ContextMiddleware` and `RequestContext` is deprecated.
> Use `FrontendTypeMiddleware` and `FrontendType` enum instead.
> See [Frontend Detection Architecture](frontend-detection.md) for migration guide.
#### One Test File Per Component
Follow the Single Responsibility Principle - each test file tests exactly one component:
@@ -368,8 +371,8 @@ async def get_products(request: Request):
}
</style>
{# Access context #}
{% if request.state.context_type.value == "admin" %}
{# Access frontend type #}
{% if request.state.frontend_type.value == "admin" %}
<div class="admin-badge">Admin Mode</div>
{% endif %}
```
@@ -394,11 +397,11 @@ async def get_products(request: Request):
↓ Sets: request.state.vendor_id = 1
↓ Sets: request.state.clean_path = "/shop/products"
3. ContextDetectionMiddleware
Analyzes path: "/shop/products"
↓ Has vendor: Yes
Not admin/api/vendor dashboard
↓ Sets: request.state.context_type = RequestContext.SHOP
3. FrontendTypeMiddleware
Uses FrontendDetector with path: "/shop/products"
↓ Has vendor context: Yes
Detects storefront frontend
↓ Sets: request.state.frontend_type = FrontendType.STOREFRONT
4. ThemeContextMiddleware
↓ Loads theme for vendor_id = 1
@@ -430,10 +433,10 @@ Each middleware component handles errors gracefully:
- If database error: Logs error, allows request to continue
- Fallback: Request proceeds without vendor context
### ContextDetectionMiddleware
### FrontendTypeMiddleware
- If clean_path missing: Uses original path
- If vendor missing: Defaults to FALLBACK context
- Always sets a context_type (never None)
- If vendor missing: Defaults to PLATFORM frontend type
- Always sets a frontend_type (never None)
### ThemeContextMiddleware
- If vendor missing: Skips theme loading
@@ -479,8 +482,10 @@ Middleware is registered in `main.py`:
# Add in REVERSE order (LIFO execution)
app.add_middleware(LoggingMiddleware)
app.add_middleware(ThemeContextMiddleware)
app.add_middleware(ContextDetectionMiddleware)
app.add_middleware(LanguageMiddleware)
app.add_middleware(FrontendTypeMiddleware)
app.add_middleware(VendorContextMiddleware)
app.add_middleware(PlatformContextMiddleware)
```
**Note**: FastAPI's `add_middleware` executes in **reverse order** (Last In, First Out)

View File

@@ -147,27 +147,25 @@ app.include_router(shop_pages.router, prefix="/vendors/{vendor_code}/shop")
**Detection Logic**:
```python
host = request.headers.get("host", "")
path = request.state.clean_path # "/shop/products"
has_vendor = hasattr(request.state, 'vendor') and request.state.vendor
if path.startswith("/api/"):
context = RequestContext.API
elif path.startswith("/admin/"):
context = RequestContext.ADMIN
elif path.startswith("/vendor/"):
context = RequestContext.VENDOR_DASHBOARD
elif hasattr(request.state, 'vendor') and request.state.vendor:
context = RequestContext.SHOP # ← Our example
else:
context = RequestContext.FALLBACK
# FrontendDetector handles all detection logic centrally
frontend_type = FrontendDetector.detect(host, path, has_vendor)
# Returns: FrontendType.STOREFRONT # ← Our example
request.state.context_type = context
request.state.frontend_type = frontend_type
```
**Request State After**:
```python
request.state.context_type = RequestContext.SHOP
request.state.frontend_type = FrontendType.STOREFRONT
```
> **Note**: Detection logic is centralized in `app/core/frontend_detector.py`.
> See [Frontend Detection Architecture](frontend-detection.md) for details.
### 6. ThemeContextMiddleware
**What happens**:
@@ -363,7 +361,7 @@ sequenceDiagram
Context->>Router: Route request
Router->>Handler: Call API handler
Handler->>DB: Query products
DB-->>Handler: Product data
DB-->>Handler: Product data
Handler-->>Router: JSON response
Router-->>Client: {products: [...]}
```
@@ -390,7 +388,7 @@ sequenceDiagram
Context->>Theme: Pass request
Note over Theme: Skip theme<br/>(No vendor)
Theme->>Router: Route request
Router->>Handler: Call handler
Router->>Handler: Call handler
Handler->>Template: Render admin template
Template-->>Client: Admin HTML page
```
@@ -423,7 +421,7 @@ sequenceDiagram
Context->>Theme: Pass request
Theme->>DB: Query theme
DB-->>Theme: Theme config
Note over Theme: Set theme in request.state
Note over Theme: Set theme in request.state
Theme->>Router: Route request
Router->>Handler: Call handler
Handler->>DB: Query products for vendor
@@ -450,12 +448,12 @@ After VendorContextMiddleware:
{
vendor: <Vendor: Wizamart>,
vendor_id: 1,
clean_path: "/shop/products",
clean_path: "/shop/products",
frontend_type: FrontendType.STOREFRONT
}
After ThemeContextMiddleware:
{
{
vendor: <Vendor: Wizamart>,
vendor_id: 1,
clean_path: "/shop/products",
@@ -463,7 +461,7 @@ After ThemeContextMiddleware:
theme: {
primary_color: "#3B82F6",
secondary_color: "#10B981",
logo_url: "/static/vendors/wizamart/logo.png",
logo_url: "/static/vendors/wizamart/logo.png",
custom_css: "..."
}
}
@@ -481,7 +479,7 @@ Typical request timings:
| - ThemeContextMiddleware | 2ms | 1% |
| Database Queries | 15ms | 10% |
| Business Logic | 50ms | 35% |
| Template Rendering | 75ms | 52% |
| Template Rendering | 75ms | 52% |
| **Total** | **145ms** | **100%** |
## Error Handling in Flow
@@ -550,7 +548,7 @@ async def debug_state(request: Request):
"has_theme": bool(getattr(request.state, 'theme', None))
}
```
### Check Middleware Order
In `main.py`, middleware registration order is critical:
@@ -562,7 +560,8 @@ In `main.py`, middleware registration order is critical:
app.add_middleware(LanguageMiddleware) # Runs fifth
app.add_middleware(FrontendTypeMiddleware) # Runs fourth
app.add_middleware(VendorContextMiddleware) # Runs second
app.add_middleware(ThemeContextMiddleware) # Runs fifth
app.add_middleware(ContextDetectionMiddleware) # Runs fourth
```
app.add_middleware(LanguageMiddleware) # Runs fifth
app.add_middleware(FrontendTypeMiddleware) # Runs fourth
app.add_middleware(VendorContextMiddleware) # Runs second
```

View File

@@ -66,51 +66,57 @@ ASGI middleware that wraps VendorContextManager for FastAPI integration.
---
## Request Context Detection
## Frontend Type Detection
### RequestContext
### FrontendType
Enum defining all possible request context types in the application.
Enum defining all possible frontend types in the application.
::: middleware.context.RequestContext
::: app.modules.enums.FrontendType
options:
show_source: false
heading_level: 4
show_root_heading: false
members:
- API
- PLATFORM
- ADMIN
- VENDOR_DASHBOARD
- SHOP
- FALLBACK
- VENDOR
- STOREFRONT
### ContextManager
### FrontendDetector
Detects the type of request (API, Admin, Vendor Dashboard, Shop) based on URL patterns.
Centralized class for detecting which frontend a request targets based on URL patterns.
**Context Detection Rules:**
- `/api/` → API context
- `/admin/` → Admin context
- `/vendor/` → Vendor Dashboard context
- `/shop/` → Shop context
- Default → Fallback context
**Detection Rules (Priority Order):**
1. Admin subdomain (`admin.*`) → ADMIN
2. Path-based detection:
- `/admin/*`, `/api/v1/admin/*` → ADMIN
- `/vendor/*`, `/api/v1/vendor/*` → VENDOR
- `/storefront/*`, `/shop/*`, `/vendors/*` → STOREFRONT
- `/api/v1/platform/*` → PLATFORM
3. Vendor subdomain → STOREFRONT
4. Vendor context set → STOREFRONT
5. Default → PLATFORM
::: middleware.context.ContextManager
::: app.core.frontend_detector.FrontendDetector
options:
show_source: false
heading_level: 4
show_root_heading: false
### ContextMiddleware
### FrontendTypeMiddleware
ASGI middleware for context detection. Must run AFTER VendorContextMiddleware.
ASGI middleware for frontend type detection. Must run AFTER VendorContextMiddleware.
::: middleware.context.ContextMiddleware
::: middleware.frontend_type.FrontendTypeMiddleware
options:
show_source: false
heading_level: 4
show_root_heading: false
> **Note**: The old `RequestContext` enum and `ContextMiddleware` are deprecated.
> See [Frontend Detection Architecture](../architecture/frontend-detection.md) for migration guide.
---
## Theme Management
@@ -235,20 +241,24 @@ The middleware stack must be configured in the correct order for proper function
```mermaid
graph TD
A[Request] --> B[LoggingMiddleware]
B --> C[VendorContextMiddleware]
C --> D[ContextMiddleware]
D --> E[ThemeContextMiddleware]
E --> F[Application Routes]
F --> G[Response]
B --> C[PlatformContextMiddleware]
C --> D[VendorContextMiddleware]
D --> E[FrontendTypeMiddleware]
E --> F[LanguageMiddleware]
F --> G[ThemeContextMiddleware]
G --> H[Application Routes]
H --> I[Response]
```
**Critical Dependencies:**
1. **LoggingMiddleware** runs first for request timing
2. **VendorContextMiddleware** detects vendor and sets clean_path
3. **ContextMiddleware** detects context type (API/Admin/Vendor/Shop)
4. **ThemeContextMiddleware** loads vendor theme based on context
2. **PlatformContextMiddleware** detects platform and sets platform context
3. **VendorContextMiddleware** detects vendor and sets clean_path
4. **FrontendTypeMiddleware** detects frontend type (ADMIN/VENDOR/STOREFRONT/PLATFORM)
5. **LanguageMiddleware** resolves language based on frontend type
6. **ThemeContextMiddleware** loads vendor theme based on context
**Note:** Path-based routing (e.g., `/vendors/{code}/shop/*`) is handled by double router mounting in `main.py`, not by middleware.
**Note:** Path-based routing (e.g., `/vendors/{code}/storefront/*`) is handled by double router mounting in `main.py`, not by middleware.
---
@@ -258,22 +268,28 @@ Middleware components inject the following variables into `request.state`:
| Variable | Set By | Type | Description |
|----------|--------|------|-------------|
| `platform` | PlatformContextMiddleware | Platform | Current platform object |
| `vendor` | VendorContextMiddleware | Vendor | Current vendor object |
| `vendor_id` | VendorContextMiddleware | int | Current vendor ID |
| `clean_path` | VendorContextMiddleware | str | Path without vendor prefix |
| `context_type` | ContextMiddleware | RequestContext | Request context (API/Admin/Vendor/Shop) |
| `frontend_type` | FrontendTypeMiddleware | FrontendType | Frontend type (ADMIN/VENDOR/STOREFRONT/PLATFORM) |
| `language` | LanguageMiddleware | str | Detected language code |
| `theme` | ThemeContextMiddleware | dict | Vendor theme configuration |
**Usage in Routes:**
```python
from fastapi import Request
from app.modules.enums import FrontendType
from middleware.frontend_type import get_frontend_type
@app.get("/shop/products")
@app.get("/storefront/products")
async def get_products(request: Request):
vendor = request.state.vendor
context = request.state.context_type
frontend_type = get_frontend_type(request)
theme = request.state.theme
return {"vendor": vendor.name, "context": context}
if frontend_type == FrontendType.STOREFRONT:
return {"vendor": vendor.name, "frontend": frontend_type.value}
```
---
@@ -308,6 +324,8 @@ For testing examples, see the [Testing Guide](../testing/testing-guide.md).
## Related Documentation
- [Frontend Detection Architecture](../architecture/frontend-detection.md) - Frontend type detection system
- [Middleware Architecture](../architecture/middleware.md) - Middleware stack overview
- [Authentication Guide](../api/authentication.md) - User authentication and JWT tokens
- [RBAC Documentation](../api/rbac.md) - Role-based access control
- [Error Handling](../api/error-handling.md) - Exception handling patterns