This commit is contained in:
Team3
2026-09-27 21:36:33 +02:00
commit 498fe2220a
34 changed files with 3608 additions and 0 deletions

123
backend/main.py Normal file
View File

@@ -0,0 +1,123 @@
import logging
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException
from fastapi.responses import FileResponse, PlainTextResponse
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel
from . import config, montage, pipeline, state
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s")
@asynccontextmanager
async def lifespan(app: FastAPI):
config.STORAGE.mkdir(exist_ok=True)
yield
for name in list(pipeline._laeufe):
await pipeline.pausieren(name)
app = FastAPI(title="buchwerk", lifespan=lifespan)
class ThemaNeu(BaseModel):
name: str
hinweise: str = ""
def _pruefen(name: str) -> dict:
if not state.name_ok(name) or not (t := state.thema(name)):
raise HTTPException(404, "Thema unbekannt")
return t
@app.get("/api/topics")
async def topics():
aus = []
for t in state.themen():
st = state.zustand(t["name"])
aus.append({"name": t["name"], "titel": t["titel"], "status": st["status"],
"laeuft": pipeline.laeuft(t["name"]), "stufe": st["stufe"]})
return aus
@app.post("/api/topics")
async def topic_anlegen(body: ThemaNeu):
name = body.name.strip()
if not state.name_ok(name):
raise HTTPException(400, "Ungültiger Name")
try:
return state.anlegen(name, body.hinweise)
except ValueError as e:
raise HTTPException(409, str(e))
@app.delete("/api/topics/{name}")
async def topic_loeschen(name: str):
_pruefen(name)
await pipeline.pausieren(name)
state.loeschen(name)
return {"ok": True}
@app.post("/api/topics/{name}/start")
async def start(name: str):
_pruefen(name)
if state.zustand(name)["status"] == "fertig":
raise HTTPException(400, "Lauf ist fertig – erst zurücksetzen")
pipeline.starten(name)
return {"ok": True}
@app.post("/api/topics/{name}/pause")
async def pause(name: str):
_pruefen(name)
await pipeline.pausieren(name)
return {"ok": True}
@app.post("/api/topics/{name}/reset")
async def reset(name: str):
_pruefen(name)
await pipeline.pausieren(name)
state.zuruecksetzen(name)
return {"ok": True}
@app.get("/api/topics/{name}/state")
async def zustand(name: str):
t = _pruefen(name)
st = state.zustand(name)
return {"name": name, "titel": t["titel"], "hinweise": t.get("hinweise", ""), **st,
"deutung": st.get("deutung") or state.deutung(name), "ereignisse": st["ereignisse"][-100:]}
@app.get("/api/topics/{name}/guide")
async def guide(name: str):
_pruefen(name)
return montage.guide(name)
@app.get("/api/topics/{name}/guide.md", response_class=PlainTextResponse)
async def guide_md(name: str):
_pruefen(name)
return montage.markdown(name)
@app.get("/api/topics/{name}/buch.pdf")
async def buch_pdf(name: str):
_pruefen(name)
p = state.verzeichnis(name) / "buch" / "buch.pdf"
if not p.exists():
raise HTTPException(404, "PDF noch nicht gebaut")
return FileResponse(p, media_type="application/pdf", filename=f"{name}.pdf")
if config.FRONTEND_DIST.exists():
app.mount("/assets", StaticFiles(directory=config.FRONTEND_DIST / "assets"), name="assets")
@app.get("/{pfad:path}")
async def spa(pfad: str):
return FileResponse(config.FRONTEND_DIST / "index.html")