Files
orion/main.py
Samir Boulahtit 3875ad91df fix: add explicit routes for platform path prefixes
Add explicit routes for /oms/ and /loyalty/ to handle path-based
platform routing in development mode. These routes are defined before
the generic /{slug} catch-all to ensure proper matching.

Routes added:
- /oms → redirect to /oms/
- /oms/ → OMS homepage
- /oms/{slug} → OMS content pages
- /loyalty → redirect to /loyalty/
- /loyalty/ → Loyalty homepage
- /loyalty/{slug} → Loyalty content pages

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 16:50:25 +01:00

783 lines
29 KiB
Python

# main.py
"""
Wizamart FastAPI Application
Multi-tenant e-commerce marketplace platform with:
- Three deployment modes (subdomain, custom domain, path-based)
- Three interfaces (platform administration (admin), vendor dashboard (vendor), customer shop (shop))
- Comprehensive exception handling
- Middleware stack for context injection
"""
import io
import sys
# Fix Windows console encoding issues (must be at the very top)
if sys.platform == "win32":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
import logging
from datetime import UTC, datetime
from pathlib import Path
import sentry_sdk
from fastapi import Depends, FastAPI, HTTPException, Request, Response
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, HTMLResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from sentry_sdk.integrations.fastapi import FastApiIntegration
from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration
from sqlalchemy import text
from sqlalchemy.orm import Session
from app.api.main import api_router
from app.core.config import settings
# =============================================================================
# SENTRY INITIALIZATION
# =============================================================================
# Initialize Sentry for error tracking (only if DSN is configured)
if settings.sentry_dsn:
sentry_sdk.init(
dsn=settings.sentry_dsn,
environment=settings.sentry_environment,
traces_sample_rate=settings.sentry_traces_sample_rate,
integrations=[
FastApiIntegration(transaction_style="endpoint"),
SqlalchemyIntegration(),
],
# Send PII data (emails, usernames) - set to False if privacy is critical
send_default_pii=True,
# Release version for tracking deployments
release=f"wizamart@{settings.version}",
)
logging.getLogger(__name__).info(
f"Sentry initialized for environment: {settings.sentry_environment}"
)
from app.core.database import get_db
from app.core.lifespan import lifespan
from app.exceptions import ServiceUnavailableException
from app.exceptions.handler import setup_exception_handlers
# Import page routers
from app.routes import admin_pages, platform_pages, shop_pages, vendor_pages
from app.utils.i18n import get_jinja2_globals
from middleware.context import ContextMiddleware
from middleware.language import LanguageMiddleware
from middleware.logging import LoggingMiddleware
from middleware.theme_context import ThemeContextMiddleware
# Import REFACTORED class-based middleware
from middleware.platform_context import PlatformContextMiddleware
from middleware.vendor_context import VendorContextMiddleware
logger = logging.getLogger(__name__)
# Get the project root directory (where main.py is located)
BASE_DIR = Path(__file__).resolve().parent
STATIC_DIR = BASE_DIR / "static"
TEMPLATES_DIR = BASE_DIR / "app" / "templates"
# FastAPI app with lifespan
app = FastAPI(
title=settings.project_name,
description=settings.description,
version=settings.version,
lifespan=lifespan,
)
# Configure Jinja2 Templates
templates = Jinja2Templates(directory=str(TEMPLATES_DIR))
# Setup custom exception handlers (unified approach)
setup_exception_handlers(app)
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=settings.allowed_hosts,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ============================================================================
# MIDDLEWARE REGISTRATION (CORRECTED ORDER!)
# ============================================================================
#
# IMPORTANT: Middleware execution order with BaseHTTPMiddleware:
#
# When using app.add_middleware or wrapping with BaseHTTPMiddleware,
# the LAST added middleware runs FIRST (LIFO - Last In, First Out).
#
# So we add them in REVERSE order of desired execution:
#
# Desired execution order:
# 1. PlatformContextMiddleware (detect platform from domain/path)
# 2. VendorContextMiddleware (detect vendor, uses platform_clean_path)
# 3. ContextMiddleware (detect context using clean_path)
# 4. LanguageMiddleware (detect language based on context)
# 5. ThemeContextMiddleware (load theme)
# 6. LoggingMiddleware (log all requests)
#
# Therefore we add them in REVERSE:
# - Add ThemeContextMiddleware FIRST (runs LAST in request)
# - Add LanguageMiddleware SECOND
# - Add ContextMiddleware THIRD
# - Add VendorContextMiddleware FOURTH
# - Add PlatformContextMiddleware FIFTH
# - Add LoggingMiddleware LAST (runs FIRST for timing)
# ============================================================================
logger.info("=" * 80)
logger.info("MIDDLEWARE REGISTRATION")
logger.info("=" * 80)
# Add logging middleware (runs first for timing, logs all requests/responses)
logger.info("Adding LoggingMiddleware (runs first for request timing)")
app.add_middleware(LoggingMiddleware)
# Add theme context middleware (runs last in request chain)
logger.info("Adding ThemeContextMiddleware (detects and loads theme)")
app.add_middleware(ThemeContextMiddleware)
# Add language middleware (detects language after context is determined)
logger.info("Adding LanguageMiddleware (detects language based on context)")
app.add_middleware(LanguageMiddleware)
# Add context detection middleware (runs after vendor context extraction)
logger.info("Adding ContextMiddleware (detects context type using clean_path)")
app.add_middleware(ContextMiddleware)
# Add vendor context middleware (runs after platform context)
logger.info("Adding VendorContextMiddleware (detects vendor, uses platform_clean_path)")
app.add_middleware(VendorContextMiddleware)
# Add platform context middleware (runs first in request chain, before vendor)
logger.info("Adding PlatformContextMiddleware (detects platform from domain/path)")
app.add_middleware(PlatformContextMiddleware)
logger.info("=" * 80)
logger.info("MIDDLEWARE ORDER SUMMARY:")
logger.info(" Execution order (request →):")
logger.info(" 1. LoggingMiddleware (timing)")
logger.info(" 2. PlatformContextMiddleware (platform detection)")
logger.info(" 3. VendorContextMiddleware (vendor detection)")
logger.info(" 4. ContextMiddleware (context detection)")
logger.info(" 5. LanguageMiddleware (language detection)")
logger.info(" 6. ThemeContextMiddleware (theme loading)")
logger.info(" 7. FastAPI Router")
logger.info("=" * 80)
# ========================================
# MOUNT STATIC FILES - Use absolute path
# ========================================
if STATIC_DIR.exists():
app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
logger.info(f"Mounted static files from: {STATIC_DIR}")
else:
logger.warning(f"Static directory not found at {STATIC_DIR}")
# Mount uploads directory for user-uploaded media files
UPLOADS_DIR = BASE_DIR / "uploads"
if UPLOADS_DIR.exists():
app.mount("/uploads", StaticFiles(directory=str(UPLOADS_DIR)), name="uploads")
logger.info(f"Mounted uploads from: {UPLOADS_DIR}")
else:
# Create uploads directory if it doesn't exist
UPLOADS_DIR.mkdir(parents=True, exist_ok=True)
app.mount("/uploads", StaticFiles(directory=str(UPLOADS_DIR)), name="uploads")
logger.info(f"Created and mounted uploads directory: {UPLOADS_DIR}")
# ========================================
# Include API router (JSON endpoints at /api/*)
app.include_router(api_router, prefix="/api")
# ============================================================================
# FAVICON ROUTES (Must be registered BEFORE page routers)
# ============================================================================
def serve_favicon() -> Response:
"""
Serve favicon with caching headers.
Checks multiple possible locations for the favicon.
"""
possible_paths = [
STATIC_DIR / "favicon.ico",
STATIC_DIR / "images" / "favicon.ico",
STATIC_DIR / "assets" / "favicon.ico",
]
for favicon_path in possible_paths:
if favicon_path.exists():
return FileResponse(
favicon_path,
media_type="image/x-icon",
headers={
"Cache-Control": "public, max-age=86400", # Cache for 1 day
},
)
return Response(status_code=204)
@app.get("/favicon.ico", include_in_schema=False)
async def favicon():
"""Serve favicon from root path."""
return serve_favicon()
@app.get("/vendor/favicon.ico", include_in_schema=False)
async def vendor_favicon():
"""Handle vendor-prefixed favicon requests."""
return serve_favicon()
# ============================================================================
# HEALTH CHECK (Must be before catch-all routes)
# ============================================================================
@app.get("/health")
def health_check(db: Session = Depends(get_db)):
"""Health check endpoint"""
try:
# Test database connection
db.execute(text("SELECT 1"))
return {
"status": "healthy",
"timestamp": datetime.now(UTC),
"message": f"{settings.project_name} v{settings.version}",
"docs": {
"swagger": "/docs",
"redoc": "/redoc",
"openapi": "/openapi.json",
"complete": "/documentation",
},
"features": [
"Multi-tenant architecture with vendor isolation",
"JWT Authentication with role-based access control",
"Marketplace product import and curation",
"Vendor catalog management",
"Product-based inventory tracking",
"Stripe Connect payment processing",
],
"supported_marketplaces": [
"Letzshop",
],
"deployment_modes": [
"Subdomain-based (production): vendor.platform.com",
"Custom domain (production): customvendordomain.com",
"Path-based (development): /vendors/vendorname/ or /vendor/vendorname/",
],
"auth_required": "Most endpoints require Bearer token authentication",
}
except Exception as e:
logger.error(f"Health check failed: {e}")
raise ServiceUnavailableException("Service unhealthy")
# ============================================================================
# HTML PAGE ROUTES (Jinja2 Templates)
# ============================================================================
# Include HTML page routes (these return rendered templates, not JSON)
logger.info("=" * 80)
logger.info("ROUTE REGISTRATION")
logger.info("=" * 80)
# Platform marketing pages (homepage, pricing, signup)
logger.info("Registering platform page routes: /*, /pricing, /find-shop, /signup")
app.include_router(
platform_pages.router, prefix="", tags=["platform-pages"], include_in_schema=False
)
# Admin pages
logger.info("Registering admin page routes: /admin/*")
app.include_router(
admin_pages.router, prefix="/admin", tags=["admin-pages"], include_in_schema=False
)
# Vendor management pages (dashboard, products, orders, etc.)
logger.info("Registering vendor page routes: /vendor/{code}/*")
app.include_router(
vendor_pages.router,
prefix="/vendor",
tags=["vendor-pages"],
include_in_schema=False,
)
# Customer shop pages - Register at TWO prefixes:
# 1. /shop/* (for subdomain/custom domain modes)
# 2. /vendors/{code}/shop/* (for path-based development mode)
logger.info("Registering shop page routes:")
logger.info(" - /shop/* (subdomain/custom domain mode)")
logger.info(" - /vendors/{code}/shop/* (path-based development mode)")
app.include_router(
shop_pages.router, prefix="/shop", tags=["shop-pages"], include_in_schema=False
)
app.include_router(
shop_pages.router,
prefix="/vendors/{vendor_code}/shop",
tags=["shop-pages"],
include_in_schema=False,
)
# Add handler for /vendors/{vendor_code}/ root path
@app.get(
"/vendors/{vendor_code}/", response_class=HTMLResponse, include_in_schema=False
)
async def vendor_root_path(
vendor_code: str, request: Request, db: Session = Depends(get_db)
):
"""Handle vendor root path (e.g., /vendors/wizamart/)"""
# Vendor should already be in request.state from middleware
vendor = getattr(request.state, "vendor", None)
platform = getattr(request.state, "platform", None)
if not vendor:
raise HTTPException(status_code=404, detail=f"Vendor '{vendor_code}' not found")
from app.routes.shop_pages import get_shop_context
from app.services.content_page_service import content_page_service
# Get platform_id (use platform from context or default to 1 for OMS)
platform_id = platform.id if platform else 1
# Try to find landing page (with three-tier resolution)
landing_page = content_page_service.get_page_for_vendor(
db, platform_id=platform_id, slug="landing", vendor_id=vendor.id, include_unpublished=False
)
if not landing_page:
landing_page = content_page_service.get_page_for_vendor(
db, platform_id=platform_id, slug="home", vendor_id=vendor.id, include_unpublished=False
)
if landing_page:
# Render landing page with selected template
template_name = landing_page.template or "default"
template_path = f"vendor/landing-{template_name}.html"
return templates.TemplateResponse(
template_path, get_shop_context(request, db=db, page=landing_page)
)
# No landing page - redirect to shop
return RedirectResponse(url=f"/vendors/{vendor_code}/shop/", status_code=302)
# ============================================================================
# PLATFORM PUBLIC PAGES (Platform Homepage, About, FAQ, etc.)
# ============================================================================
logger.info("Registering platform public page routes:")
logger.info(" - / (platform homepage)")
logger.info(" - /{platform_code}/ (platform-prefixed homepage for dev mode)")
logger.info(" - /{platform_code}/{slug} (platform-prefixed content pages)")
logger.info(" - /{slug} (platform content pages: /about, /faq, /terms, /contact)")
@app.get("/", response_class=HTMLResponse, include_in_schema=False)
async def platform_homepage(request: Request, db: Session = Depends(get_db)):
"""
Platform homepage at localhost:8000 or platform.com
Uses multi-platform CMS with three-tier resolution:
1. Platform marketing pages (is_platform_page=True)
2. Vendor default pages (fallback)
3. Vendor override pages
Falls back to default static template if not found.
"""
from app.services.content_page_service import content_page_service
logger.debug("[PLATFORM] Homepage requested")
# Get platform from middleware (multi-platform support)
platform = getattr(request.state, "platform", None)
if platform:
# Try to load platform homepage from CMS (platform marketing page)
homepage = content_page_service.get_platform_page(
db,
platform_id=platform.id,
slug="home",
include_unpublished=False,
)
# Also try platform_homepage slug for backwards compatibility
if not homepage:
homepage = content_page_service.get_platform_page(
db,
platform_id=platform.id,
slug="platform_homepage",
include_unpublished=False,
)
# Load header and footer navigation (platform marketing pages)
header_pages = content_page_service.list_platform_pages(
db, platform_id=platform.id, header_only=True, include_unpublished=False
)
footer_pages = content_page_service.list_platform_pages(
db, platform_id=platform.id, footer_only=True, include_unpublished=False
)
else:
# Fallback for when no platform context (shouldn't happen normally)
homepage = None
header_pages = []
footer_pages = []
# Get language from request state and build i18n context
language = getattr(request.state, "language", "fr")
i18n_globals = get_jinja2_globals(language)
if homepage:
# Use template selection from CMS
template_name = homepage.template or "default"
template_path = f"platform/homepage-{template_name}.html"
logger.info(f"[PLATFORM] Rendering CMS homepage with template: {template_path}")
context = {
"request": request,
"page": homepage,
"header_pages": header_pages,
"footer_pages": footer_pages,
}
context.update(i18n_globals)
return templates.TemplateResponse(template_path, context)
# Fallback to default static template
logger.info("[PLATFORM] No CMS homepage found, using default template")
context = {
"request": request,
"header_pages": header_pages,
"footer_pages": footer_pages,
}
context.update(i18n_globals)
return templates.TemplateResponse("platform/homepage-default.html", context)
# ============================================================================
# PLATFORM-PREFIXED ROUTES (Development mode: /oms/, /loyalty/, etc.)
# These routes handle path-based platform routing in development.
# In production, domain-based routing means these won't be needed.
# ============================================================================
async def _serve_platform_homepage(request: Request, platform_code: str, db: Session):
"""Helper function to serve platform homepage."""
from app.services.content_page_service import content_page_service
from models.database.platform import Platform
# Get platform from middleware or query directly
platform = getattr(request.state, "platform", None)
if not platform:
platform = db.query(Platform).filter(
Platform.code == platform_code.lower(),
Platform.is_active == True
).first()
if not platform:
raise HTTPException(status_code=404, detail=f"Platform not found: {platform_code}")
# Load platform homepage from CMS
homepage = content_page_service.get_platform_page(
db, platform_id=platform.id, slug="home", include_unpublished=False
)
if not homepage:
homepage = content_page_service.get_platform_page(
db, platform_id=platform.id, slug="platform_homepage", include_unpublished=False
)
# Load navigation
header_pages = content_page_service.list_platform_pages(
db, platform_id=platform.id, header_only=True, include_unpublished=False
)
footer_pages = content_page_service.list_platform_pages(
db, platform_id=platform.id, footer_only=True, include_unpublished=False
)
# Build context
language = getattr(request.state, "language", "fr")
i18n_globals = get_jinja2_globals(language)
if homepage:
template_name = homepage.template or "default"
template_path = f"platform/homepage-{template_name}.html"
logger.info(f"[PLATFORM] Rendering {platform.code} homepage: {template_path}")
context = {
"request": request,
"page": homepage,
"platform": platform,
"header_pages": header_pages,
"footer_pages": footer_pages,
}
context.update(i18n_globals)
return templates.TemplateResponse(template_path, context)
# Fallback
logger.info(f"[PLATFORM] No CMS homepage for {platform.code}, using default")
context = {
"request": request,
"platform": platform,
"header_pages": header_pages,
"footer_pages": footer_pages,
}
context.update(i18n_globals)
return templates.TemplateResponse("platform/homepage-default.html", context)
async def _serve_platform_content_page(request: Request, platform_code: str, slug: str, db: Session):
"""Helper function to serve platform content page."""
from app.services.content_page_service import content_page_service
from models.database.platform import Platform
# Get platform
platform = getattr(request.state, "platform", None)
if not platform:
platform = db.query(Platform).filter(
Platform.code == platform_code.lower(),
Platform.is_active == True
).first()
if not platform:
raise HTTPException(status_code=404, detail=f"Platform not found: {platform_code}")
# Load content page
page = content_page_service.get_platform_page(
db, platform_id=platform.id, slug=slug, include_unpublished=False
)
if not page:
logger.warning(f"[PLATFORM] Page not found: /{platform_code}/{slug}")
raise HTTPException(status_code=404, detail=f"Page not found: {slug}")
# Load navigation
header_pages = content_page_service.list_platform_pages(
db, platform_id=platform.id, header_only=True, include_unpublished=False
)
footer_pages = content_page_service.list_platform_pages(
db, platform_id=platform.id, footer_only=True, include_unpublished=False
)
logger.info(f"[PLATFORM] Rendering {platform.code} page: {page.title} (/{slug})")
language = getattr(request.state, "language", "fr")
i18n_globals = get_jinja2_globals(language)
context = {
"request": request,
"page": page,
"platform": platform,
"header_pages": header_pages,
"footer_pages": footer_pages,
}
context.update(i18n_globals)
return templates.TemplateResponse("platform/content-page.html", context)
# Explicit routes for OMS platform
@app.get("/oms", response_class=RedirectResponse, include_in_schema=False)
async def oms_redirect():
"""Redirect /oms to /oms/"""
return RedirectResponse(url="/oms/", status_code=307)
@app.get("/oms/", response_class=HTMLResponse, include_in_schema=False)
async def oms_homepage(request: Request, db: Session = Depends(get_db)):
"""OMS platform homepage"""
return await _serve_platform_homepage(request, "oms", db)
@app.get("/oms/{slug}", response_class=HTMLResponse, include_in_schema=False)
async def oms_content_page(request: Request, slug: str, db: Session = Depends(get_db)):
"""OMS platform content pages"""
return await _serve_platform_content_page(request, "oms", slug, db)
# Explicit routes for Loyalty platform
@app.get("/loyalty", response_class=RedirectResponse, include_in_schema=False)
async def loyalty_redirect():
"""Redirect /loyalty to /loyalty/"""
return RedirectResponse(url="/loyalty/", status_code=307)
@app.get("/loyalty/", response_class=HTMLResponse, include_in_schema=False)
async def loyalty_homepage(request: Request, db: Session = Depends(get_db)):
"""Loyalty platform homepage"""
return await _serve_platform_homepage(request, "loyalty", db)
@app.get("/loyalty/{slug}", response_class=HTMLResponse, include_in_schema=False)
async def loyalty_content_page(request: Request, slug: str, db: Session = Depends(get_db)):
"""Loyalty platform content pages"""
return await _serve_platform_content_page(request, "loyalty", slug, db)
# ============================================================================
# GENERIC PLATFORM CONTENT PAGE (must be LAST - catches all /{slug})
# ============================================================================
@app.get("/{slug}", response_class=HTMLResponse, include_in_schema=False)
async def platform_content_page(
request: Request, slug: str, db: Session = Depends(get_db)
):
"""
Platform content pages: /about, /faq, /terms, /contact, etc.
Uses multi-platform CMS with three-tier resolution.
Returns 404 if page not found.
This route MUST be defined LAST to avoid conflicts with other routes.
"""
from app.services.content_page_service import content_page_service
logger.debug(f"[PLATFORM] Content page requested: /{slug}")
# Get platform from middleware (multi-platform support)
platform = getattr(request.state, "platform", None)
if not platform:
logger.warning(f"[PLATFORM] No platform context for content page: {slug}")
raise HTTPException(status_code=404, detail=f"Page not found: {slug}")
# Load platform marketing page from CMS
page = content_page_service.get_platform_page(
db,
platform_id=platform.id,
slug=slug,
include_unpublished=False,
)
if not page:
logger.warning(f"[PLATFORM] Content page not found: {slug}")
raise HTTPException(status_code=404, detail=f"Page not found: {slug}")
# Load header and footer navigation (platform marketing pages)
header_pages = content_page_service.list_platform_pages(
db, platform_id=platform.id, header_only=True, include_unpublished=False
)
footer_pages = content_page_service.list_platform_pages(
db, platform_id=platform.id, footer_only=True, include_unpublished=False
)
logger.info(f"[PLATFORM] Rendering content page: {page.title} (/{slug})")
# Get language from request state and build i18n context
language = getattr(request.state, "language", "fr")
i18n_globals = get_jinja2_globals(language)
context = {
"request": request,
"page": page,
"header_pages": header_pages,
"footer_pages": footer_pages,
}
context.update(i18n_globals)
return templates.TemplateResponse("platform/content-page.html", context)
logger.info("=" * 80)
# Log all registered routes
logger.info("=" * 80)
logger.info("REGISTERED ROUTES SUMMARY")
logger.info("=" * 80)
for route in app.routes:
if hasattr(route, "methods") and hasattr(route, "path"):
methods = ", ".join(route.methods) if route.methods else "N/A"
logger.info(f" {methods:<10} {route.path:<60}")
logger.info("=" * 80)
# ============================================================================
# API ROUTES (JSON Responses)
# ============================================================================
# Public Routes (no authentication required)
@app.get("/", response_class=HTMLResponse, include_in_schema=False)
async def root(request: Request, db: Session = Depends(get_db)):
"""
Smart root handler:
- If vendor detected (domain/subdomain): Show vendor landing page or redirect to shop
- If no vendor (platform root): Redirect to documentation
"""
vendor = getattr(request.state, "vendor", None)
platform = getattr(request.state, "platform", None)
if vendor:
# Vendor context detected - serve landing page
from app.services.content_page_service import content_page_service
# Get platform_id (use platform from context or default to 1 for OMS)
platform_id = platform.id if platform else 1
# Try to find landing page (slug='landing' or 'home') with three-tier resolution
landing_page = content_page_service.get_page_for_vendor(
db, platform_id=platform_id, slug="landing", vendor_id=vendor.id, include_unpublished=False
)
if not landing_page:
# Try 'home' slug as fallback
landing_page = content_page_service.get_page_for_vendor(
db, platform_id=platform_id, slug="home", vendor_id=vendor.id, include_unpublished=False
)
if landing_page:
# Render landing page with selected template
from app.routes.shop_pages import get_shop_context
template_name = landing_page.template or "default"
template_path = f"vendor/landing-{template_name}.html"
return templates.TemplateResponse(
template_path, get_shop_context(request, db=db, page=landing_page)
)
# No landing page - redirect to shop
vendor_context = getattr(request.state, "vendor_context", None)
access_method = (
vendor_context.get("detection_method", "unknown")
if vendor_context
else "unknown"
)
if access_method == "path":
full_prefix = (
vendor_context.get("full_prefix", "/vendor/")
if vendor_context
else "/vendor/"
)
return RedirectResponse(
url=f"{full_prefix}{vendor.subdomain}/shop/", status_code=302
)
# Domain/subdomain
return RedirectResponse(url="/shop/", status_code=302)
# No vendor - platform root
return RedirectResponse(url="/documentation")
@app.get("/documentation", response_class=HTMLResponse, include_in_schema=False)
async def documentation():
"""Redirect to documentation"""
if settings.debug:
return RedirectResponse(url="http://localhost:8001")
return RedirectResponse(url=settings.documentation_url)
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)