middleware fix for path-based vendor url
This commit is contained in:
124
main.py
124
main.py
@@ -1,4 +1,14 @@
|
||||
# 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 sys
|
||||
import io
|
||||
|
||||
@@ -28,9 +38,11 @@ from app.core.database import get_db
|
||||
from app.core.lifespan import lifespan
|
||||
from app.exceptions.handler import setup_exception_handlers
|
||||
from app.exceptions import ServiceUnavailableException
|
||||
from middleware.context_middleware import context_middleware
|
||||
from middleware.theme_context import theme_context_middleware
|
||||
from middleware.vendor_context import vendor_context_middleware
|
||||
|
||||
# Import REFACTORED class-based middleware
|
||||
from middleware.vendor_context import VendorContextMiddleware
|
||||
from middleware.context_middleware import ContextMiddleware
|
||||
from middleware.theme_context import ThemeContextMiddleware
|
||||
from middleware.logging_middleware import LoggingMiddleware
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -63,23 +75,66 @@ app.add_middleware(
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Add vendor context middleware (must be after CORS)
|
||||
app.middleware("http")(vendor_context_middleware)
|
||||
# ============================================================================
|
||||
# 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. VendorContextMiddleware (detect vendor, extract clean_path)
|
||||
# 2. ContextMiddleware (detect context using clean_path)
|
||||
# 3. ThemeContextMiddleware (load theme)
|
||||
# 4. LoggingMiddleware (log all requests)
|
||||
#
|
||||
# Therefore we add them in REVERSE:
|
||||
# - Add ThemeContextMiddleware FIRST (runs LAST in request)
|
||||
# - Add ContextMiddleware SECOND
|
||||
# - Add VendorContextMiddleware THIRD
|
||||
# - Add LoggingMiddleware LAST (runs FIRST for timing)
|
||||
# ============================================================================
|
||||
|
||||
# Add middleware (AFTER vendor_context_middleware)
|
||||
app.middleware("http")(context_middleware)
|
||||
logger.info("=" * 80)
|
||||
logger.info("MIDDLEWARE REGISTRATION")
|
||||
logger.info("=" * 80)
|
||||
|
||||
# Add theme context middleware (must be after vendor context)
|
||||
app.middleware("http")(theme_context_middleware)
|
||||
|
||||
# Add logging middleware (logs all requests/responses)
|
||||
# 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 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 first in request chain)
|
||||
logger.info("Adding VendorContextMiddleware (detects vendor, extracts clean_path)")
|
||||
app.add_middleware(VendorContextMiddleware)
|
||||
|
||||
logger.info("=" * 80)
|
||||
logger.info("MIDDLEWARE ORDER SUMMARY:")
|
||||
logger.info(" Execution order (request →):")
|
||||
logger.info(" 1. LoggingMiddleware (timing)")
|
||||
logger.info(" 2. VendorContextMiddleware (vendor detection)")
|
||||
logger.info(" 3. ContextMiddleware (context detection)")
|
||||
logger.info(" 4. ThemeContextMiddleware (theme loading)")
|
||||
logger.info(" 5. 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}")
|
||||
# ========================================
|
||||
@@ -87,7 +142,6 @@ else:
|
||||
# Include API router (JSON endpoints at /api/*)
|
||||
app.include_router(api_router, prefix="/api")
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# FAVICON ROUTES (Must be registered BEFORE page routers)
|
||||
# ============================================================================
|
||||
@@ -97,14 +151,12 @@ def serve_favicon() -> Response:
|
||||
Serve favicon with caching headers.
|
||||
Checks multiple possible locations for the favicon.
|
||||
"""
|
||||
# Possible favicon locations (in priority order)
|
||||
possible_paths = [
|
||||
STATIC_DIR / "favicon.ico",
|
||||
STATIC_DIR / "images" / "favicon.ico",
|
||||
STATIC_DIR / "assets" / "favicon.ico",
|
||||
]
|
||||
|
||||
# Find first existing favicon
|
||||
for favicon_path in possible_paths:
|
||||
if favicon_path.exists():
|
||||
return FileResponse(
|
||||
@@ -115,7 +167,6 @@ def serve_favicon() -> Response:
|
||||
}
|
||||
)
|
||||
|
||||
# No favicon found - return 204 No Content
|
||||
return Response(status_code=204)
|
||||
|
||||
|
||||
@@ -136,30 +187,60 @@ async def vendor_favicon():
|
||||
# ============================================================================
|
||||
# Include HTML page routes (these return rendered templates, not JSON)
|
||||
|
||||
logger.info("=" * 80)
|
||||
logger.info("ROUTE REGISTRATION")
|
||||
logger.info("=" * 80)
|
||||
|
||||
# Admin pages
|
||||
logger.info("Registering admin page routes: /admin/*")
|
||||
app.include_router(
|
||||
admin_pages.router,
|
||||
prefix="/admin",
|
||||
tags=["admin-pages"],
|
||||
include_in_schema=False # Don't show HTML pages in API docs
|
||||
include_in_schema=False
|
||||
)
|
||||
|
||||
# Vendor pages
|
||||
# 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 # Don't show HTML pages in API docs
|
||||
include_in_schema=False
|
||||
)
|
||||
|
||||
# Shop pages
|
||||
# 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 # Don't show HTML pages in API docs
|
||||
include_in_schema=False
|
||||
)
|
||||
|
||||
app.include_router(
|
||||
shop_pages.router,
|
||||
prefix="/vendors/{vendor_code}/shop",
|
||||
tags=["shop-pages"],
|
||||
include_in_schema=False
|
||||
)
|
||||
|
||||
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)
|
||||
@@ -201,7 +282,8 @@ def health_check(db: Session = Depends(get_db)):
|
||||
],
|
||||
"deployment_modes": [
|
||||
"Subdomain-based (production): vendor.platform.com",
|
||||
"Path-based (development): /vendor/vendorname/",
|
||||
"Custom domain (production): customvendordomain.com",
|
||||
"Path-based (development): /vendors/vendorname/ or /vendor/vendorname/",
|
||||
],
|
||||
"auth_required": "Most endpoints require Bearer token authentication",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user