chore: initial import
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user