chore: initial import

This commit is contained in:
2026-07-24 21:37:03 +02:00
commit 45bc449ea0
53 changed files with 12329 additions and 0 deletions
+104
View File
@@ -0,0 +1,104 @@
# -*- coding: utf-8 -*-
"""
Plugin-Adapter für die Rezept-Inventur.
Nur lesend: Es gibt hier bewusst keinen Endpunkt, der etwas verändern könnte.
"""
from __future__ import annotations
import json
import sys
from typing import Any
from fastapi import FastAPI, HTTPException
from fastapi.responses import FileResponse
from pydantic import BaseModel, Field
from core.jobs import job_router
TOOL = "lint.py"
SEVERITIES = {"err", "warn", "info"}
class ScanRequest(BaseModel):
only: list[str] = Field(default_factory=list)
skip: list[str] = Field(default_factory=list)
insecure: bool = False
def create_app(ctx):
tool = ctx.path("tool", TOOL)
report_file = ctx.data_dir / "bericht" / "bericht.json"
csv_file = ctx.data_dir / "bericht" / "befunde.csv"
def tool_env() -> dict:
"""
Umgebung für den Subprozess.
Wichtig: DATA_DIR ausdrücklich mitgeben. Ohne das schreibt das Skript
seinen Rückfallpfad neben die Anwendung — im Container ist der
schreibgeschützt, und die Oberfläche würde den Bericht nie finden.
"""
env = ctx.settings.tool_env()
env["DATA_DIR"] = str(ctx.data_dir)
return env
app = FastAPI(title=ctx.meta.name, docs_url="/api/docs", redoc_url=None)
@app.get("/", include_in_schema=False)
def index():
return FileResponse(ctx.path("static", "index.html"))
@app.get("/api/state")
def state() -> dict[str, Any]:
report = None
if report_file.is_file():
try:
report = json.loads(report_file.read_text(encoding="utf-8"))
except Exception:
report = None
running = [j.info() for j in ctx.jobs.running(ctx.id)]
return {
"report": report,
"tandoor": ctx.settings.status()["tandoor"],
"running": running[0] if running else None,
"has_csv": csv_file.is_file(),
}
@app.get("/api/export.csv")
def export():
if not csv_file.is_file():
raise HTTPException(404, "Noch kein Bericht vorhanden.")
return FileResponse(csv_file, media_type="text/csv", filename="befunde.csv")
@app.post("/api/run/scan")
async def run_scan(request: ScanRequest) -> dict[str, Any]:
if ctx.jobs.running(ctx.id):
raise HTTPException(409, "Die Inventur läuft bereits.")
if not ctx.settings.status()["tandoor"]:
raise HTTPException(400, "Tandoor-URL und Token fehlen siehe Einstellungen.")
argv = [sys.executable, str(tool)]
if request.insecure:
argv.append("--insecure")
argv.append("pruefen")
bad = [s for s in request.only if s not in SEVERITIES]
if bad:
raise HTTPException(400, f"Unbekannter Schweregrad: {bad}")
if request.only:
argv += ["--nur", ",".join(request.only)]
if request.skip:
# Die Schlüssel prüft das Skript selbst; hier nur grob absichern.
clean = [s for s in request.skip if s.replace("_", "").isalnum()]
if clean:
argv += ["--ohne", ",".join(clean)]
job = await ctx.jobs.start(
plugin=ctx.id, label="Inventur der Sammlung", argv=argv,
cwd=ctx.data_dir, env=tool_env(),
)
return job.info()
app.include_router(job_router(ctx.jobs, ctx.id))
return app