# app/api/v1/themes/router.py from typing import List from uuid import UUID from fastapi import APIRouter, Depends, HTTPException, status, Query from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session from app.api.dependencies.common import get_storage_provider from app.api.dependencies.auth import get_current_user from app.core.database import get_db from app.crud.event_theme import event_theme_crud as event_theme_crud from app.models import User from app.schemas.event_themes import EventThemeCreate, EventThemeResponse, EventThemeUpdate from app.core.storage import StorageProvider from app.utils.files import _relocate_theme_file router = APIRouter() @router.post("/", response_model=EventThemeResponse, operation_id="create_event_theme") def create_theme( *, db: Session = Depends(get_db), theme_in: EventThemeCreate, current_user: User = Depends(get_current_user), storage: StorageProvider = Depends(get_storage_provider) ) -> EventThemeResponse: if current_user is None or not current_user.is_superuser: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid authentication credentials", headers={"WWW-Authenticate": "Bearer"}, ) """Create new event theme with proper file organization.""" # First create the theme to get an ID theme = event_theme_crud.create(db, obj_in=theme_in) # Keep track of files to move and URLs to update url_updates = {} # Move preview image if it exists if theme.preview_image_url and 'uploads/' in theme.preview_image_url: new_url = _relocate_theme_file(theme.id, theme.preview_image_url, 'preview', storage) if new_url: url_updates['preview_image_url'] = new_url # Move background image if it exists if theme.background_image_url and 'uploads/' in theme.background_image_url: new_url = _relocate_theme_file(theme.id, theme.background_image_url, 'background', storage) if new_url: url_updates['background_image_url'] = new_url # Move foreground image if it exists if theme.foreground_image_url and 'uploads/' in theme.foreground_image_url: new_url = _relocate_theme_file(theme.id, theme.foreground_image_url, 'foreground', storage) if new_url: url_updates['foreground_image_url'] = new_url # Handle asset images if they exist if theme.asset_image_urls: new_assets = {} for key, url in theme.asset_image_urls.items(): if url and 'uploads/' in url: new_url = _relocate_theme_file(theme.id, url, f'assets/{key}', storage) if new_url: new_assets[key] = new_url else: new_assets[key] = url else: new_assets[key] = url if new_assets: url_updates['asset_image_urls'] = new_assets # Update the theme if we relocated any files if url_updates: theme = event_theme_crud.update(db, db_obj=theme, obj_in=url_updates) return theme @router.get("/", response_model=List[EventThemeResponse], operation_id="list_event_themes") def list_themes( db: Session = Depends(get_db), skip: int = 0, limit: int = 100 ) -> List[EventThemeResponse]: """List event themes.""" themes = event_theme_crud.get_multi(db, skip=skip, limit=limit) return themes @router.get("/{theme_id}", response_model=EventThemeResponse, operation_id="get_event_theme") def get_theme( *, db: Session = Depends(get_db), theme_id: UUID ) -> EventThemeResponse: """Get specific theme by ID.""" theme = event_theme_crud.get(db, id=theme_id) if not theme: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Theme not found" ) return theme @router.patch("/{theme_id}", response_model=EventThemeResponse, operation_id="update_event_theme") def update_theme( *, db: Session = Depends(get_db), theme_id: UUID, theme_in: EventThemeUpdate, current_user: User = Depends(get_current_user), storage: StorageProvider = Depends(get_storage_provider) ) -> EventThemeResponse: if current_user is None or not current_user.is_superuser: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid authentication credentials", headers={"WWW-Authenticate": "Bearer"}, ) """Update specific theme by ID with proper file organization.""" # Get the existing theme theme = event_theme_crud.get(db, id=theme_id) if not theme: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Theme not found" ) # Create a mutable copy of theme_in data update_data = theme_in.model_dump(exclude_unset=True) # Relocate any image files and update URLs if 'preview_image_url' in update_data and update_data['preview_image_url'] and 'uploads/' in update_data['preview_image_url']: new_url = _relocate_theme_file(theme_id, update_data['preview_image_url'], 'preview', storage) if new_url: update_data['preview_image_url'] = new_url if 'background_image_url' in update_data and update_data['background_image_url'] and 'uploads/' in update_data['background_image_url']: new_url = _relocate_theme_file(theme_id, update_data['background_image_url'], 'background', storage) if new_url: update_data['background_image_url'] = new_url if 'foreground_image_url' in update_data and update_data['foreground_image_url'] and 'uploads/' in update_data['foreground_image_url']: new_url = _relocate_theme_file(theme_id, update_data['foreground_image_url'], 'foreground', storage) if new_url: update_data['foreground_image_url'] = new_url if 'asset_image_urls' in update_data and update_data['asset_image_urls']: new_assets = {} for key, url in update_data['asset_image_urls'].items(): if url and 'uploads/' in url: new_url = _relocate_theme_file(theme_id, url, f'assets/{key}', storage) if new_url: new_assets[key] = new_url else: new_assets[key] = url else: new_assets[key] = url if new_assets: update_data['asset_image_urls'] = new_assets # Update the theme with the modified data theme = event_theme_crud.update(db, db_obj=theme, obj_in=update_data) return theme @router.delete("/{theme_id}", operation_id="delete_event_theme") def delete_theme( *, db: Session = Depends(get_db), theme_id: UUID, current_user: User = Depends(get_current_user), hard_delete: bool = Query(False, description="Perform hard delete instead of soft delete") ): """Delete specific theme by ID.""" if current_user is None: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid authentication credentials", headers={"WWW-Authenticate": "Bearer"}, ) try: event_theme_obj = event_theme_crud.get(db=db, id=theme_id) if not event_theme_obj: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Event theme not found" ) # Only creator or superuser can delete if not current_user.is_superuser: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions to delete this event theme" ) if hard_delete: # Hard delete - only for superusers if not current_user.is_superuser: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Only administrators can perform hard delete" ) event_theme_crud.remove(db=db, id=theme_id) else: # Soft delete - set is_active to False event_theme_crud.update(db=db, db_obj=event_theme_obj, obj_in={"is_active": False}) return None # 204 No Content except SQLAlchemyError: db.rollback() raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Error deleting event" )