""" Folders API router. Source roots themselves are config-driven (PHOTO_DIRS in .env → backend bootstrap on startup) — adding or removing one is a docker-compose change. The UI can read the list, trigger a manual rescan, and rename the display label, but it can't change the on-disk path. """ from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession import os from app.database import get_db from app.models import Folder, SourceRoot router = APIRouter() class FolderRename(BaseModel): name: str @router.get("") async def get_folders(db: AsyncSession = Depends(get_db)): """Get all source folders""" # Get source roots instead of regular folders result = await db.execute(select(SourceRoot).where(SourceRoot.is_active == True)) source_roots = result.scalars().all() folders_list = [] for root in source_roots: # Get photo count for this source root folder_result = await db.execute( select(Folder).where(Folder.source_root_id == root.id) ) folders = folder_result.scalars().all() photo_count = sum(f.photo_count for f in folders) folders_list.append({ "id": root.id, "name": root.name or os.path.basename(root.path), "path": root.path, "photo_count": photo_count }) return {"folders": folders_list} @router.patch("/{folder_id}") async def rename_folder( folder_id: str, body: FolderRename, db: AsyncSession = Depends(get_db), ): """Rename a source root's display label. Does NOT touch the on-disk path — that's controlled by the docker mount.""" name = (body.name or '').strip() if not name: raise HTTPException(status_code=400, detail="Name cannot be empty") result = await db.execute(select(SourceRoot).where(SourceRoot.id == folder_id)) source_root = result.scalar_one_or_none() if not source_root: raise HTTPException(status_code=404, detail="Source folder not found") source_root.name = name await db.commit() return {"id": source_root.id, "name": source_root.name, "path": source_root.path} @router.post("/{folder_id}/scan") async def scan_folder(folder_id: str, db: AsyncSession = Depends(get_db)): """Trigger manual re-scan of source root folder""" from app.tasks.celery import celery_app result = await db.execute(select(SourceRoot).where(SourceRoot.id == folder_id)) source_root = result.scalar_one_or_none() if not source_root: raise HTTPException(status_code=404, detail="Source folder not found") # Queue scan task using the task name defined in the decorator task = celery_app.send_task('scan_folder', args=[source_root.path, source_root.id]) return {"status": "success", "message": f"Scan queued for {source_root.path}", "task_id": task.id}