feat: platform-aware storefront routing and billing improvements
Overhaul storefront URL routing to be platform-aware:
- Dev: /platforms/{code}/storefront/{store_code}/
- Prod: subdomain.platform.lu/ (internally rewritten to /storefront/)
- Add subdomain detection in PlatformContextMiddleware
- Add /storefront/ path rewrite for prod mode (subdomain/custom domain)
- Remove all silent platform fallbacks (platform_id=1)
- Add require_platform dependency for clean endpoint validation
- Update route registration, templates, module definitions, base_url calc
- Update StoreContextMiddleware for /storefront/ path detection
- Remove /stores/ from FrontendDetector STOREFRONT_PATH_PREFIXES
Billing service improvements:
- Add store_platform_sync_service to keep store_platforms in sync
- Make tier lookups platform-aware across billing services
- Add tiers for all platforms in seed data
- Add demo subscriptions to seed
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
57
main.py
57
main.py
@@ -22,7 +22,7 @@ from datetime import UTC, datetime
|
||||
from pathlib import Path
|
||||
|
||||
import sentry_sdk
|
||||
from fastapi import Depends, FastAPI, HTTPException, Request, Response
|
||||
from fastapi import Depends, FastAPI, Response
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import FileResponse, HTMLResponse, RedirectResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
@@ -435,13 +435,13 @@ for route_info in store_page_routes:
|
||||
# STOREFRONT PAGES (Customer Shop)
|
||||
# =============================================================================
|
||||
# Customer shop pages - Register at TWO prefixes:
|
||||
# 1. /storefront/* (for subdomain/custom domain modes)
|
||||
# 2. /stores/{code}/storefront/* (for path-based development mode)
|
||||
# 1. /storefront/* (for prod: subdomain/custom domain, after path rewrite by middleware)
|
||||
# 2. /storefront/{store_code}/* (for dev: path-based, after /platforms/{code}/ strip)
|
||||
logger.info("Auto-discovering storefront page routes...")
|
||||
storefront_page_routes = get_storefront_page_routes()
|
||||
logger.info(f" Found {len(storefront_page_routes)} storefront page route modules")
|
||||
|
||||
# Register at /storefront/* (direct access)
|
||||
# Register at /storefront/* (prod mode — middleware rewrites /products → /storefront/products)
|
||||
logger.info(" Registering storefront routes at /storefront/*")
|
||||
for route_info in storefront_page_routes:
|
||||
prefix = f"/storefront{route_info.custom_prefix}" if route_info.custom_prefix else "/storefront"
|
||||
@@ -453,10 +453,10 @@ for route_info in storefront_page_routes:
|
||||
include_in_schema=False,
|
||||
)
|
||||
|
||||
# Register at /stores/{code}/storefront/* (path-based development mode)
|
||||
logger.info(" Registering storefront routes at /stores/{code}/storefront/*")
|
||||
# Register at /storefront/{store_code}/* (dev mode — /platforms/oms/storefront/WIZATECH/...)
|
||||
logger.info(" Registering storefront routes at /storefront/{store_code}/*")
|
||||
for route_info in storefront_page_routes:
|
||||
prefix = f"/stores/{{store_code}}/storefront{route_info.custom_prefix}" if route_info.custom_prefix else "/stores/{store_code}/storefront"
|
||||
prefix = f"/storefront/{{store_code}}{route_info.custom_prefix}" if route_info.custom_prefix else "/storefront/{store_code}"
|
||||
app.include_router(
|
||||
route_info.router,
|
||||
prefix=prefix,
|
||||
@@ -465,49 +465,6 @@ for route_info in storefront_page_routes:
|
||||
)
|
||||
|
||||
|
||||
# Add handler for /stores/{store_code}/ root path
|
||||
@app.get(
|
||||
"/stores/{store_code}/", response_class=HTMLResponse, include_in_schema=False
|
||||
)
|
||||
async def store_root_path(
|
||||
store_code: str, request: Request, db: Session = Depends(get_db)
|
||||
):
|
||||
"""Handle store root path (e.g., /stores/orion/)"""
|
||||
# Store should already be in request.state from middleware
|
||||
store = getattr(request.state, "store", None)
|
||||
platform = getattr(request.state, "platform", None)
|
||||
|
||||
if not store:
|
||||
raise HTTPException(status_code=404, detail=f"Store '{store_code}' not found")
|
||||
|
||||
from app.modules.cms.services import content_page_service
|
||||
from app.modules.core.utils.page_context import get_storefront_context
|
||||
|
||||
# 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_store(
|
||||
db, platform_id=platform_id, slug="landing", store_id=store.id, include_unpublished=False
|
||||
)
|
||||
|
||||
if not landing_page:
|
||||
landing_page = content_page_service.get_page_for_store(
|
||||
db, platform_id=platform_id, slug="home", store_id=store.id, include_unpublished=False
|
||||
)
|
||||
|
||||
if landing_page:
|
||||
# Render landing page with selected template
|
||||
template_name = landing_page.template or "default"
|
||||
template_path = f"store/landing-{template_name}.html"
|
||||
|
||||
return templates.TemplateResponse(
|
||||
template_path, get_storefront_context(request, db=db, page=landing_page)
|
||||
)
|
||||
# No landing page - redirect to shop
|
||||
return RedirectResponse(url=f"/stores/{store_code}/storefront/", status_code=302)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# PLATFORM ROUTING (via PlatformContextMiddleware)
|
||||
#
|
||||
|
||||
Reference in New Issue
Block a user