Files
moreminimore-marketing/backend/utils/asset_tracker.py
Kunthawat Greethong c35fa52117 Base code
2026-01-08 22:39:53 +07:00

159 lines
5.3 KiB
Python

"""
Asset Tracker Utility
Helper utility for modules to easily save generated content to the unified asset library.
"""
from typing import Dict, Any, Optional
from sqlalchemy.orm import Session
from services.content_asset_service import ContentAssetService
from models.content_asset_models import AssetType, AssetSource
import logging
import re
from urllib.parse import urlparse
logger = logging.getLogger(__name__)
# Maximum file size (100MB)
MAX_FILE_SIZE = 100 * 1024 * 1024
# Allowed URL schemes
ALLOWED_URL_SCHEMES = ['http', 'https', '/'] # Allow relative paths starting with /
def validate_file_url(file_url: str) -> bool:
"""Validate file URL format."""
if not file_url or not isinstance(file_url, str):
return False
# Allow relative paths
if file_url.startswith('/'):
return True
# Validate absolute URLs
try:
parsed = urlparse(file_url)
return parsed.scheme in ALLOWED_URL_SCHEMES and parsed.netloc
except Exception:
return False
def save_asset_to_library(
db: Session,
user_id: str,
asset_type: str,
source_module: str,
filename: str,
file_url: str,
file_path: Optional[str] = None,
file_size: Optional[int] = None,
mime_type: Optional[str] = None,
title: Optional[str] = None,
description: Optional[str] = None,
prompt: Optional[str] = None,
tags: Optional[list] = None,
asset_metadata: Optional[Dict[str, Any]] = None,
provider: Optional[str] = None,
model: Optional[str] = None,
cost: Optional[float] = None,
generation_time: Optional[float] = None,
) -> Optional[int]:
"""
Helper function to save a generated asset to the unified asset library.
This can be called from any module (story writer, image studio, etc.)
to automatically track generated content.
Args:
db: Database session
user_id: Clerk user ID
asset_type: 'text', 'image', 'video', or 'audio'
source_module: 'story_writer', 'image_studio', 'main_text_generation', etc.
filename: Original filename
file_url: Public URL to access the asset
file_path: Server file path (optional)
file_size: File size in bytes (optional)
mime_type: MIME type (optional)
title: Asset title (optional)
description: Asset description (optional)
prompt: Generation prompt (optional)
tags: List of tags (optional)
asset_metadata: Additional metadata (optional)
provider: AI provider used (optional)
model: Model used (optional)
cost: Generation cost (optional)
generation_time: Generation time in seconds (optional)
Returns:
Asset ID if successful, None otherwise
"""
try:
# Validate inputs
if not user_id or not isinstance(user_id, str):
logger.error("Invalid user_id provided")
return None
if not filename or not isinstance(filename, str):
logger.error("Invalid filename provided")
return None
if not validate_file_url(file_url):
logger.error(f"Invalid file_url format: {file_url}")
return None
if file_size and file_size > MAX_FILE_SIZE:
logger.warning(f"File size {file_size} exceeds maximum {MAX_FILE_SIZE}")
# Don't fail, just log warning
# Convert string enums to enum types
try:
asset_type_enum = AssetType(asset_type.lower())
except ValueError:
logger.warning(f"Invalid asset type: {asset_type}, defaulting to 'text'")
asset_type_enum = AssetType.TEXT
try:
source_module_enum = AssetSource(source_module.lower())
except ValueError:
logger.warning(f"Invalid source module: {source_module}, defaulting to 'story_writer'")
source_module_enum = AssetSource.STORY_WRITER
# Sanitize filename (remove path traversal attempts)
filename = re.sub(r'[^\w\s\-_\.]', '', filename.split('/')[-1])
if not filename:
filename = f"asset_{asset_type}_{source_module}.{asset_type}"
# Generate title from filename if not provided
if not title:
title = filename.replace('_', ' ').replace('-', ' ').title()
# Limit title length
if len(title) > 200:
title = title[:197] + '...'
service = ContentAssetService(db)
asset = service.create_asset(
user_id=user_id,
asset_type=asset_type_enum,
source_module=source_module_enum,
filename=filename,
file_url=file_url,
file_path=file_path,
file_size=file_size,
mime_type=mime_type,
title=title,
description=description,
prompt=prompt,
tags=tags or [],
asset_metadata=asset_metadata or {},
provider=provider,
model=model,
cost=cost,
generation_time=generation_time,
)
logger.info(f"✅ Asset saved to library: {asset.id} ({asset_type} from {source_module})")
return asset.id
except Exception as e:
logger.error(f"❌ Error saving asset to library: {str(e)}", exc_info=True)
return None