init
This commit is contained in:
123
backend/main.py
Normal file
123
backend/main.py
Normal 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")
|
||||
Reference in New Issue
Block a user