Files
eventspace/backend/app/api/routes/event_themes.py
Felipe Cardoso 0eabd9e5dd
All checks were successful
Build and Push Docker Images / changes (push) Successful in 5s
Build and Push Docker Images / build-backend (push) Successful in 52s
Build and Push Docker Images / build-frontend (push) Has been skipped
Refactor theme creation and update for file management
Enhanced theme creation and update logic to include proper file organization by relocating and managing URLs for images and assets. Introduced roles validation to restrict access to superusers for these operations. Updated tests to align with the refactored logic and dependencies.
2025-03-12 21:16:49 +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"
)