"""Alle API-Routen. Fachlogik wirft ValueError/FileNotFoundError, hier wird auf 400/404/409 gemappt.""" from fastapi import APIRouter, HTTPException import agents import chat import features import projekte import planung import pruefung router = APIRouter(prefix="/api") @router.get("/health") async def health(): return {"ok": True} @router.get("/projekte") async def projekte_liste(): return projekte.liste() @router.post("/projekte") async def projekt_anlegen(body: dict): name = (body.get("name") or "").strip() try: projekte.anlegen(name) except ValueError as e: raise HTTPException(409 if "existiert" in str(e) else 400, str(e)) return {"name": name} @router.put("/projekte/{name}") async def projekt_umbenennen(name: str, body: dict): neu = (body.get("name") or "").strip() try: projekte.umbenennen(name, neu) except FileNotFoundError as e: raise HTTPException(404, str(e)) except ValueError as e: raise HTTPException(409 if "existiert" in str(e) else 400, str(e)) return {"name": neu} @router.delete("/projekte/{name}") async def projekt_loeschen(name: str): try: projekte.loeschen(name) except FileNotFoundError as e: raise HTTPException(404, str(e)) except ValueError as e: raise HTTPException(400, str(e)) return {"ok": True} @router.get("/projekte/{name}/features") async def features_liste(name: str, sicht: str = "planung"): """`sicht=uebersicht` liest den Projektstand, sonst die Arbeitskopie. Ohne laufende Planung sind beide dasselbe Verzeichnis. """ try: return features.liste(name, nur_stand=sicht == "uebersicht") except (FileNotFoundError, ValueError) as e: raise HTTPException(404, str(e)) @router.post("/projekte/{name}/features") async def feature_anlegen(name: str, body: dict): try: return features.anlegen(name, body.get("roh") or "") except FileNotFoundError as e: raise HTTPException(404, str(e)) except ValueError as e: raise HTTPException(400, str(e)) @router.put("/projekte/{name}/features/{id}") async def feature_speichern(name: str, id: str, body: dict): try: return features.speichern(name, id, body.get("roh") or "") except FileNotFoundError as e: raise HTTPException(404, str(e)) except ValueError as e: raise HTTPException(400, str(e)) @router.post("/projekte/{name}/pruefung") async def pruefung_starten(name: str, body: dict | None = None): art = (body or {}).get("art") or "stil" try: return await pruefung.starte(name, art) except FileNotFoundError as e: raise HTTPException(404, str(e)) except pruefung.LaeuftBereits as e: raise HTTPException(409, str(e)) # 409 heißt genau das und sonst nichts except ValueError as e: raise HTTPException(400, str(e)) @router.get("/projekte/{name}/pruefung") async def pruefung_status(name: str): return pruefung.status(name) @router.get("/projekte/{name}/planung") async def planung_stand(name: str): try: return planung.stand(name) except FileNotFoundError as e: raise HTTPException(404, str(e)) @router.post("/projekte/{name}/planung") async def planung_starten(name: str): try: planung.starte(name) except FileNotFoundError as e: raise HTTPException(404, str(e)) except ValueError as e: raise HTTPException(409, str(e)) return planung.stand(name) @router.post("/projekte/{name}/planung/umsetzen") async def planung_umsetzen(name: str): try: planung.setze_um(name) except FileNotFoundError as e: raise HTTPException(404, str(e)) except ValueError as e: raise HTTPException(409, str(e)) return {"ok": True} @router.delete("/projekte/{name}/planung") async def planung_abbrechen(name: str): try: planung.brich_ab(name) except FileNotFoundError as e: raise HTTPException(404, str(e)) return {"ok": True} @router.post("/projekte/{name}/planung/zuruecksetzen") async def planung_zuruecksetzen(name: str, body: dict): try: planung.zuruecksetzen(name, (body or {}).get("titel") or "") except FileNotFoundError as e: raise HTTPException(404, str(e)) except ValueError as e: raise HTTPException(409, str(e)) return planung.stand(name) @router.delete("/projekte/{name}/pruefung") async def pruefung_beenden(name: str): pruefung.beende(name) return {"ok": True} @router.delete("/projekte/{name}/features/{id}") async def feature_loeschen(name: str, id: str): try: features.loeschen(name, id) except (FileNotFoundError, ValueError) as e: raise HTTPException(404, str(e)) return {"ok": True} @router.put("/projekte/{name}/features/{id}/projekt") async def feature_verschieben(name: str, id: str, body: dict): try: return features.verschieben(name, id, (body.get("ziel") or "").strip()) except FileNotFoundError as e: raise HTTPException(404, str(e)) except ValueError as e: raise HTTPException(400, str(e)) @router.get("/projekte/{name}/chat") async def chat_verlauf(name: str): try: return {"verlauf": chat.verlauf(name)} except FileNotFoundError as e: raise HTTPException(404, str(e)) @router.post("/projekte/{name}/chat") async def chat_frage(name: str, body: dict): try: return await chat.frage(name, body.get("text") or "") except FileNotFoundError as e: raise HTTPException(404, str(e)) except agents.NichtKonfiguriert as e: raise HTTPException(400, str(e)) except ValueError as e: raise HTTPException(400, str(e)) @router.delete("/projekte/{name}/chat") async def chat_leeren(name: str): try: chat.leeren(name) except FileNotFoundError as e: raise HTTPException(404, str(e)) return {"ok": True}