Some checks failed
Move all auth schemas (UserContext, UserLogin, LoginResponse, etc.) from legacy models/schema/auth.py to app/modules/tenancy/schemas/auth.py per MOD-019. Update 84 import sites across 14 modules. Legacy file now re-exports for backwards compatibility. Add missing tenancy service methods for cross-module consumers: - merchant_service.get_merchant_by_owner_id() - merchant_service.get_merchant_count_for_owner() - admin_service.get_user_by_id() (public, was private-only) - platform_service.get_active_store_count() Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
225 lines
7.4 KiB
Python
225 lines
7.4 KiB
Python
# app/modules/tenancy/routes/api/admin_platforms.py
|
|
"""
|
|
Admin API endpoints for Platform management (Multi-Platform CMS).
|
|
|
|
Provides CRUD operations for platforms:
|
|
- GET /platforms - List all platforms
|
|
- GET /platforms/{code} - Get platform details
|
|
- PUT /platforms/{code} - Update platform settings
|
|
- GET /platforms/{code}/stats - Get platform statistics
|
|
|
|
Platforms are business offerings (OMS, Loyalty, Site Builder) with their own:
|
|
- Marketing pages (homepage, pricing, features)
|
|
- Store defaults (about, terms, privacy)
|
|
- Configuration and branding
|
|
"""
|
|
|
|
import logging
|
|
from typing import Any
|
|
|
|
from fastapi import APIRouter, Depends, Path, Query
|
|
from pydantic import BaseModel, Field
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.api.deps import get_current_admin_from_cookie_or_header, get_db
|
|
from app.modules.tenancy.schemas.auth import UserContext
|
|
from app.modules.tenancy.services.platform_service import platform_service
|
|
|
|
logger = logging.getLogger(__name__)
|
|
admin_platforms_router = APIRouter(prefix="/platforms")
|
|
|
|
|
|
# =============================================================================
|
|
# Pydantic Schemas
|
|
# =============================================================================
|
|
|
|
|
|
class PlatformResponse(BaseModel):
|
|
"""Platform response schema."""
|
|
|
|
id: int
|
|
code: str
|
|
name: str
|
|
description: str | None = None
|
|
domain: str | None = None
|
|
path_prefix: str | None = None
|
|
logo: str | None = None
|
|
logo_dark: str | None = None
|
|
favicon: str | None = None
|
|
theme_config: dict[str, Any] = Field(default_factory=dict)
|
|
default_language: str = "fr"
|
|
supported_languages: list[str] = Field(default_factory=lambda: ["fr", "de", "en"])
|
|
is_active: bool = True
|
|
is_public: bool = True
|
|
settings: dict[str, Any] = Field(default_factory=dict)
|
|
created_at: str
|
|
updated_at: str
|
|
|
|
# Computed fields (added by endpoint)
|
|
store_count: int = 0
|
|
platform_pages_count: int = 0
|
|
store_defaults_count: int = 0
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class PlatformListResponse(BaseModel):
|
|
"""Response for platform list."""
|
|
|
|
platforms: list[PlatformResponse]
|
|
total: int
|
|
|
|
|
|
class PlatformUpdateRequest(BaseModel):
|
|
"""Request schema for updating a platform."""
|
|
|
|
name: str | None = None
|
|
description: str | None = None
|
|
domain: str | None = None
|
|
path_prefix: str | None = None
|
|
logo: str | None = None
|
|
logo_dark: str | None = None
|
|
favicon: str | None = None
|
|
theme_config: dict[str, Any] | None = None
|
|
default_language: str | None = None
|
|
supported_languages: list[str] | None = None
|
|
is_active: bool | None = None
|
|
is_public: bool | None = None
|
|
settings: dict[str, Any] | None = None
|
|
|
|
|
|
class PlatformStatsResponse(BaseModel):
|
|
"""Platform statistics response."""
|
|
|
|
platform_id: int
|
|
platform_code: str
|
|
platform_name: str
|
|
store_count: int
|
|
platform_pages_count: int
|
|
store_defaults_count: int
|
|
store_overrides_count: int
|
|
published_pages_count: int
|
|
draft_pages_count: int
|
|
|
|
|
|
# =============================================================================
|
|
# Helper Functions
|
|
# =============================================================================
|
|
|
|
|
|
def _build_platform_response(db: Session, platform) -> PlatformResponse:
|
|
"""Build PlatformResponse from Platform model with computed fields."""
|
|
return PlatformResponse(
|
|
id=platform.id,
|
|
code=platform.code,
|
|
name=platform.name,
|
|
description=platform.description,
|
|
domain=platform.domain,
|
|
path_prefix=platform.path_prefix,
|
|
logo=platform.logo,
|
|
logo_dark=platform.logo_dark,
|
|
favicon=platform.favicon,
|
|
theme_config=platform.theme_config or {},
|
|
default_language=platform.default_language,
|
|
supported_languages=platform.supported_languages or ["fr", "de", "en"],
|
|
is_active=platform.is_active,
|
|
is_public=platform.is_public,
|
|
settings=platform.settings or {},
|
|
created_at=platform.created_at.isoformat(),
|
|
updated_at=platform.updated_at.isoformat(),
|
|
store_count=platform_service.get_store_count(db, platform.id),
|
|
platform_pages_count=platform_service.get_platform_pages_count(db, platform.id),
|
|
store_defaults_count=platform_service.get_store_defaults_count(db, platform.id),
|
|
)
|
|
|
|
|
|
# =============================================================================
|
|
# API Endpoints
|
|
# =============================================================================
|
|
|
|
|
|
@admin_platforms_router.get("", response_model=PlatformListResponse)
|
|
async def list_platforms(
|
|
db: Session = Depends(get_db),
|
|
current_user: UserContext = Depends(get_current_admin_from_cookie_or_header),
|
|
include_inactive: bool = Query(False, description="Include inactive platforms"),
|
|
):
|
|
"""
|
|
List all platforms with their statistics.
|
|
|
|
Returns all platforms (OMS, Loyalty, etc.) with store counts and page counts.
|
|
"""
|
|
platforms = platform_service.list_platforms(db, include_inactive=include_inactive)
|
|
|
|
result = [_build_platform_response(db, platform) for platform in platforms]
|
|
|
|
logger.info(f"[PLATFORMS] Listed {len(result)} platforms")
|
|
|
|
return PlatformListResponse(platforms=result, total=len(result))
|
|
|
|
|
|
@admin_platforms_router.get("/{code}", response_model=PlatformResponse)
|
|
async def get_platform(
|
|
code: str = Path(..., description="Platform code (oms, loyalty, etc.)"),
|
|
db: Session = Depends(get_db),
|
|
current_user: UserContext = Depends(get_current_admin_from_cookie_or_header),
|
|
):
|
|
"""
|
|
Get platform details by code.
|
|
|
|
Returns full platform configuration including statistics.
|
|
"""
|
|
platform = platform_service.get_platform_by_code(db, code)
|
|
return _build_platform_response(db, platform)
|
|
|
|
|
|
@admin_platforms_router.put("/{code}", response_model=PlatformResponse)
|
|
async def update_platform(
|
|
update_data: PlatformUpdateRequest,
|
|
code: str = Path(..., description="Platform code"),
|
|
db: Session = Depends(get_db),
|
|
current_user: UserContext = Depends(get_current_admin_from_cookie_or_header),
|
|
):
|
|
"""
|
|
Update platform settings.
|
|
|
|
Allows updating name, description, branding, and configuration.
|
|
"""
|
|
platform = platform_service.get_platform_by_code(db, code)
|
|
|
|
update_dict = update_data.model_dump(exclude_unset=True)
|
|
platform = platform_service.update_platform(db, platform, update_dict)
|
|
|
|
db.commit()
|
|
db.refresh(platform)
|
|
|
|
return _build_platform_response(db, platform)
|
|
|
|
|
|
@admin_platforms_router.get("/{code}/stats", response_model=PlatformStatsResponse)
|
|
async def get_platform_stats(
|
|
code: str = Path(..., description="Platform code"),
|
|
db: Session = Depends(get_db),
|
|
current_user: UserContext = Depends(get_current_admin_from_cookie_or_header),
|
|
):
|
|
"""
|
|
Get detailed statistics for a platform.
|
|
|
|
Returns counts for stores, pages, and content breakdown.
|
|
"""
|
|
platform = platform_service.get_platform_by_code(db, code)
|
|
stats = platform_service.get_platform_stats(db, platform)
|
|
|
|
return PlatformStatsResponse(
|
|
platform_id=stats.platform_id,
|
|
platform_code=stats.platform_code,
|
|
platform_name=stats.platform_name,
|
|
store_count=stats.store_count,
|
|
platform_pages_count=stats.platform_pages_count,
|
|
store_defaults_count=stats.store_defaults_count,
|
|
store_overrides_count=stats.store_overrides_count,
|
|
published_pages_count=stats.published_pages_count,
|
|
draft_pages_count=stats.draft_pages_count,
|
|
)
|