# app/modules/cms/routes/pages/platform.py """ CMS Platform Page Routes (HTML rendering). Platform (unauthenticated) pages for platform content: - Homepage - Generic content pages (/{slug} catch-all) """ import logging from fastapi import APIRouter, Depends, HTTPException, Request from fastapi.responses import HTMLResponse, RedirectResponse from sqlalchemy.orm import Session from app.core.database import get_db from app.modules.cms.services import content_page_service from app.modules.core.utils.page_context import get_platform_context from app.templates_config import templates logger = logging.getLogger(__name__) router = APIRouter() # Route configuration - high priority so catch-all is registered last ROUTE_CONFIG = { "priority": 100, } def _get_tiers_data(db: Session) -> list[dict]: """Build tier data for display in templates from database.""" from app.modules.billing.models import SubscriptionTier, TierCode tiers_db = ( db.query(SubscriptionTier) .filter( SubscriptionTier.is_active == True, SubscriptionTier.is_public == True, ) .order_by(SubscriptionTier.display_order) .all() ) tiers = [] for tier in tiers_db: feature_codes = sorted(tier.get_feature_codes()) tiers.append({ "code": tier.code, "name": tier.name, "price_monthly": tier.price_monthly_cents / 100, "price_annual": (tier.price_annual_cents / 100) if tier.price_annual_cents else None, "feature_codes": feature_codes, "products_limit": tier.get_limit_for_feature("products_limit"), "orders_per_month": tier.get_limit_for_feature("orders_per_month"), "team_members": tier.get_limit_for_feature("team_members"), "is_popular": tier.code == TierCode.PROFESSIONAL.value, "is_enterprise": tier.code == TierCode.ENTERPRISE.value, }) return tiers # ============================================================================ # HOMEPAGE # ============================================================================ @router.get("/", response_class=HTMLResponse, name="platform_homepage") async def homepage( request: Request, db: Session = Depends(get_db), ): """ Homepage handler. Handles two scenarios: 1. Store on custom domain (store.com) -> Show store landing page or redirect to shop 2. Platform marketing site -> Show platform homepage from CMS or default template URL routing: - localhost:9999/ -> Main marketing site ('main' platform) - localhost:9999/platforms/oms/ -> OMS platform (middleware rewrites to /) - omsflow.lu/ -> OMS platform (domain-based) - shop.mymerchant.com/ -> Store landing page (custom domain) """ # Get platform and store from middleware platform = getattr(request.state, "platform", None) store = getattr(request.state, "store", None) # Scenario 1: Store detected (custom domain like store.com) if store: logger.debug(f"[HOMEPAGE] Store detected: {store.subdomain}") # Get platform_id (use platform from context or default to 1 for OMS) platform_id = platform.id if platform else 1 # Try to find store landing page (slug='landing' or 'home') 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 from app.modules.core.utils.page_context import get_storefront_context template_name = landing_page.template or "default" template_path = f"cms/storefront/landing-{template_name}.html" logger.info(f"[HOMEPAGE] Rendering store landing page: {template_path}") return templates.TemplateResponse( template_path, get_storefront_context(request, db=db, page=landing_page), ) # No landing page - redirect to shop store_context = getattr(request.state, "store_context", None) access_method = ( store_context.get("detection_method", "unknown") if store_context else "unknown" ) if access_method == "path": full_prefix = ( store_context.get("full_prefix", "/store/") if store_context else "/store/" ) return RedirectResponse( url=f"{full_prefix}{store.subdomain}/storefront/", status_code=302 ) # Domain/subdomain - redirect to /storefront/ return RedirectResponse(url="/storefront/", status_code=302) # Scenario 2: Platform marketing site (no store) # Load platform homepage from CMS (slug='home') platform_id = platform.id if platform else 1 cms_homepage = content_page_service.get_platform_page( db, platform_id=platform_id, slug="home", include_unpublished=False ) if cms_homepage: # Use CMS-based homepage with template selection context = get_platform_context(request, db) context["page"] = cms_homepage context["tiers"] = _get_tiers_data(db) template_name = cms_homepage.template or "default" template_path = f"cms/platform/homepage-{template_name}.html" logger.info(f"[HOMEPAGE] Rendering CMS homepage with template: {template_path}") return templates.TemplateResponse(template_path, context) # Fallback: Default orion homepage (no CMS content) logger.info("[HOMEPAGE] No CMS homepage found, using default orion template") context = get_platform_context(request, db) context["tiers"] = _get_tiers_data(db) # Add-ons (hardcoded for now, will come from DB) context["addons"] = [ { "code": "domain", "name": "Custom Domain", "description": "Use your own domain (mydomain.com)", "price": 15, "billing_period": "year", "icon": "globe", }, { "code": "ssl_premium", "name": "Premium SSL", "description": "EV certificate for trust badges", "price": 49, "billing_period": "year", "icon": "shield-check", }, { "code": "email", "name": "Email Package", "description": "Professional email addresses", "price": 5, "billing_period": "month", "icon": "mail", "options": [ {"quantity": 5, "price": 5}, {"quantity": 10, "price": 9}, {"quantity": 25, "price": 19}, ], }, ] return templates.TemplateResponse( "cms/platform/homepage-orion.html", context, ) # ============================================================================ # GENERIC CONTENT PAGES (CMS) # ============================================================================ # IMPORTANT: This route must be LAST as it catches all /{slug} URLs @router.get("/{slug}", response_class=HTMLResponse, name="platform_content_page") async def content_page( request: Request, slug: str, db: Session = Depends(get_db), ): """ Serve CMS content pages (about, contact, faq, privacy, terms, etc.). This is a catch-all route for dynamic content pages managed via the admin CMS. Platform pages have store_id=None and is_platform_page=True. """ # Get platform from middleware (default to OMS platform_id=1) platform = getattr(request.state, "platform", None) platform_id = platform.id if platform else 1 # Load platform marketing page from database page = content_page_service.get_platform_page( db, platform_id=platform_id, slug=slug, include_unpublished=False ) if not page: raise HTTPException(status_code=404, detail=f"Page not found: {slug}") context = get_platform_context(request, db) context["page"] = page context["page_title"] = page.title return templates.TemplateResponse( "cms/platform/content-page.html", context, )