import asyncio import uuid from datetime import datetime, timezone from pathlib import Path from fastapi import APIRouter, HTTPException from fastapi.responses import FileResponse from config import FORMAT_META, STORAGE_DIR from database import create_guide, delete_guide, get_guide, list_guides from generator import generate_guide, cancel_guide from models import GuideCreateRequest, GuideResponse router = APIRouter(prefix="/api") @router.get("/formats") async def get_formats(): return FORMAT_META @router.post("/guides", response_model=GuideResponse) async def create(req: GuideCreateRequest): now = datetime.now(timezone.utc).isoformat() guide = { "id": str(uuid.uuid4()), "topic": req.topic.strip(), "format": req.format, "status": "queued", "progress": None, "html_path": None, "pdf_path": None, "created_at": now, "updated_at": now, } await create_guide(guide) asyncio.create_task(generate_guide(guide["id"], guide["topic"], guide["format"])) return guide @router.get("/guides", response_model=list[GuideResponse]) async def list_all(): return await list_guides() @router.get("/guides/{guide_id}", response_model=GuideResponse) async def get_one(guide_id: str): guide = await get_guide(guide_id) if guide is None: raise HTTPException(404, "Guide nicht gefunden") return guide @router.get("/guides/{guide_id}/html") async def download_html(guide_id: str): guide = await get_guide(guide_id) if guide is None: raise HTTPException(404, "Guide nicht gefunden") if guide["status"] != "done" or not guide["html_path"]: raise HTTPException(404, "HTML nicht verfügbar") path = Path(guide["html_path"]) if not path.exists(): raise HTTPException(404, "Datei nicht gefunden") return FileResponse(path, filename=f"{guide['topic']}-{guide['format']}.html", media_type="text/html") @router.post("/guides/{guide_id}/cancel") async def cancel(guide_id: str): cancelled = await cancel_guide(guide_id) if not cancelled: raise HTTPException(404, "Kein aktiver Prozess gefunden") return {"ok": True} @router.get("/guides/{guide_id}/pdf") async def download_pdf(guide_id: str): guide = await get_guide(guide_id) if guide is None: raise HTTPException(404, "Guide nicht gefunden") if guide["status"] != "done" or not guide["pdf_path"]: raise HTTPException(404, "PDF nicht verfügbar") path = Path(guide["pdf_path"]) if not path.exists(): raise HTTPException(404, "Datei nicht gefunden") return FileResponse(path, filename=f"{guide['topic']}-{guide['format']}.pdf", media_type="application/pdf") @router.delete("/guides/{guide_id}") async def remove(guide_id: str): guide = await get_guide(guide_id) if guide is None: raise HTTPException(404, "Guide nicht gefunden") if guide["html_path"]: Path(guide["html_path"]).unlink(missing_ok=True) if guide["pdf_path"]: Path(guide["pdf_path"]).unlink(missing_ok=True) await delete_guide(guide_id) return {"ok": True}