feat(sprint-6): Phase 4 — Immutable audit log
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled

- V8 migration: audit_events table (JSONB metadata, immutable by design)
- AuditEvent entity + AuditEventType enum (18 event types)
- AuditService: log events, paginated query, PDF export
- AuditController: GET /api/v1/audit (paginated, filtered), GET export
- AuditEventRepository with JPQL filtered queries
- Frontend: /audit-log page (read-only, filterable, timezone-aware)
- PDF export button for Behörde inspections
- Sidebar: 'Protokoll' under new Compliance section
- PdfReportGenerator: generateAuditReport method added
- 10-year retention, REVOKE DELETE documented
- Full i18n (de/en) with 18 event type translations
This commit is contained in:
Patrick Plate
2026-06-12 22:40:40 +02:00
parent 61e481b37b
commit 05933a08ca
12 changed files with 982 additions and 0 deletions
@@ -0,0 +1,88 @@
import { useMutation, useQuery } from "@tanstack/react-query"
import { apiClient, apiDownload } from "@/lib/api-client"
// --- Types ---
export interface AuditEventData {
id: string
eventType: string
entityType: string
entityId: string | null
actorId: string
actorName: string
actorRole: string
description: string
metadata: string | null
ipAddress: string | null
timestamp: string
}
export interface AuditLogPage {
content: AuditEventData[]
totalElements: number
totalPages: number
number: number
size: number
}
export interface AuditLogFilters {
page?: number
size?: number
eventType?: string
entityType?: string
actorId?: string
from?: string
to?: string
}
// --- Query Hooks ---
export function useAuditLogQuery(filters: AuditLogFilters = {}) {
const {
page = 0,
size = 20,
eventType,
entityType,
actorId,
from,
to,
} = filters
return useQuery({
queryKey: ["audit", page, size, eventType, entityType, actorId, from, to],
queryFn: () =>
apiClient<AuditLogPage>("/audit", {
params: {
page: page.toString(),
size: size.toString(),
eventType: eventType || undefined,
entityType: entityType || undefined,
actorId: actorId || undefined,
from: from || undefined,
to: to || undefined,
},
}),
})
}
// --- Mutation Hooks ---
export function useExportAuditPdfMutation() {
return useMutation({
mutationFn: async ({ from, to }: { from: string; to: string }) => {
const { blob, filename } = await apiDownload(
`/audit/export?from=${from}&to=${to}`
)
// Trigger browser download
const url = URL.createObjectURL(blob)
const a = document.createElement("a")
a.href = url
a.download = filename || `audit-log-${from}-to-${to}.pdf`
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
},
})
}