refactor: update registry and main.py for module auto-discovery
- app/modules/registry.py: Use auto-discovery from discovery.py - main.py: Integrate module route auto-discovery - app/routes/vendor_pages.py: Remove routes now handled by modules The registry now dynamically discovers modules from definition.py files instead of hardcoded imports. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,16 +1,19 @@
|
|||||||
# app/modules/registry.py
|
# app/modules/registry.py
|
||||||
"""
|
"""
|
||||||
Module registry defining all available platform modules.
|
Module registry - AUTO-DISCOVERED.
|
||||||
|
|
||||||
|
All modules are automatically discovered from app/modules/*/definition.py.
|
||||||
|
No manual imports needed - just create a module directory with definition.py.
|
||||||
|
|
||||||
The module system uses a three-tier classification:
|
The module system uses a three-tier classification:
|
||||||
|
|
||||||
1. CORE MODULES (4) - Always enabled, cannot be disabled
|
1. CORE MODULES - Always enabled, cannot be disabled (is_core=True)
|
||||||
- core: Dashboard, settings, profile
|
- core: Dashboard, settings, profile
|
||||||
- tenancy: Platform, company, vendor, admin user management
|
- tenancy: Platform, company, vendor, admin user management
|
||||||
- cms: Content pages, media library, themes
|
- cms: Content pages, media library, themes
|
||||||
- customers: Customer database, profiles, segmentation
|
- customers: Customer database, profiles, segmentation
|
||||||
|
|
||||||
2. OPTIONAL MODULES (7) - Can be enabled/disabled per platform
|
2. OPTIONAL MODULES - Can be enabled/disabled per platform (default)
|
||||||
- payments: Payment gateway integrations (Stripe, PayPal, etc.)
|
- payments: Payment gateway integrations (Stripe, PayPal, etc.)
|
||||||
- billing: Platform subscriptions, vendor invoices (requires: payments)
|
- billing: Platform subscriptions, vendor invoices (requires: payments)
|
||||||
- inventory: Stock management, locations
|
- inventory: Stock management, locations
|
||||||
@@ -19,142 +22,56 @@ The module system uses a three-tier classification:
|
|||||||
- analytics: Reports, dashboards
|
- analytics: Reports, dashboards
|
||||||
- messaging: Messages, notifications
|
- messaging: Messages, notifications
|
||||||
|
|
||||||
3. INTERNAL MODULES (2) - Admin-only tools, not customer-facing
|
3. INTERNAL MODULES - Admin-only tools, not customer-facing (is_internal=True)
|
||||||
- dev-tools: Component library, icons
|
- dev-tools: Component library, icons
|
||||||
- monitoring: Logs, background tasks, Flower link, Grafana dashboards
|
- monitoring: Logs, background tasks, Flower link, Grafana dashboards
|
||||||
|
|
||||||
Module Structure:
|
To add a new module:
|
||||||
- Inline modules: Defined directly in this file (core, tenancy)
|
1. Create app/modules/<code>/ directory
|
||||||
- Extracted modules: Imported from app/modules/{module}/ (billing, etc.)
|
2. Add definition.py with ModuleDefinition instance
|
||||||
|
3. Set is_core=True or is_internal=True for non-optional modules
|
||||||
As modules are extracted to their own directories, they are imported
|
4. That's it! Module will be auto-discovered.
|
||||||
here and their inline definitions are replaced.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
from app.modules.base import ModuleDefinition
|
from app.modules.base import ModuleDefinition
|
||||||
|
from app.modules.discovery import discover_modules, discover_modules_by_tier
|
||||||
from models.database.admin_menu_config import FrontendType
|
from models.database.admin_menu_config import FrontendType
|
||||||
|
|
||||||
# Import extracted modules
|
logger = logging.getLogger(__name__)
|
||||||
from app.modules.billing.definition import billing_module
|
|
||||||
from app.modules.payments.definition import payments_module
|
|
||||||
from app.modules.inventory.definition import inventory_module
|
|
||||||
from app.modules.marketplace.definition import marketplace_module
|
|
||||||
from app.modules.orders.definition import orders_module
|
|
||||||
from app.modules.customers.definition import customers_module
|
|
||||||
from app.modules.cms.definition import cms_module
|
|
||||||
from app.modules.analytics.definition import analytics_module
|
|
||||||
from app.modules.messaging.definition import messaging_module
|
|
||||||
from app.modules.dev_tools.definition import dev_tools_module
|
|
||||||
from app.modules.monitoring.definition import monitoring_module
|
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Core Modules (Always Enabled, Cannot Be Disabled)
|
# Auto-Discovered Module Registry
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
CORE_MODULES: dict[str, ModuleDefinition] = {
|
@lru_cache(maxsize=1)
|
||||||
"core": ModuleDefinition(
|
def _get_all_modules() -> dict[str, ModuleDefinition]:
|
||||||
code="core",
|
"""Get all modules (cached)."""
|
||||||
name="Core Platform",
|
return discover_modules()
|
||||||
description="Dashboard, settings, and profile management. Required for basic operation.",
|
|
||||||
is_core=True,
|
|
||||||
features=[
|
|
||||||
"dashboard",
|
|
||||||
"settings",
|
|
||||||
"profile",
|
|
||||||
],
|
|
||||||
menu_items={
|
|
||||||
FrontendType.ADMIN: [
|
|
||||||
"dashboard",
|
|
||||||
"settings",
|
|
||||||
"email-templates",
|
|
||||||
"my-menu",
|
|
||||||
],
|
|
||||||
FrontendType.VENDOR: [
|
|
||||||
"dashboard",
|
|
||||||
"profile",
|
|
||||||
"settings",
|
|
||||||
"email-templates",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
"tenancy": ModuleDefinition(
|
|
||||||
code="tenancy",
|
|
||||||
name="Tenancy Management",
|
|
||||||
description="Platform, company, vendor, and admin user management. Required for multi-tenant operation.",
|
|
||||||
is_core=True,
|
|
||||||
features=[
|
|
||||||
"platform_management",
|
|
||||||
"company_management",
|
|
||||||
"vendor_management",
|
|
||||||
"admin_user_management",
|
|
||||||
],
|
|
||||||
menu_items={
|
|
||||||
FrontendType.ADMIN: [
|
|
||||||
"platforms",
|
|
||||||
"companies",
|
|
||||||
"vendors",
|
|
||||||
"admin-users",
|
|
||||||
],
|
|
||||||
FrontendType.VENDOR: [
|
|
||||||
"team",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
# CMS module - imported from app/modules/cms/
|
|
||||||
"cms": cms_module,
|
|
||||||
# Customers module - imported from app/modules/customers/
|
|
||||||
"customers": customers_module,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
@lru_cache(maxsize=1)
|
||||||
# Optional Modules (Can Be Enabled/Disabled Per Platform)
|
def _get_modules_by_tier() -> dict[str, dict[str, ModuleDefinition]]:
|
||||||
# =============================================================================
|
"""Get modules organized by tier (cached)."""
|
||||||
|
return discover_modules_by_tier()
|
||||||
OPTIONAL_MODULES: dict[str, ModuleDefinition] = {
|
|
||||||
# Payments module - imported from app/modules/payments/
|
|
||||||
# Gateway integrations (Stripe, PayPal, etc.)
|
|
||||||
"payments": payments_module,
|
|
||||||
# Billing module - imported from app/modules/billing/
|
|
||||||
# Platform subscriptions, vendor invoices (requires: payments)
|
|
||||||
"billing": billing_module,
|
|
||||||
# Inventory module - imported from app/modules/inventory/
|
|
||||||
"inventory": inventory_module,
|
|
||||||
# Orders module - imported from app/modules/orders/
|
|
||||||
# Order management, customer checkout (requires: payments)
|
|
||||||
"orders": orders_module,
|
|
||||||
# Marketplace module - imported from app/modules/marketplace/
|
|
||||||
# Letzshop integration (requires: inventory)
|
|
||||||
"marketplace": marketplace_module,
|
|
||||||
# Analytics module - imported from app/modules/analytics/
|
|
||||||
"analytics": analytics_module,
|
|
||||||
# Messaging module - imported from app/modules/messaging/
|
|
||||||
"messaging": messaging_module,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
# Expose as module-level variables for backward compatibility
|
||||||
# Internal Modules (Admin-Only, Not Customer-Facing)
|
# These are computed lazily on first access
|
||||||
# =============================================================================
|
def __getattr__(name: str):
|
||||||
|
"""Lazy module-level attribute access for backward compatibility."""
|
||||||
INTERNAL_MODULES: dict[str, ModuleDefinition] = {
|
if name == "MODULES":
|
||||||
# Dev-Tools module - imported from app/modules/dev_tools/
|
return _get_all_modules()
|
||||||
"dev-tools": dev_tools_module,
|
elif name == "CORE_MODULES":
|
||||||
# Monitoring module - imported from app/modules/monitoring/
|
return _get_modules_by_tier()["core"]
|
||||||
"monitoring": monitoring_module,
|
elif name == "OPTIONAL_MODULES":
|
||||||
}
|
return _get_modules_by_tier()["optional"]
|
||||||
|
elif name == "INTERNAL_MODULES":
|
||||||
|
return _get_modules_by_tier()["internal"]
|
||||||
# =============================================================================
|
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||||||
# Combined Module Registry
|
|
||||||
# =============================================================================
|
|
||||||
|
|
||||||
MODULES: dict[str, ModuleDefinition] = {
|
|
||||||
**CORE_MODULES,
|
|
||||||
**OPTIONAL_MODULES,
|
|
||||||
**INTERNAL_MODULES,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@@ -164,52 +81,52 @@ MODULES: dict[str, ModuleDefinition] = {
|
|||||||
|
|
||||||
def get_module(code: str) -> ModuleDefinition | None:
|
def get_module(code: str) -> ModuleDefinition | None:
|
||||||
"""Get a module definition by code."""
|
"""Get a module definition by code."""
|
||||||
return MODULES.get(code)
|
return _get_all_modules().get(code)
|
||||||
|
|
||||||
|
|
||||||
def get_core_modules() -> list[ModuleDefinition]:
|
def get_core_modules() -> list[ModuleDefinition]:
|
||||||
"""Get all core modules (cannot be disabled)."""
|
"""Get all core modules (cannot be disabled)."""
|
||||||
return list(CORE_MODULES.values())
|
return list(_get_modules_by_tier()["core"].values())
|
||||||
|
|
||||||
|
|
||||||
def get_core_module_codes() -> set[str]:
|
def get_core_module_codes() -> set[str]:
|
||||||
"""Get codes of all core modules."""
|
"""Get codes of all core modules."""
|
||||||
return set(CORE_MODULES.keys())
|
return set(_get_modules_by_tier()["core"].keys())
|
||||||
|
|
||||||
|
|
||||||
def get_optional_modules() -> list[ModuleDefinition]:
|
def get_optional_modules() -> list[ModuleDefinition]:
|
||||||
"""Get all optional modules (can be enabled/disabled)."""
|
"""Get all optional modules (can be enabled/disabled)."""
|
||||||
return list(OPTIONAL_MODULES.values())
|
return list(_get_modules_by_tier()["optional"].values())
|
||||||
|
|
||||||
|
|
||||||
def get_optional_module_codes() -> set[str]:
|
def get_optional_module_codes() -> set[str]:
|
||||||
"""Get codes of all optional modules."""
|
"""Get codes of all optional modules."""
|
||||||
return set(OPTIONAL_MODULES.keys())
|
return set(_get_modules_by_tier()["optional"].keys())
|
||||||
|
|
||||||
|
|
||||||
def get_internal_modules() -> list[ModuleDefinition]:
|
def get_internal_modules() -> list[ModuleDefinition]:
|
||||||
"""Get all internal modules (admin-only tools)."""
|
"""Get all internal modules (admin-only tools)."""
|
||||||
return list(INTERNAL_MODULES.values())
|
return list(_get_modules_by_tier()["internal"].values())
|
||||||
|
|
||||||
|
|
||||||
def get_internal_module_codes() -> set[str]:
|
def get_internal_module_codes() -> set[str]:
|
||||||
"""Get codes of all internal modules."""
|
"""Get codes of all internal modules."""
|
||||||
return set(INTERNAL_MODULES.keys())
|
return set(_get_modules_by_tier()["internal"].keys())
|
||||||
|
|
||||||
|
|
||||||
def get_all_module_codes() -> set[str]:
|
def get_all_module_codes() -> set[str]:
|
||||||
"""Get all module codes."""
|
"""Get all module codes."""
|
||||||
return set(MODULES.keys())
|
return set(_get_all_modules().keys())
|
||||||
|
|
||||||
|
|
||||||
def is_core_module(code: str) -> bool:
|
def is_core_module(code: str) -> bool:
|
||||||
"""Check if a module is a core module."""
|
"""Check if a module is a core module."""
|
||||||
return code in CORE_MODULES
|
return code in _get_modules_by_tier()["core"]
|
||||||
|
|
||||||
|
|
||||||
def is_internal_module(code: str) -> bool:
|
def is_internal_module(code: str) -> bool:
|
||||||
"""Check if a module is an internal module."""
|
"""Check if a module is an internal module."""
|
||||||
return code in INTERNAL_MODULES
|
return code in _get_modules_by_tier()["internal"]
|
||||||
|
|
||||||
|
|
||||||
def get_menu_item_module(menu_item_id: str, frontend_type: FrontendType) -> str | None:
|
def get_menu_item_module(menu_item_id: str, frontend_type: FrontendType) -> str | None:
|
||||||
@@ -223,7 +140,7 @@ def get_menu_item_module(menu_item_id: str, frontend_type: FrontendType) -> str
|
|||||||
Returns:
|
Returns:
|
||||||
Module code if found, None otherwise
|
Module code if found, None otherwise
|
||||||
"""
|
"""
|
||||||
for module in MODULES.values():
|
for module in _get_all_modules().values():
|
||||||
if menu_item_id in module.get_menu_items(frontend_type):
|
if menu_item_id in module.get_menu_items(frontend_type):
|
||||||
return module.code
|
return module.code
|
||||||
return None
|
return None
|
||||||
@@ -239,7 +156,7 @@ def get_feature_module(feature_code: str) -> str | None:
|
|||||||
Returns:
|
Returns:
|
||||||
Module code if found, None otherwise
|
Module code if found, None otherwise
|
||||||
"""
|
"""
|
||||||
for module in MODULES.values():
|
for module in _get_all_modules().values():
|
||||||
if module.has_feature(feature_code):
|
if module.has_feature(feature_code):
|
||||||
return module.code
|
return module.code
|
||||||
return None
|
return None
|
||||||
@@ -254,15 +171,16 @@ def validate_module_dependencies() -> list[str]:
|
|||||||
"""
|
"""
|
||||||
errors = []
|
errors = []
|
||||||
all_codes = get_all_module_codes()
|
all_codes = get_all_module_codes()
|
||||||
|
core_codes = get_core_module_codes()
|
||||||
|
|
||||||
for module in MODULES.values():
|
for module in _get_all_modules().values():
|
||||||
for required in module.requires:
|
for required in module.requires:
|
||||||
if required not in all_codes:
|
if required not in all_codes:
|
||||||
errors.append(
|
errors.append(
|
||||||
f"Module '{module.code}' requires unknown module '{required}'"
|
f"Module '{module.code}' requires unknown module '{required}'"
|
||||||
)
|
)
|
||||||
# Core modules should not depend on optional modules
|
# Core modules should not depend on optional modules
|
||||||
if module.is_core and required not in get_core_module_codes():
|
if module.is_core and required not in core_codes:
|
||||||
errors.append(
|
errors.append(
|
||||||
f"Core module '{module.code}' depends on optional module '{required}'"
|
f"Core module '{module.code}' depends on optional module '{required}'"
|
||||||
)
|
)
|
||||||
@@ -277,10 +195,11 @@ def get_modules_by_tier() -> dict[str, list[ModuleDefinition]]:
|
|||||||
Returns:
|
Returns:
|
||||||
Dict with keys 'core', 'optional', 'internal' mapping to module lists
|
Dict with keys 'core', 'optional', 'internal' mapping to module lists
|
||||||
"""
|
"""
|
||||||
|
by_tier = _get_modules_by_tier()
|
||||||
return {
|
return {
|
||||||
"core": list(CORE_MODULES.values()),
|
"core": list(by_tier["core"].values()),
|
||||||
"optional": list(OPTIONAL_MODULES.values()),
|
"optional": list(by_tier["optional"].values()),
|
||||||
"internal": list(INTERNAL_MODULES.values()),
|
"internal": list(by_tier["internal"].values()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -294,26 +213,41 @@ def get_module_tier(code: str) -> str | None:
|
|||||||
Returns:
|
Returns:
|
||||||
'core', 'optional', 'internal', or None if not found
|
'core', 'optional', 'internal', or None if not found
|
||||||
"""
|
"""
|
||||||
if code in CORE_MODULES:
|
by_tier = _get_modules_by_tier()
|
||||||
|
if code in by_tier["core"]:
|
||||||
return "core"
|
return "core"
|
||||||
elif code in OPTIONAL_MODULES:
|
elif code in by_tier["optional"]:
|
||||||
return "optional"
|
return "optional"
|
||||||
elif code in INTERNAL_MODULES:
|
elif code in by_tier["internal"]:
|
||||||
return "internal"
|
return "internal"
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
# Validate dependencies on import (development check)
|
def clear_registry_cache():
|
||||||
_validation_errors = validate_module_dependencies()
|
"""Clear the module registry cache. Useful for testing."""
|
||||||
if _validation_errors:
|
_get_all_modules.cache_clear()
|
||||||
import warnings
|
_get_modules_by_tier.cache_clear()
|
||||||
|
|
||||||
for error in _validation_errors:
|
|
||||||
warnings.warn(f"Module registry validation: {error}", stacklevel=2)
|
# =============================================================================
|
||||||
|
# Validation on Import (Development Check)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
def _run_validation():
|
||||||
|
"""Run validation checks on module registry."""
|
||||||
|
_validation_errors = validate_module_dependencies()
|
||||||
|
if _validation_errors:
|
||||||
|
import warnings
|
||||||
|
for error in _validation_errors:
|
||||||
|
warnings.warn(f"Module registry validation: {error}", stacklevel=2)
|
||||||
|
|
||||||
|
|
||||||
|
# Run validation on import (can be disabled in production)
|
||||||
|
_run_validation()
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
# Module dictionaries
|
# Module dictionaries (lazy-loaded)
|
||||||
"MODULES",
|
"MODULES",
|
||||||
"CORE_MODULES",
|
"CORE_MODULES",
|
||||||
"OPTIONAL_MODULES",
|
"OPTIONAL_MODULES",
|
||||||
@@ -337,4 +271,6 @@ __all__ = [
|
|||||||
"get_feature_module",
|
"get_feature_module",
|
||||||
# Validation
|
# Validation
|
||||||
"validate_module_dependencies",
|
"validate_module_dependencies",
|
||||||
|
# Cache management
|
||||||
|
"clear_registry_cache",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -673,25 +673,10 @@ async def vendor_notifications_page(
|
|||||||
# ============================================================================
|
# ============================================================================
|
||||||
# ANALYTICS
|
# ANALYTICS
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
# NOTE: Analytics routes moved to self-contained module: app.modules.analytics.routes.pages.vendor
|
||||||
|
# Routes are registered directly in main.py from the Analytics module
|
||||||
@router.get(
|
# This includes:
|
||||||
"/{vendor_code}/analytics", response_class=HTMLResponse, include_in_schema=False
|
# - /{vendor_code}/analytics (dashboard)
|
||||||
)
|
|
||||||
async def vendor_analytics_page(
|
|
||||||
request: Request,
|
|
||||||
vendor_code: str = Path(..., description="Vendor code"),
|
|
||||||
current_user: User = Depends(get_current_vendor_from_cookie_or_header),
|
|
||||||
db: Session = Depends(get_db),
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Render analytics and reports page.
|
|
||||||
JavaScript loads analytics data via API.
|
|
||||||
"""
|
|
||||||
return templates.TemplateResponse(
|
|
||||||
"vendor/analytics.html",
|
|
||||||
get_vendor_context(request, db, current_user, vendor_code),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|||||||
40
main.py
40
main.py
@@ -61,12 +61,11 @@ from app.core.lifespan import lifespan
|
|||||||
from app.exceptions import ServiceUnavailableException
|
from app.exceptions import ServiceUnavailableException
|
||||||
from app.exceptions.handler import setup_exception_handlers
|
from app.exceptions.handler import setup_exception_handlers
|
||||||
|
|
||||||
# Import page routers
|
# Import page routers (legacy routes - will be migrated to modules)
|
||||||
from app.routes import admin_pages, platform_pages, shop_pages, vendor_pages
|
from app.routes import admin_pages, platform_pages, shop_pages, vendor_pages
|
||||||
|
|
||||||
# Import CMS module page routers (self-contained module)
|
# Module route auto-discovery
|
||||||
from app.modules.cms.routes.pages import admin_router as cms_admin_pages
|
from app.modules.routes import discover_module_routes, get_vendor_page_routes
|
||||||
from app.modules.cms.routes.pages import vendor_router as cms_vendor_pages
|
|
||||||
from app.utils.i18n import get_jinja2_globals
|
from app.utils.i18n import get_jinja2_globals
|
||||||
from middleware.context import ContextMiddleware
|
from middleware.context import ContextMiddleware
|
||||||
from middleware.language import LanguageMiddleware
|
from middleware.language import LanguageMiddleware
|
||||||
@@ -330,6 +329,7 @@ app.include_router(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Vendor management pages (dashboard, products, orders, etc.)
|
# Vendor management pages (dashboard, products, orders, etc.)
|
||||||
|
# NOTE: Legacy routes - modules with their own routes will override these
|
||||||
logger.info("Registering vendor page routes: /vendor/{code}/*")
|
logger.info("Registering vendor page routes: /vendor/{code}/*")
|
||||||
app.include_router(
|
app.include_router(
|
||||||
vendor_pages.router,
|
vendor_pages.router,
|
||||||
@@ -338,12 +338,32 @@ app.include_router(
|
|||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
# CMS module vendor pages (self-contained module)
|
# =============================================================================
|
||||||
# NOTE: Includes catch-all /{vendor_code}/{slug} - must be registered AFTER vendor_pages
|
# AUTO-DISCOVERED MODULE ROUTES
|
||||||
logger.info("Registering CMS vendor page routes: /vendor/{code}/content-pages/*")
|
# =============================================================================
|
||||||
app.include_router(
|
# Self-contained modules register their routes automatically.
|
||||||
cms_vendor_pages, prefix="/vendor", tags=["cms-vendor-pages"], include_in_schema=False
|
# Routes are discovered from app/modules/*/routes/pages/ and routes/api/
|
||||||
)
|
# NOTE: CMS has catch-all route, so it's registered last via priority sorting
|
||||||
|
|
||||||
|
logger.info("Auto-discovering module page routes...")
|
||||||
|
vendor_page_routes = get_vendor_page_routes()
|
||||||
|
|
||||||
|
# Sort routes: CMS last (has catch-all), others alphabetically
|
||||||
|
def route_priority(route):
|
||||||
|
if route.module_code == "cms":
|
||||||
|
return (1, route.module_code) # CMS last
|
||||||
|
return (0, route.module_code)
|
||||||
|
|
||||||
|
vendor_page_routes.sort(key=route_priority)
|
||||||
|
|
||||||
|
for route_info in vendor_page_routes:
|
||||||
|
logger.info(f" Registering {route_info.module_code} vendor pages: {route_info.prefix}")
|
||||||
|
app.include_router(
|
||||||
|
route_info.router,
|
||||||
|
prefix=route_info.prefix,
|
||||||
|
tags=route_info.tags,
|
||||||
|
include_in_schema=route_info.include_in_schema,
|
||||||
|
)
|
||||||
|
|
||||||
# Customer shop pages - Register at TWO prefixes:
|
# Customer shop pages - Register at TWO prefixes:
|
||||||
# 1. /shop/* (for subdomain/custom domain modes)
|
# 1. /shop/* (for subdomain/custom domain modes)
|
||||||
|
|||||||
Reference in New Issue
Block a user