feat: complete CMS as fully autonomous self-contained module
Transform CMS from a thin wrapper into a fully self-contained module with all code living within app/modules/cms/: Module Structure: - models/: ContentPage model (canonical location with dynamic discovery) - schemas/: Pydantic schemas for API validation - services/: ContentPageService business logic - exceptions/: Module-specific exceptions - routes/api/: REST API endpoints (admin, vendor, shop) - routes/pages/: HTML page routes (admin, vendor) - templates/cms/: Jinja2 templates (namespaced) - static/: JavaScript files (admin/vendor) - locales/: i18n translations (en, fr, de, lb) Key Changes: - Move ContentPage model to module with dynamic model discovery - Create Pydantic schemas package for request/response validation - Extract API routes from app/api/v1/*/ to module - Extract page routes from admin_pages.py/vendor_pages.py to module - Move static JS files to module with dedicated mount point - Update templates to use cms_static for module assets - Add module static file mounting in main.py - Delete old scattered files (no shims - hard errors on old imports) This establishes the pattern for migrating other modules to be fully autonomous and independently deployable. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,27 @@
|
||||
# models/database/__init__.py
|
||||
"""Database models package."""
|
||||
"""
|
||||
Database models package.
|
||||
|
||||
This package imports all SQLAlchemy models to ensure they are registered
|
||||
with Base.metadata. This includes:
|
||||
1. Core models (defined in this directory)
|
||||
2. Module models (discovered from app/modules/<module>/models/)
|
||||
|
||||
Module Model Discovery:
|
||||
- Modules can define their own models in app/modules/<module>/models/
|
||||
- These are automatically imported when this package loads
|
||||
- Module models must use `from app.core.database import Base`
|
||||
"""
|
||||
|
||||
import importlib
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# ============================================================================
|
||||
# CORE MODELS (always loaded)
|
||||
# ============================================================================
|
||||
|
||||
from .admin import (
|
||||
AdminAuditLog,
|
||||
@@ -18,7 +40,6 @@ from .architecture_scan import (
|
||||
)
|
||||
from .base import Base
|
||||
from .company import Company
|
||||
from .content_page import ContentPage
|
||||
from .platform import Platform
|
||||
from .platform_module import PlatformModule
|
||||
from .vendor_platform import VendorPlatform
|
||||
@@ -82,6 +103,49 @@ from .vendor import Role, Vendor, VendorUser
|
||||
from .vendor_domain import VendorDomain
|
||||
from .vendor_theme import VendorTheme
|
||||
|
||||
# ============================================================================
|
||||
# MODULE MODELS (dynamically discovered)
|
||||
# ============================================================================
|
||||
|
||||
|
||||
def _discover_module_models():
|
||||
"""
|
||||
Discover and import models from app/modules/<module>/models/ directories.
|
||||
|
||||
This ensures module models are registered with Base.metadata for:
|
||||
1. Alembic migrations
|
||||
2. SQLAlchemy queries
|
||||
|
||||
Module models must:
|
||||
- Be in app/modules/<module>/models/__init__.py or individual files
|
||||
- Import Base from app.core.database
|
||||
"""
|
||||
modules_dir = Path(__file__).parent.parent.parent / "app" / "modules"
|
||||
|
||||
if not modules_dir.exists():
|
||||
return
|
||||
|
||||
for module_dir in sorted(modules_dir.iterdir()):
|
||||
if not module_dir.is_dir():
|
||||
continue
|
||||
|
||||
models_init = module_dir / "models" / "__init__.py"
|
||||
if models_init.exists():
|
||||
module_name = f"app.modules.{module_dir.name}.models"
|
||||
try:
|
||||
importlib.import_module(module_name)
|
||||
logger.debug(f"[Models] Loaded module models: {module_name}")
|
||||
except ImportError as e:
|
||||
logger.warning(f"[Models] Failed to import {module_name}: {e}")
|
||||
|
||||
|
||||
# Run discovery at import time
|
||||
_discover_module_models()
|
||||
|
||||
# ============================================================================
|
||||
# EXPORTS
|
||||
# ============================================================================
|
||||
|
||||
__all__ = [
|
||||
# Admin-specific models
|
||||
"AdminAuditLog",
|
||||
@@ -113,8 +177,6 @@ __all__ = [
|
||||
"Role",
|
||||
"VendorDomain",
|
||||
"VendorTheme",
|
||||
# Content
|
||||
"ContentPage",
|
||||
# Platform
|
||||
"Platform",
|
||||
"PlatformModule",
|
||||
|
||||
Reference in New Issue
Block a user