Replace all ~1,086 occurrences of Wizamart/wizamart/WIZAMART/WizaMart with Orion/orion/ORION across 184 files. This includes database identifiers, email addresses, domain references, R2 bucket names, DNS prefixes, encryption salt, Celery app name, config defaults, Docker configs, CI configs, documentation, seed data, and templates. Renames homepage-wizamart.html template to homepage-orion.html. Fixes duplicate file_pattern key in api.yaml architecture rule. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
234 lines
7.1 KiB
Python
234 lines
7.1 KiB
Python
# app/modules/cms/routes/pages/store.py
|
|
"""
|
|
CMS Store Page Routes (HTML rendering).
|
|
|
|
Store pages for managing content pages and rendering CMS content.
|
|
"""
|
|
|
|
import logging
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Path, Request
|
|
from fastapi.responses import HTMLResponse
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.api.deps import get_current_store_from_cookie_or_header, get_db
|
|
from app.modules.cms.services import content_page_service
|
|
from app.modules.core.services.platform_settings_service import (
|
|
platform_settings_service, # MOD-004 - shared platform service
|
|
)
|
|
from app.modules.tenancy.models import Store, User
|
|
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,
|
|
}
|
|
|
|
|
|
# ============================================================================
|
|
# HELPER: Build Store Dashboard Context
|
|
# ============================================================================
|
|
|
|
|
|
def get_store_context(
|
|
request: Request,
|
|
db: Session,
|
|
current_user: User,
|
|
store_code: str,
|
|
**extra_context,
|
|
) -> dict:
|
|
"""
|
|
Build template context for store dashboard pages.
|
|
|
|
Resolves locale/currency using the platform settings service with
|
|
store override support.
|
|
"""
|
|
# Load store from database
|
|
store = db.query(Store).filter(Store.subdomain == store_code).first()
|
|
|
|
# Get platform defaults
|
|
platform_config = platform_settings_service.get_storefront_config(db)
|
|
|
|
# Resolve with store override
|
|
storefront_locale = platform_config["locale"]
|
|
storefront_currency = platform_config["currency"]
|
|
|
|
if store and store.storefront_locale:
|
|
storefront_locale = store.storefront_locale
|
|
|
|
context = {
|
|
"request": request,
|
|
"user": current_user,
|
|
"store": store,
|
|
"store_code": store_code,
|
|
"storefront_locale": storefront_locale,
|
|
"storefront_currency": storefront_currency,
|
|
"dashboard_language": store.dashboard_language if store else "en",
|
|
}
|
|
|
|
# Add any extra context
|
|
if extra_context:
|
|
context.update(extra_context)
|
|
|
|
return context
|
|
|
|
|
|
# ============================================================================
|
|
# CONTENT PAGES MANAGEMENT
|
|
# ============================================================================
|
|
|
|
|
|
@router.get(
|
|
"/{store_code}/content-pages", response_class=HTMLResponse, include_in_schema=False
|
|
)
|
|
async def store_content_pages_list(
|
|
request: Request,
|
|
store_code: str = Path(..., description="Store code"),
|
|
current_user: User = Depends(get_current_store_from_cookie_or_header),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
"""
|
|
Render content pages management page.
|
|
Shows platform defaults (can be overridden) and store custom pages.
|
|
"""
|
|
return templates.TemplateResponse(
|
|
"cms/store/content-pages.html",
|
|
get_store_context(request, db, current_user, store_code),
|
|
)
|
|
|
|
|
|
@router.get(
|
|
"/{store_code}/content-pages/create",
|
|
response_class=HTMLResponse,
|
|
include_in_schema=False,
|
|
)
|
|
async def store_content_page_create(
|
|
request: Request,
|
|
store_code: str = Path(..., description="Store code"),
|
|
current_user: User = Depends(get_current_store_from_cookie_or_header),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
"""
|
|
Render content page creation form.
|
|
"""
|
|
return templates.TemplateResponse(
|
|
"cms/store/content-page-edit.html",
|
|
get_store_context(request, db, current_user, store_code, page_id=None),
|
|
)
|
|
|
|
|
|
@router.get(
|
|
"/{store_code}/content-pages/{page_id}/edit",
|
|
response_class=HTMLResponse,
|
|
include_in_schema=False,
|
|
)
|
|
async def store_content_page_edit(
|
|
request: Request,
|
|
store_code: str = Path(..., description="Store code"),
|
|
page_id: int = Path(..., description="Content page ID"),
|
|
current_user: User = Depends(get_current_store_from_cookie_or_header),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
"""
|
|
Render content page edit form.
|
|
"""
|
|
return templates.TemplateResponse(
|
|
"cms/store/content-page-edit.html",
|
|
get_store_context(request, db, current_user, store_code, page_id=page_id),
|
|
)
|
|
|
|
|
|
# ============================================================================
|
|
# DYNAMIC CONTENT PAGES (CMS) - Public Shop Display
|
|
# ============================================================================
|
|
|
|
|
|
@router.get(
|
|
"/{store_code}/{slug}", response_class=HTMLResponse, include_in_schema=False
|
|
)
|
|
async def store_content_page(
|
|
request: Request,
|
|
store_code: str = Path(..., description="Store code"),
|
|
slug: str = Path(..., description="Content page slug"),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
"""
|
|
Generic content page handler for store shop (CMS).
|
|
|
|
Handles dynamic content pages like:
|
|
- /stores/orion/about, /stores/orion/faq, /stores/orion/contact, etc.
|
|
|
|
Features:
|
|
- Two-tier system: Store overrides take priority, fallback to platform defaults
|
|
- Only shows published pages
|
|
- Returns 404 if page not found or unpublished
|
|
|
|
NOTE: This is a catch-all route and must be registered LAST to avoid
|
|
shadowing other specific routes.
|
|
"""
|
|
logger.debug(
|
|
"[CMS] store_content_page REACHED",
|
|
extra={
|
|
"path": request.url.path,
|
|
"store_code": store_code,
|
|
"slug": slug,
|
|
"store": getattr(request.state, "store", "NOT SET"),
|
|
"context": getattr(request.state, "context_type", "NOT SET"),
|
|
},
|
|
)
|
|
|
|
store = getattr(request.state, "store", None)
|
|
platform = getattr(request.state, "platform", None)
|
|
store_id = store.id if store else None
|
|
platform_id = platform.id if platform else 1
|
|
|
|
# Load content page from database (store override → platform default)
|
|
page = content_page_service.get_page_for_store(
|
|
db, platform_id=platform_id, slug=slug, store_id=store_id, include_unpublished=False
|
|
)
|
|
|
|
if not page:
|
|
logger.info(
|
|
f"[CMS] Content page not found: {slug}",
|
|
extra={
|
|
"slug": slug,
|
|
"store_code": store_code,
|
|
"store_id": store_id,
|
|
},
|
|
)
|
|
raise HTTPException(status_code=404, detail="Page not found")
|
|
|
|
logger.info(
|
|
f"[CMS] Rendering page: {page.title}",
|
|
extra={
|
|
"slug": slug,
|
|
"page_id": page.id,
|
|
"is_store_override": page.store_id is not None,
|
|
"store_id": store_id,
|
|
},
|
|
)
|
|
|
|
# Resolve locale for shop template
|
|
platform_config = platform_settings_service.get_storefront_config(db)
|
|
storefront_locale = platform_config["locale"]
|
|
storefront_currency = platform_config["currency"]
|
|
|
|
if store and store.storefront_locale:
|
|
storefront_locale = store.storefront_locale
|
|
|
|
return templates.TemplateResponse(
|
|
"storefront/content-page.html",
|
|
{
|
|
"request": request,
|
|
"page": page,
|
|
"store": store,
|
|
"store_code": store_code,
|
|
"storefront_locale": storefront_locale,
|
|
"storefront_currency": storefront_currency,
|
|
},
|
|
)
|