feat(audit): P88 PDF-Export via WeasyPrint
CI / detect-changes (push) Successful in 9s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / test-python-backend (push) Successful in 42s
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / validate-canonical-controls (push) Successful in 14s
CI / loc-budget (push) Failing after 17s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Has been skipped
CI / test-go (push) Has been skipped
CI / iace-gt-coverage (push) Has been skipped

GET /api/compliance/agent/snapshots/{id}/pdf liefert application/pdf
mit dem vollen Audit-Mail-Inhalt im A4-Print-Layout (Header mit
Site/Timestamp/Snapshot-ID, Seitenzahlen unten rechts).

check_replay.py liefert jetzt zusaetzlich 'full_html' (nicht nur
500-char-preview), damit der PDF-Renderer das komplette HTML hat.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-05-21 17:06:48 +02:00
parent 3207acea3e
commit c93c88577c
3 changed files with 109 additions and 0 deletions
@@ -207,6 +207,28 @@ async def get_snapshot(snapshot_id: str):
db.close()
@router.get("/snapshots/{snapshot_id}/pdf")
async def export_snapshot_pdf(snapshot_id: str):
"""P88 — PDF-Export der Audit-Mail. Liefert application/pdf."""
from fastapi import HTTPException
from fastapi.responses import Response
from database import SessionLocal
from compliance.services.mail_pdf_export import render_snapshot_as_pdf
db = SessionLocal()
try:
pdf = render_snapshot_as_pdf(db, snapshot_id)
finally:
db.close()
if not pdf:
raise HTTPException(404, f"Snapshot {snapshot_id} nicht gefunden "
"oder PDF-Render fehlgeschlagen.")
fname = f"breakpilot-audit-{snapshot_id[:8]}.pdf"
return Response(
content=pdf, media_type="application/pdf",
headers={"Content-Disposition": f'attachment; filename="{fname}"'},
)
@router.post("/snapshots/{snapshot_id}/replay")
async def replay_snapshot(
snapshot_id: str,