# 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/*, /shop/*, /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
    "/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.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
