diff --git a/app/admin.py b/app/admin.py index 4960c53..cc210b2 100644 --- a/app/admin.py +++ b/app/admin.py @@ -2,11 +2,12 @@ import csv import io from pathlib import Path -from fastapi import APIRouter, Depends, Query +from fastapi import APIRouter, Depends, File, Form, HTTPException, Query, UploadFile from fastapi.responses import Response from app.config import get_settings from app.db import list_leads, stats +from app.frontend import ALLOWED_FILENAMES from app.security import require_admin router = APIRouter(prefix="/admin", tags=["admin"]) @@ -30,6 +31,43 @@ def admin_material_files(_user: str = Depends(require_admin)): return {"material_dir": str(material_dir), "exists": True, "files": files} +@router.post("/upload-material") +async def admin_upload_material( + target_name: str = Form(...), + file: UploadFile = File(...), + _user: str = Depends(require_admin), +): + """Sube un archivo al volumen /app/material/ con un nombre de la whitelist.""" + if target_name not in ALLOWED_FILENAMES: + raise HTTPException( + status_code=400, + detail=f"target_name must be one of {sorted(ALLOWED_FILENAMES)}", + ) + material_dir = Path(get_settings().material_dir) + material_dir.mkdir(parents=True, exist_ok=True) + dest = material_dir / target_name + content = await file.read() + dest.write_bytes(content) + return { + "saved_as": str(dest), + "size_bytes": len(content), + "size_mb": round(len(content) / (1024 * 1024), 2), + "original_filename": file.filename, + } + + +@router.delete("/material-files/{filename}") +def admin_delete_material(filename: str, _user: str = Depends(require_admin)): + """Borra un archivo del volumen material/.""" + if filename not in ALLOWED_FILENAMES: + raise HTTPException(status_code=400, detail="filename not in whitelist") + path = Path(get_settings().material_dir) / filename + if not path.exists(): + raise HTTPException(status_code=404, detail="file not found") + path.unlink() + return {"deleted": filename} + + @router.get("/leads.json") def admin_leads_json( _user: str = Depends(require_admin),