refactor: rename public routes and templates to platform

Complete the public -> platform naming migration across the codebase.
This aligns with the naming convention where "platform" refers to
the marketing/public-facing pages of the platform itself.

Changes:
- Update all imports from public to platform modules
- Update template references from public/ to platform/
- Update route registrations to use platform prefix
- Update documentation to reflect new naming
- Update test files for platform API endpoints

Files affected:
- app/api/main.py - router imports
- app/modules/*/routes/*/platform.py - route definitions
- app/modules/*/templates/*/platform/ - template files
- app/modules/routes.py - route discovery
- docs/* - documentation updates
- tests/integration/api/v1/platform/ - test files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-02 18:49:39 +01:00
parent 967f08e4ba
commit fb8cb14506
44 changed files with 980 additions and 327 deletions

View File

@@ -1,11 +1,11 @@
# app/modules/billing/routes/api/public.py
# app/modules/billing/routes/api/platform.py
"""
Public pricing API endpoints.
Platform pricing API endpoints.
Provides subscription tier and add-on product information
for the marketing homepage and signup flow.
All endpoints are public (no authentication required).
All endpoints are unauthenticated (no authentication required).
"""
from fastapi import APIRouter, Depends

View File

@@ -1,8 +1,8 @@
# app/modules/billing/routes/pages/public.py
# app/modules/billing/routes/pages/platform.py
"""
Billing Public Page Routes (HTML rendering).
Billing Platform Page Routes (HTML rendering).
Public (unauthenticated) pages for pricing and signup:
Platform (unauthenticated) pages for pricing and signup:
- Pricing page
- Signup wizard
- Signup success
@@ -14,7 +14,7 @@ from sqlalchemy.orm import Session
from app.core.database import get_db
from app.modules.billing.models import TIER_LIMITS, TierCode
from app.modules.core.utils.page_context import get_public_context
from app.modules.core.utils.page_context import get_platform_context
from app.templates_config import templates
router = APIRouter()
@@ -57,12 +57,12 @@ async def pricing_page(
"""
Standalone pricing page with detailed tier comparison.
"""
context = get_public_context(request, db)
context = get_platform_context(request, db)
context["tiers"] = _get_tiers_data()
context["page_title"] = "Pricing"
return templates.TemplateResponse(
"billing/public/pricing.html",
"billing/platform/pricing.html",
context,
)
@@ -86,14 +86,14 @@ async def signup_page(
- tier: Pre-selected tier code
- annual: Pre-select annual billing
"""
context = get_public_context(request, db)
context = get_platform_context(request, db)
context["page_title"] = "Start Your Free Trial"
context["selected_tier"] = tier
context["is_annual"] = annual
context["tiers"] = _get_tiers_data()
return templates.TemplateResponse(
"billing/public/signup.html",
"billing/platform/signup.html",
context,
)
@@ -111,11 +111,11 @@ async def signup_success_page(
Shown after successful account creation.
"""
context = get_public_context(request, db)
context = get_platform_context(request, db)
context["page_title"] = "Welcome to Wizamart!"
context["vendor_code"] = vendor_code
return templates.TemplateResponse(
"billing/public/signup-success.html",
"billing/platform/signup-success.html",
context,
)

View File

@@ -1,6 +1,6 @@
{# app/templates/platform/pricing.html #}
{# Standalone Pricing Page #}
{% extends "public/base.html" %}
{% extends "platform/base.html" %}
{% block title %}{{ _("cms.platform.pricing.title") }} - Wizamart{% endblock %}

View File

@@ -1,6 +1,6 @@
{# app/templates/platform/signup-success.html #}
{# Signup Success Page #}
{% extends "public/base.html" %}
{% extends "platform/base.html" %}
{% block title %}{{ _("cms.platform.success.title") }}{% endblock %}

View File

@@ -1,6 +1,6 @@
{# app/templates/platform/signup.html #}
{# Multi-step Signup Wizard #}
{% extends "public/base.html" %}
{% extends "platform/base.html" %}
{% block title %}Start Your Free Trial - Wizamart{% endblock %}
@@ -321,7 +321,7 @@ function signupWizard() {
async startSignup() {
this.loading = true;
try {
const response = await fetch('/api/v1/public/signup/start', {
const response = await fetch('/api/v1/platform/signup/start', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -352,7 +352,7 @@ function signupWizard() {
try {
// First lookup the vendor
const lookupResponse = await fetch('/api/v1/public/letzshop-vendors/lookup', {
const lookupResponse = await fetch('/api/v1/platform/letzshop-vendors/lookup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url: this.letzshopUrl })
@@ -364,7 +364,7 @@ function signupWizard() {
this.letzshopVendor = lookupData.vendor;
// Claim the vendor
const claimResponse = await fetch('/api/v1/public/signup/claim-vendor', {
const claimResponse = await fetch('/api/v1/platform/signup/claim-vendor', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -411,7 +411,7 @@ function signupWizard() {
this.accountError = null;
try {
const response = await fetch('/api/v1/public/signup/create-account', {
const response = await fetch('/api/v1/platform/signup/create-account', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -461,7 +461,7 @@ function signupWizard() {
// Get SetupIntent
try {
const response = await fetch('/api/v1/public/signup/setup-payment', {
const response = await fetch('/api/v1/platform/signup/setup-payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ session_id: this.sessionId })
@@ -500,7 +500,7 @@ function signupWizard() {
}
// Complete signup
const response = await fetch('/api/v1/public/signup/complete', {
const response = await fetch('/api/v1/platform/signup/complete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({

View File

@@ -1,8 +1,8 @@
# app/modules/cms/routes/pages/public.py
# app/modules/cms/routes/pages/platform.py
"""
CMS Public Page Routes (HTML rendering).
CMS Platform Page Routes (HTML rendering).
Public (unauthenticated) pages for platform content:
Platform (unauthenticated) pages for platform content:
- Homepage
- Generic content pages (/{slug} catch-all)
"""
@@ -16,7 +16,7 @@ from sqlalchemy.orm import Session
from app.core.database import get_db
from app.modules.billing.models import TIER_LIMITS, TierCode
from app.modules.cms.services import content_page_service
from app.modules.core.utils.page_context import get_public_context
from app.modules.core.utils.page_context import get_platform_context
from app.templates_config import templates
logger = logging.getLogger(__name__)
@@ -147,19 +147,19 @@ async def homepage(
if cms_homepage:
# Use CMS-based homepage with template selection
context = get_public_context(request, db)
context = get_platform_context(request, db)
context["page"] = cms_homepage
context["tiers"] = _get_tiers_data()
template_name = cms_homepage.template or "default"
template_path = f"cms/public/homepage-{template_name}.html"
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 wizamart homepage (no CMS content)
logger.info("[HOMEPAGE] No CMS homepage found, using default wizamart template")
context = get_public_context(request, db)
context = get_platform_context(request, db)
context["tiers"] = _get_tiers_data()
# Add-ons (hardcoded for now, will come from DB)
@@ -196,7 +196,7 @@ async def homepage(
]
return templates.TemplateResponse(
"cms/public/homepage-wizamart.html",
"cms/platform/homepage-wizamart.html",
context,
)
@@ -231,11 +231,11 @@ async def content_page(
if not page:
raise HTTPException(status_code=404, detail=f"Page not found: {slug}")
context = get_public_context(request, db)
context = get_platform_context(request, db)
context["page"] = page
context["page_title"] = page.title
return templates.TemplateResponse(
"cms/public/content-page.html",
"cms/platform/content-page.html",
context,
)

View File

@@ -1,6 +1,6 @@
{# app/templates/platform/content-page.html #}
{# Generic template for platform content pages (About, FAQ, Terms, Contact, etc.) #}
{% extends "public/base.html" %}
{% extends "platform/base.html" %}
{% block title %}{{ page.title }} - Marketplace{% endblock %}

View File

@@ -1,6 +1,6 @@
{# app/templates/platform/homepage-default.html #}
{# Default platform homepage template with section-based rendering #}
{% extends "public/base.html" %}
{% extends "platform/base.html" %}
{# Import section partials #}
{% from 'platform/sections/_hero.html' import render_hero %}

View File

@@ -1,6 +1,6 @@
{# app/templates/platform/homepage-minimal.html #}
{# Minimal/clean platform homepage template #}
{% extends "public/base.html" %}
{% extends "platform/base.html" %}
{% block title %}
{% if page %}{{ page.title }}{% else %}Home{% endif %} - Marketplace

View File

@@ -1,6 +1,6 @@
{# app/templates/platform/homepage-modern.html #}
{# Wizamart OMS - Luxembourg-focused homepage inspired by Veeqo #}
{% extends "public/base.html" %}
{% extends "platform/base.html" %}
{% block title %}
Wizamart - The Back-Office for Letzshop Sellers

View File

@@ -1,6 +1,6 @@
{# app/templates/platform/homepage-wizamart.html #}
{# Wizamart Marketing Homepage - Letzshop OMS Platform #}
{% extends "public/base.html" %}
{% extends "platform/base.html" %}
{% from 'shared/macros/inputs.html' import toggle_switch %}
{% block title %}Wizamart - Order Management for Letzshop Sellers{% endblock %}
@@ -407,7 +407,7 @@ function homepageData() {
this.vendorResult = null;
try {
const response = await fetch('/api/v1/public/letzshop-vendors/lookup', {
const response = await fetch('/api/v1/platform/letzshop-vendors/lookup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url: this.shopUrl })

View File

@@ -1,13 +1,13 @@
# app/modules/core/routes/api/public.py
# app/modules/core/routes/api/platform.py
"""
Public language API endpoints.
Platform language API endpoints.
Handles:
- Setting language preference via cookie
- Getting current language info
- Listing available languages
All endpoints are public (no authentication required).
All endpoints are unauthenticated (no authentication required).
"""
import logging

View File

@@ -5,12 +5,12 @@ from .page_context import (
get_admin_context,
get_vendor_context,
get_storefront_context,
get_public_context,
get_platform_context,
)
__all__ = [
"get_admin_context",
"get_vendor_context",
"get_storefront_context",
"get_public_context",
"get_platform_context",
]

View File

@@ -253,13 +253,13 @@ def get_storefront_context(
return context
def get_public_context(
def get_platform_context(
request: Request,
db: Session,
**extra_context,
) -> dict:
"""
Build context for public/marketing pages.
Build context for platform/marketing pages.
Includes platform info, i18n globals, and CMS navigation pages.

View File

@@ -1,8 +1,8 @@
# app/modules/loyalty/routes/api/public.py
# app/modules/loyalty/routes/api/platform.py
"""
Loyalty module public routes.
Loyalty module platform routes.
Public endpoints for:
Platform endpoints for:
- Customer enrollment (by vendor code)
- Apple Wallet pass download
- Apple Web Service endpoints for device registration/updates
@@ -29,8 +29,8 @@ from app.modules.loyalty.services import (
logger = logging.getLogger(__name__)
# Public router (no auth required for some endpoints)
public_router = APIRouter(prefix="/loyalty")
# Platform router (no auth required for some endpoints)
platform_router = APIRouter(prefix="/loyalty")
# =============================================================================
@@ -38,7 +38,7 @@ public_router = APIRouter(prefix="/loyalty")
# =============================================================================
@public_router.get("/programs/{vendor_code}")
@platform_router.get("/programs/{vendor_code}")
def get_program_by_vendor_code(
vendor_code: str = Path(..., min_length=1, max_length=50),
db: Session = Depends(get_db),
@@ -85,7 +85,7 @@ def get_program_by_vendor_code(
# =============================================================================
@public_router.get("/passes/apple/{serial_number}.pkpass")
@platform_router.get("/passes/apple/{serial_number}.pkpass")
def download_apple_pass(
serial_number: str = Path(...),
db: Session = Depends(get_db),
@@ -122,7 +122,7 @@ def download_apple_pass(
# =============================================================================
@public_router.post("/apple/v1/devices/{device_id}/registrations/{pass_type_id}/{serial_number}")
@platform_router.post("/apple/v1/devices/{device_id}/registrations/{pass_type_id}/{serial_number}")
def register_device(
device_id: str = Path(...),
pass_type_id: str = Path(...),
@@ -165,7 +165,7 @@ def register_device(
raise HTTPException(status_code=500)
@public_router.delete("/apple/v1/devices/{device_id}/registrations/{pass_type_id}/{serial_number}")
@platform_router.delete("/apple/v1/devices/{device_id}/registrations/{pass_type_id}/{serial_number}")
def unregister_device(
device_id: str = Path(...),
pass_type_id: str = Path(...),
@@ -205,7 +205,7 @@ def unregister_device(
raise HTTPException(status_code=500)
@public_router.get("/apple/v1/devices/{device_id}/registrations/{pass_type_id}")
@platform_router.get("/apple/v1/devices/{device_id}/registrations/{pass_type_id}")
def get_serial_numbers(
device_id: str = Path(...),
pass_type_id: str = Path(...),
@@ -256,7 +256,7 @@ def get_serial_numbers(
}
@public_router.get("/apple/v1/passes/{pass_type_id}/{serial_number}")
@platform_router.get("/apple/v1/passes/{pass_type_id}/{serial_number}")
def get_latest_pass(
pass_type_id: str = Path(...),
serial_number: str = Path(...),
@@ -302,7 +302,7 @@ def get_latest_pass(
)
@public_router.post("/apple/v1/log")
@platform_router.post("/apple/v1/log")
def log_errors():
"""
Receive error logs from Apple.

View File

@@ -1,11 +1,11 @@
# app/modules/marketplace/routes/api/public.py
# app/modules/marketplace/routes/api/platform.py
"""
Public Letzshop vendor lookup API endpoints.
Platform Letzshop vendor lookup API endpoints.
Allows potential vendors to find themselves in the Letzshop marketplace
and claim their shop during signup.
All endpoints are public (no authentication required).
All endpoints are unauthenticated (no authentication required).
"""
import logging

View File

@@ -1,8 +1,8 @@
# app/modules/marketplace/routes/pages/public.py
# app/modules/marketplace/routes/pages/platform.py
"""
Marketplace Public Page Routes (HTML rendering).
Marketplace Platform Page Routes (HTML rendering).
Public (unauthenticated) pages:
Platform (unauthenticated) pages:
- Find shop (Letzshop vendor browser)
"""
@@ -11,7 +11,7 @@ from fastapi.responses import HTMLResponse
from sqlalchemy.orm import Session
from app.core.database import get_db
from app.modules.core.utils.page_context import get_public_context
from app.modules.core.utils.page_context import get_platform_context
from app.templates_config import templates
router = APIRouter()
@@ -32,10 +32,10 @@ async def find_shop_page(
Allows vendors to search for and claim their Letzshop shop.
"""
context = get_public_context(request, db)
context = get_platform_context(request, db)
context["page_title"] = "Find Your Letzshop Shop"
return templates.TemplateResponse(
"marketplace/public/find-shop.html",
"marketplace/platform/find-shop.html",
context,
)

View File

@@ -1,6 +1,6 @@
{# app/modules/marketplace/templates/marketplace/public/find-shop.html #}
{# Letzshop Vendor Finder Page #}
{% extends "public/base.html" %}
{% extends "platform/base.html" %}
{% block title %}{{ _("cms.platform.find_shop.title") }} - Wizamart{% endblock %}
@@ -151,7 +151,7 @@ function vendorFinderData() {
this.result = null;
try {
const response = await fetch('/api/v1/public/letzshop-vendors/lookup', {
const response = await fetch('/api/v1/platform/letzshop-vendors/lookup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url: this.searchQuery })

View File

@@ -183,9 +183,9 @@ def _discover_routes_in_dir(
"pages_prefix": "/storefront",
"include_in_schema": True if route_type == "api" else False,
},
"public": {
"api_prefix": "/api/v1/public",
"pages_prefix": "/public",
"platform": {
"api_prefix": "/api/v1/platform",
"pages_prefix": "/platform",
"include_in_schema": True,
},
"webhooks": {
@@ -335,16 +335,16 @@ def get_storefront_api_routes() -> list[RouteInfo]:
return sorted(routes, key=lambda r: r.priority)
def get_public_api_routes() -> list[RouteInfo]:
def get_platform_api_routes() -> list[RouteInfo]:
"""
Get public API routes from modules, sorted by priority.
Get platform API routes from modules, sorted by priority.
Public routes are unauthenticated endpoints for marketing pages,
Platform routes are unauthenticated endpoints for marketing pages,
pricing info, and other public-facing features.
"""
routes = [
r for r in discover_module_routes()
if r.route_type == "api" and r.frontend == "public"
if r.route_type == "api" and r.frontend == "platform"
]
return sorted(routes, key=lambda r: r.priority)
@@ -363,11 +363,11 @@ def get_webhooks_api_routes() -> list[RouteInfo]:
return sorted(routes, key=lambda r: r.priority)
def get_public_page_routes() -> list[RouteInfo]:
def get_platform_page_routes() -> list[RouteInfo]:
"""
Get public (marketing) page routes from modules, sorted by priority.
Get platform (marketing) page routes from modules, sorted by priority.
Public pages are unauthenticated marketing pages like:
Platform pages are unauthenticated marketing pages like:
- Homepage (/)
- Pricing (/pricing)
- Signup (/signup)
@@ -379,7 +379,7 @@ def get_public_page_routes() -> list[RouteInfo]:
"""
routes = [
r for r in discover_module_routes()
if r.route_type == "pages" and r.frontend == "public"
if r.route_type == "pages" and r.frontend == "platform"
]
return sorted(routes, key=lambda r: r.priority)
@@ -414,8 +414,8 @@ __all__ = [
"get_admin_api_routes",
"get_vendor_api_routes",
"get_storefront_api_routes",
"get_public_api_routes",
"get_platform_api_routes",
"get_webhooks_api_routes",
"get_public_page_routes",
"get_platform_page_routes",
"get_storefront_page_routes",
]

View File

@@ -135,6 +135,6 @@
</script>
<!-- 6. Login Logic -->
<script src="{{ url_for('tenancy_static', path='admin/js/login.js') }}"></script>
<script src="{{ url_for('core_static', path='admin/js/login.js') }}"></script>
</body>
</html>