111 lines
3.2 KiB
Python
111 lines
3.2 KiB
Python
|
|
"""
|
||
|
|
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"}
|