Files
eventspace/backend/app/api/routes/event_themes.py
Felipe Cardoso 41eb631a5e Remove leading slash from 'uploads/' checks in theme URLs
Updated conditions to check 'uploads/' without a leading slash across preview, background, foreground, and asset image URLs. This ensures consistent handling of URLs during the relocation process and avoids potential mismatches in the path string comparison.
2025-03-14 02:23:44 +01:00

228 lines
8.4 KiB
Python

# 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 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"
)