feat: consolidate media service, add merchant users page, fix metrics overlap
- Merge ImageService into MediaService with WebP variant generation, DB-backed storage stats, and module-driven media usage discovery via new MediaUsageProviderProtocol - Add merchant users admin page with scoped user listing, stats endpoint, template, JS, and i18n strings (de/en/fr/lb) - Fix merchant user metrics so Owners and Team Members are mutually exclusive (filter team_members on user_type="member" and exclude owner IDs) ensuring stat cards add up correctly - Update billing and monitoring services to use media_service - Update subscription-billing and feature-gating docs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -43,20 +43,17 @@ from app.modules.cms.schemas.media import (
|
||||
MediaListResponse,
|
||||
MediaMetadataUpdate,
|
||||
MediaUploadResponse,
|
||||
MediaUsageItem,
|
||||
MediaUsageResponse,
|
||||
MessageResponse,
|
||||
MultipleUploadResponse,
|
||||
OptimizationResultResponse,
|
||||
ProductUsageInfo,
|
||||
UploadedFileInfo,
|
||||
)
|
||||
|
||||
# Image schemas
|
||||
from app.modules.cms.schemas.image import (
|
||||
ImageDeleteResponse,
|
||||
ImageStorageStats,
|
||||
ImageUploadResponse,
|
||||
ImageUrls,
|
||||
)
|
||||
|
||||
# Theme schemas
|
||||
@@ -109,13 +106,10 @@ __all__ = [
|
||||
"MessageResponse",
|
||||
"MultipleUploadResponse",
|
||||
"OptimizationResultResponse",
|
||||
"ProductUsageInfo",
|
||||
"MediaUsageItem",
|
||||
"UploadedFileInfo",
|
||||
# Image
|
||||
"ImageDeleteResponse",
|
||||
"ImageStorageStats",
|
||||
"ImageUploadResponse",
|
||||
"ImageUrls",
|
||||
# Theme
|
||||
"ThemeDeleteResponse",
|
||||
"ThemePresetListResponse",
|
||||
|
||||
@@ -6,33 +6,6 @@ Pydantic schemas for image operations.
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ImageUrls(BaseModel):
|
||||
"""URLs for image variants."""
|
||||
|
||||
original: str
|
||||
medium: str | None = None # 800px variant
|
||||
thumb: str | None = None # 200px variant
|
||||
|
||||
# Allow arbitrary keys for flexibility
|
||||
class Config:
|
||||
extra = "allow"
|
||||
|
||||
|
||||
class ImageUploadResponse(BaseModel):
|
||||
"""Response from image upload."""
|
||||
|
||||
success: bool
|
||||
image: dict | None = None
|
||||
error: str | None = None
|
||||
|
||||
|
||||
class ImageDeleteResponse(BaseModel):
|
||||
"""Response from image deletion."""
|
||||
|
||||
success: bool
|
||||
message: str
|
||||
|
||||
|
||||
class ImageStorageStats(BaseModel):
|
||||
"""Image storage statistics."""
|
||||
|
||||
|
||||
@@ -164,22 +164,21 @@ class MediaMetadataUpdate(BaseModel):
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class ProductUsageInfo(BaseModel):
|
||||
"""Information about product using this media."""
|
||||
class MediaUsageItem(BaseModel):
|
||||
"""Information about an entity using this media."""
|
||||
|
||||
product_id: int
|
||||
product_name: str
|
||||
usage_type: str # main_image, gallery, variant, etc.
|
||||
entity_type: str # Defined by provider (e.g. "product")
|
||||
entity_id: int
|
||||
entity_name: str
|
||||
usage_type: str # Defined by provider (e.g. "main_image", "gallery")
|
||||
|
||||
|
||||
class MediaUsageResponse(BaseModel):
|
||||
"""Response showing where media is being used."""
|
||||
|
||||
media_id: int | None = None
|
||||
products: list[ProductUsageInfo] = []
|
||||
other_usage: list[dict[str, Any]] = []
|
||||
usage: list[MediaUsageItem] = []
|
||||
total_usage_count: int = 0
|
||||
message: str | None = None
|
||||
|
||||
|
||||
# ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user