feat(audit-report): deterministischer Textreport je Audit (MD + PDF) + Bericht-Tab

Firmen-tauglicher Bericht aus den Snapshot-Modulergebnissen (kein Re-Crawl, kein
LLM): Einleitung, Testumfang+Methodik, Management-Summary (4-Status), Detail-
befunde je Modul, Maßnahmen, Rechtlicher Hinweis. Co-Pilot-Tonalität, Tracking-
statt Cookie-Rohzahl, Norm nur referenziert (kein Normtext).
- audit_report.py: assemble_report (pur) + render_markdown + render_pdf (reportlab)
- snapshot_check_routes: GET /report (struktur+md) + GET /report.pdf
- Frontend: AuditReportTab + Proxys (report, report/pdf) + "Bericht"-Tab
- Tests: 5 Assembler (compliance/tests → CI-geprüft) + 1 Vitest

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-06-13 14:50:45 +02:00
parent 7273245054
commit d720db07dd
8 changed files with 602 additions and 0 deletions
@@ -0,0 +1,40 @@
/**
* Audit-Report PDF — Proxy (streamt die PDF-Bytes durch)
* GET /api/sdk/v1/agent/snapshots/{snapshotId}/report/pdf
* → backend /api/compliance/agent/snapshots/{snapshotId}/report.pdf
*/
import { NextRequest, NextResponse } from 'next/server'
const BACKEND_URL =
process.env.BACKEND_API_URL || process.env.BACKEND_URL ||
'http://backend-compliance:8002'
export async function GET(
_request: NextRequest,
{ params }: { params: Promise<{ snapshotId: string }> },
) {
const { snapshotId } = await params
try {
const res = await fetch(
`${BACKEND_URL}/api/compliance/agent/snapshots/${snapshotId}/report.pdf`,
{ signal: AbortSignal.timeout(120_000) },
)
if (!res.ok) {
return NextResponse.json(
{ error: `PDF fehlgeschlagen (${res.status})` }, { status: res.status })
}
const buf = await res.arrayBuffer()
return new NextResponse(buf, {
status: 200,
headers: {
'Content-Type': 'application/pdf',
'Content-Disposition':
res.headers.get('content-disposition') ||
'attachment; filename="audit-report.pdf"',
},
})
} catch {
return NextResponse.json({ error: 'PDF fehlgeschlagen' }, { status: 503 })
}
}
@@ -0,0 +1,29 @@
/**
* Audit-Report (strukturiert + Markdown) — Proxy
* GET /api/sdk/v1/agent/snapshots/{snapshotId}/report
* → backend /api/compliance/agent/snapshots/{snapshotId}/report
*/
import { NextRequest, NextResponse } from 'next/server'
const BACKEND_URL =
process.env.BACKEND_API_URL || process.env.BACKEND_URL ||
'http://backend-compliance:8002'
export async function GET(
_request: NextRequest,
{ params }: { params: Promise<{ snapshotId: string }> },
) {
const { snapshotId } = await params
try {
const res = await fetch(
`${BACKEND_URL}/api/compliance/agent/snapshots/${snapshotId}/report`,
{ signal: AbortSignal.timeout(120_000) },
)
const data = await res.json()
return NextResponse.json(data, { status: res.status })
} catch {
return NextResponse.json(
{ error: 'Report-Erzeugung fehlgeschlagen' }, { status: 503 })
}
}