Legacy service and task files now re-export from module locations:
app/services/:
- stats_service.py -> app.modules.analytics.services
- usage_service.py -> app.modules.analytics.services
app/tasks/celery_tasks/:
- code_quality.py -> app.modules.dev_tools.tasks
- test_runner.py -> app.modules.dev_tools.tasks
Maintains backwards compatibility while actual code lives
in self-contained modules.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- 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>
Add self-contained configuration and migrations support for modules:
Config auto-discovery (app/modules/config.py):
- Modules can have config.py with Pydantic Settings
- Environment variables prefixed with MODULE_NAME_
- Auto-discovered via get_module_config()
Migrations auto-discovery:
- Each module has migrations/versions/ directory
- Alembic discovers module migrations automatically
- Naming convention: {module}_{seq}_{description}.py
New architecture rules (MOD-013 to MOD-015):
- MOD-013: config.py should export config/config_class
- MOD-014: Migrations must follow naming convention
- MOD-015: Migrations directory must have __init__.py
Created for all 11 self-contained modules:
- config.py placeholder files
- migrations/ directories with __init__.py files
Added core and tenancy module definitions for completeness.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Phase 4 of module migration plan:
- Add ScheduledTask dataclass for declaring Celery Beat tasks
- Add tasks_path and scheduled_tasks fields to ModuleDefinition
- Create ModuleTask base class with database session management
- Create task discovery utilities (discover_module_tasks, build_beat_schedule)
- Update celery_config.py to discover and register module tasks
- Maintain backward compatibility with legacy task modules
Modules can now define tasks in their tasks/ directory and scheduled
tasks in their definition. The infrastructure supports gradual migration
of existing tasks from app/tasks/ to their respective modules.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add observability framework documentation (health checks, metrics, Sentry)
- Add developer guide for creating modules
- Add comprehensive module migration plan with Celery task integration
- Update architecture overview with module system and observability sections
- Update module-system.md with links to new docs
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Renamed duplicate revision zc2m3n4o5p6q7 to ze4o5p6q7r8s9
- Fixed JSON vs JSONB operator compatibility in migrations
- Simplified JSON settings update to use text replacement
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add AdminMenuConfig model for per-platform menu customization
- Add menu registry for centralized menu configuration
- Add my-menu-config and platform-menu-config admin pages
- Update sidebar with improved layout and Alpine.js interactions
- Add FrontendType enum for admin/vendor menu separation
- Document self-contained module patterns in session note
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
BadRequestException doesn't exist in app.exceptions. Use
ValidationException for module code validation errors.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix FastAPI static mount order: module statics must come BEFORE main
/static mount, otherwise the more general mount catches all requests
- Update validate_architecture.py to check self-contained module paths:
- JS files in app/modules/*/static/{admin,vendor}/js/
- Templates in app/modules/*/templates/*/{admin,vendor}/
- TPL-010 now finds module JS files for Alpine variable checking
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add dedicated pages for viewing module details and configuring module
settings. Includes routes, templates, and JS components for module
info page showing features, menu items, dependencies, and self-contained
module paths. Also adds View/Configure navigation buttons to the platform
modules list.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>
Phase 1 - Foundation:
- Add app/modules/contracts/ with Protocol definitions for cross-module
communication (ServiceProtocol, ContentServiceProtocol, MediaServiceProtocol)
- Enhance app/modules/base.py ModuleDefinition with self-contained module
support (is_self_contained, services_path, models_path, etc.)
- Update app/templates_config.py with multi-directory template loading
using Jinja2 ChoiceLoader for module templates
Phase 2 - CMS Pilot Module:
- Migrate CMS service to app/modules/cms/services/content_page_service.py
- Create app/modules/cms/exceptions.py with CMS-specific exceptions
- Configure app/modules/cms/models/ to re-export ContentPage from canonical
location (models.database) to avoid circular imports
- Update cms_module definition with is_self_contained=True and paths
- Add backwards compatibility shims with deprecation warnings:
- app/services/content_page_service.py -> app.modules.cms.services
- app/exceptions/content_page.py -> app.modules.cms.exceptions
Note: SQLAlchemy models remain in models/database/ as the canonical location
to avoid circular imports at startup time. Module model packages re-export
from the canonical location.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Documents the complete implementation of Phases 1-5:
- Module foundation and registry
- Menu integration and route protection
- Billing, inventory, orders, marketplace module extractions
- Admin UI for module management
- Pending optional next steps
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Added a complete Admin UI for managing platform modules:
API endpoints (app/api/v1/admin/modules.py):
- GET /modules - List all available modules
- GET /modules/platforms/{id} - Get platform modules
- PUT /modules/platforms/{id} - Update enabled modules
- POST /modules/platforms/{id}/enable - Enable a module
- POST /modules/platforms/{id}/disable - Disable a module
- POST /modules/platforms/{id}/enable-all - Enable all
- POST /modules/platforms/{id}/disable-optional - Core only
Admin UI:
- New page route: /admin/platforms/{code}/modules
- Template: platform-modules.html
- JavaScript: platform-modules.js
- Link added to platform-detail.html Super Admin section
Features:
- Toggle modules on/off with dependency resolution
- Enable all / Core only bulk actions
- Visual dependency indicators
- Separate sections for core vs optional modules
- Feature list preview per module
Also includes require_menu_access updates to page routes from Phase 2.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extract three additional modules following the billing module pattern:
Inventory Module (app/modules/inventory/):
- Stock management and tracking
- Inventory locations
- Low stock alerts
- Admin and vendor routes with module access control
Orders Module (app/modules/orders/):
- Order management and fulfillment
- Order item exceptions
- Bulk operations and export
- Admin and vendor routes with module access control
Marketplace Module (app/modules/marketplace/):
- Letzshop integration
- Product sync
- Marketplace import
- Depends on inventory module
- Admin and vendor routes with module access control
Admin router updated:
- Uses module routers with require_module_access dependency
- Legacy router includes commented out
- Routes verified: 15 inventory, 16 orders, 42 marketplace
All 31 module tests passing.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The last_login field was missing from the API response, causing it
to always show empty on the admin user detail page.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The API endpoint has a max limit of 500 but the stats query was
requesting 1000, causing a 422 validation error.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use inline template literals with {% raw %}{% endraw %} and HTML entities
(") for quotes, consistent with other copy code buttons on the page.
Removed the codeSnippets JS variables.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Store code snippets as JS variables in components.js and reference them
in the template. This avoids multi-line template literals in HTML
attributes while still providing copy functionality.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The last_login field was never being updated. Now it gets set to the
current UTC timestamp when a user successfully logs in.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Multi-line template literals in HTML attributes cause Alpine.js parsing
errors. Replaced @click="copyCode(...)" with static <pre><code> blocks.
Also simplified the x-text expression to use template literals.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The helper function was building the response manually but was missing
the new timestamp fields added to the schema.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Added {% raw %}{% endraw %} tags around the confirm_modal code snippets
to prevent Jinja2 from interpreting the {{ }} as template tags.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The admin user detail page was showing empty dates because the API
response schema was missing the created_at and updated_at fields
that exist on the User model via TimestampMixin.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Added documentation and interactive examples for both confirm_modal
and confirm_modal_dynamic macros, showing danger, warning, info variants
and dynamic message capabilities.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Added a new macro that accepts an Alpine.js expression for the message,
allowing runtime data to be included in confirmation dialogs. Updated
admin-user-edit.html to use this macro for platform removal confirmation.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Changed from static confirm_modal macro to custom modal with Alpine.js
binding to display the platform name dynamically in the confirmation
message.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replaced the native browser confirm() dialog with the styled confirm_modal
macro for a consistent UI experience when removing platform assignments
from admin users.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The admin user edit page now uses a dedicated template and JS file
that properly matches the /admin/admin-users/{id}/edit URL pattern.
The page allows super admins to:
- Toggle super admin status
- Toggle active status
- Manage platform assignments for platform admins
- Delete admin users
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add /admin/admin-users routes for managing admin users (super admin only)
- Remove vendor role from user creation form (vendors created via company hierarchy)
- Add admin-users.html and admin-user-detail.html templates
- Add admin-users.js and admin-user-detail.js for frontend logic
- Move database operations to admin_platform_service (list, get, create, delete, toggle status)
- Update sidebar to show Admin Users section only for super admins
- Add isSuperAdmin computed property to init-alpine.js
- Fix /api/v1 prefix issues in JS files (apiClient already adds prefix)
- Update architecture rule JS-012 to catch more variable patterns (url, endpoint, path)
- Replace inline SVGs with $icon() helper in select-platform.html
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Unit tests (test_admin_service.py):
- Vendor creation without platforms
- Vendor creation with single platform
- Vendor creation with multiple platforms
- Vendor creation with invalid platform ID (ignored)
- Vendor creation with duplicate code/subdomain (fails)
- Vendor creation with invalid company ID (fails)
Integration tests (test_vendors.py):
- Create vendor via API without platforms
- Create vendor via API with platforms
- Create vendor with duplicate code fails (409)
- Non-admin cannot create vendors (403)
Auth tests (test_auth.py):
- Super admin login includes is_super_admin=true
- Platform admin login includes is_super_admin=false
- Get current super admin info includes is_super_admin
Total: 69 admin platform tests passing
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
User create page:
- When role=admin, show super admin toggle
- If not super admin, show platform multi-select
- Admin users created via /api/v1/admin/admin-users endpoint
- Vendor users created via existing /admin/users endpoint
Vendor create page:
- Add platform selection section
- Vendors can be assigned to multiple platforms on creation
- Update VendorCreate schema to accept platform_ids
- Update AdminService.create_vendor() to create VendorPlatform records
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>