Files
planer/backend/routes.py
2026-07-22 16:12:23 +02:00

109 lines
3.5 KiB
Python

"""API des Viewers (Phase 2). Lese-Routen über viewer.py, Lauf-Routen über scan_dienst."""
from pathlib import Path
from fastapi import APIRouter, HTTPException
import agents
import database as db
import projekte
import scan_dienst
import viewer
router = APIRouter(prefix="/api")
def _wurzel(name: str) -> Path:
wurzel = projekte.finde(name)
if wurzel is None:
raise HTTPException(404, f"Projekt '{name}' nicht in projekte.txt")
return wurzel
@router.get("/health")
async def health():
return {"ok": True}
@router.get("/projekte")
async def liste():
out = []
for p in projekte.lade():
eintrag = dict(p)
if "fehler" not in p:
wurzel = Path(p["pfad"])
eintrag["hat_planer"] = (wurzel / ".planer" / "kern.md").is_file()
staende = {d: s for d, (s, _) in scan_dienst.einheiten_bestand(wurzel).items()}
eintrag["drift"] = viewer.drift(wurzel, staende)["veraltet"]
eintrag["letzter_scan_ts"] = await db.letzter_agent_ts(p["name"])
lauf = scan_dienst.status(p["name"])
eintrag["scan_laeuft"] = bool(lauf and lauf["laeuft"])
out.append(eintrag)
return out
@router.get("/projekte/{name}")
async def projekt(name: str):
wurzel = _wurzel(name)
daten = viewer.lade_projekt(wurzel)
einheiten_kurz = {eid: {"beschreibung": e["beschreibung"]}
for eid, e in daten["einheiten"].items()}
return {
"name": name,
"kern": daten["kern"],
"bereiche": daten["bereiche"],
"architektur": daten["architektur"],
"flows": daten["flows"],
"pruefung": daten["pruefung"],
"einheiten": einheiten_kurz,
"doku_ordnung": viewer.doku_ordnung(daten),
"drift": viewer.drift(wurzel, daten["staende"]),
}
@router.get("/projekte/{name}/einheit")
async def einheit(name: str, id: str):
wurzel = _wurzel(name)
daten = viewer.lade_projekt(wurzel)
e = daten["einheiten"].get(id)
if e is None:
raise HTTPException(404, f"Einheit '{id}' unbekannt")
quelldatei = id.partition("::")[0]
veraltet = quelldatei in viewer.drift(wurzel, daten["staende"])["veraltet"]
code = viewer.code_extrahieren(wurzel, id)
for anker in e["anker"]:
zusatz = viewer.code_extrahieren(wurzel, anker)
if zusatz and zusatz not in code:
code += ("\n\n" if code else "") + zusatz
return {**e, "code": code, "veraltet": veraltet}
@router.post("/projekte/{name}/scan")
async def scan_starten(name: str, body: dict):
wurzel = _wurzel(name)
art = body.get("art", "scan")
if art not in ("scan", "rescan"):
raise HTTPException(400, f"unbekannte Art '{art}'")
try:
status = scan_dienst.starte(name, wurzel, art)
except scan_dienst.VorflugFehler as e:
raise HTTPException(409, str(e))
return {"status": status}
@router.get("/projekte/{name}/scan-status")
async def scan_status(name: str):
_wurzel(name)
lauf = scan_dienst.status(name)
if lauf is None:
return {"laeuft": False, "art": None, "ergebnis": None, "stufen": {}, "agenten": [], "dead": []}
out = dict(lauf)
if lauf["laeuft"]:
out["stufen"] = (await db.kanban_stage_counts(name)).get("scan", {})
out["agenten"] = agents.active_agents(f"scan-{name}-")
out["dead"] = [c["card_id"] for c in await db.kanban_dead(name)]
else:
out["stufen"] = {}
out["agenten"] = []
out["dead"] = (lauf["ergebnis"] or {}).get("dead", [])
return out