""" Files API Router """ import os import aiofiles from pathlib import Path from typing import List from uuid import UUID from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select from app.core.database import get_db from app.core.config import get_settings from app.models.models import File from app.schemas.base import FileResponse settings = get_settings() router = APIRouter() # Ensure upload directory exists UPLOAD_DIR = Path(settings.UPLOAD_DIR) UPLOAD_DIR.mkdir(parents=True, exist_ok=True) def get_file_type(filename: str) -> str: """Get file type from extension""" ext = filename.rsplit('.', 1)[-1].lower() if '.' in filename else '' type_map = { 'pdf': 'pdf', 'docx': 'docx', 'doc': 'docx', 'xlsx': 'xlsx', 'xls': 'xlsx', 'csv': 'csv', 'epub': 'epub', 'md': 'md', 'markdown': 'md', 'txt': 'txt' } return type_map.get(ext, 'txt') @router.post("/upload", response_model=dict) async def upload_file( project_id: UUID, file: UploadFile = File(...), db: AsyncSession = Depends(get_db) ): """Upload a file""" # Save file to disk file_path = UPLOAD_DIR / f"{project_id}_{file.filename}" async with aiofiles.open(file_path, 'wb') as f: content = await file.read() await f.write(content) # Create file record db_file = File( project_id=project_id, filename=file.filename, file_type=get_file_type(file.filename), file_path=str(file_path), size=len(content), status="pending" ) db.add(db_file) await db.commit() await db.refresh(db_file) return {"id": str(db_file.id), "filename": db_file.filename, "status": db_file.status} @router.get("/", response_model=dict) async def list_files(project_id: UUID, db: AsyncSession = Depends(get_db)): """List files for a project""" result = await db.execute( select(File).where(File.project_id == project_id).order_by(File.created_at.desc()) ) files = result.scalars().all() return {"files": [FileResponse.model_validate(f) for f in files]} @router.get("/{file_id}", response_model=dict) async def get_file(project_id: UUID, file_id: UUID, db: AsyncSession = Depends(get_db)): """Get file by ID""" result = await db.execute( select(File).where(File.id == file_id, File.project_id == project_id) ) file = result.scalar_one_or_none() if not file: raise HTTPException(status_code=404, detail="File not found") return FileResponse.model_validate(file) @router.delete("/{file_id}", response_model=dict) async def delete_file(project_id: UUID, file_id: UUID, db: AsyncSession = Depends(get_db)): """Delete file""" result = await db.execute( select(File).where(File.id == file_id, File.project_id == project_id) ) file = result.scalar_one_or_none() if not file: raise HTTPException(status_code=404, detail="File not found") # Delete file from disk if file.file_path and os.path.exists(file.file_path): os.remove(file.file_path) await db.delete(file) await db.commit() return {"message": "File deleted successfully"}