Files
2026-06-24 14:40:43 +00:00

76 lines
2.5 KiB
Python

from __future__ import annotations
import os
from typing import Any
from fastapi import FastAPI, HTTPException, Request, Response
from fastapi.responses import HTMLResponse, JSONResponse
from .renderer import ALLOWED_THEMES, pdf_filename, render_documents, render_pdf_with_gotenberg
from .theme_catalog import public_theme_catalog
from .validation import validate_report
app = FastAPI(
title="Monthly Report Renderer",
version="2.0.0",
description="Validiert Report-Payloads, rendert feste HTML-Templates und erzeugt PDFs via Gotenberg.",
)
@app.middleware("http")
async def limit_request_size(request: Request, call_next):
max_bytes = int(os.getenv("MAX_PAYLOAD_BYTES", "2097152"))
content_length = request.headers.get("content-length")
if content_length and int(content_length) > max_bytes:
return JSONResponse(status_code=413, content={"detail": "Payload too large"})
return await call_next(request)
@app.get("/health")
def health() -> dict[str, Any]:
return {"status": "ok", "service": "monthly-report-renderer", "version": "2.0.0", "theme_count": len(ALLOWED_THEMES)}
@app.get("/themes")
def themes() -> dict[str, Any]:
return {"themes": sorted(ALLOWED_THEMES), "catalog": public_theme_catalog()}
@app.post("/validate")
def validate(payload: dict[str, Any]) -> dict[str, Any]:
errors = validate_report(payload)
return {"valid": not errors, "errors": errors}
def _ensure_valid(payload: dict[str, Any]) -> None:
errors = validate_report(payload)
if errors:
raise HTTPException(status_code=422, detail={"message": "Invalid report payload", "errors": errors})
@app.post("/preview/html", response_class=HTMLResponse)
def preview_html(payload: dict[str, Any]) -> HTMLResponse:
_ensure_valid(payload)
html, _ = render_documents(payload)
return HTMLResponse(content=html, status_code=200)
@app.post("/render")
def render(payload: dict[str, Any]) -> Response:
_ensure_valid(payload)
try:
pdf = render_pdf_with_gotenberg(payload)
except Exception as exc:
raise HTTPException(status_code=502, detail=f"PDF rendering failed: {exc}") from exc
filename = pdf_filename(payload)
return Response(
content=pdf,
media_type="application/pdf",
headers={
"Content-Disposition": f'attachment; filename="{filename}"',
"X-Report-Version": payload["report"]["report_version"],
"X-Template-Version": payload["report"]["template_version"],
},
)