import { useState } from 'react'; import { useQuery, useMutation } from '@tanstack/react-query'; import { useNavigate } from 'react-router-dom'; import { auditLogApi, AuditLogSearchParams } from '../../services/api'; import type { AuditLog, AuditAction, AuditSensitivity } from '../../types'; import Card from '../../components/ui/Card'; import Button from '../../components/ui/Button'; import Input from '../../components/ui/Input'; import Select from '../../components/ui/Select'; import { ArrowLeft, Download, Eye, Shield, ShieldCheck, ShieldAlert, RefreshCw, ChevronLeft, ChevronRight, X } from 'lucide-react'; const ACTION_OPTIONS = [ { value: '', label: 'Alle Aktionen' }, { value: 'CREATE', label: 'Erstellt' }, { value: 'READ', label: 'Gelesen' }, { value: 'UPDATE', label: 'Aktualisiert' }, { value: 'DELETE', label: 'Gelöscht' }, { value: 'EXPORT', label: 'Exportiert' }, { value: 'ANONYMIZE', label: 'Anonymisiert' }, { value: 'LOGIN', label: 'Login' }, { value: 'LOGOUT', label: 'Logout' }, { value: 'LOGIN_FAILED', label: 'Login fehlgeschlagen' }, ]; const SENSITIVITY_OPTIONS = [ { value: '', label: 'Alle Stufen' }, { value: 'LOW', label: 'Niedrig' }, { value: 'MEDIUM', label: 'Mittel' }, { value: 'HIGH', label: 'Hoch' }, { value: 'CRITICAL', label: 'Kritisch' }, ]; const RESOURCE_OPTIONS = [ { value: '', label: 'Alle Ressourcen' }, { value: 'Customer', label: 'Kunden' }, { value: 'Contract', label: 'Verträge' }, { value: 'User', label: 'Benutzer' }, { value: 'BankCard', label: 'Bankdaten' }, { value: 'IdentityDocument', label: 'Ausweisdokumente' }, { value: 'Authentication', label: 'Authentifizierung' }, { value: 'CustomerConsent', label: 'Einwilligungen' }, { value: 'GDPR', label: 'DSGVO' }, ]; function formatDate(date: string): string { return new Date(date).toLocaleString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', }); } function getActionColor(action: AuditAction): string { switch (action) { case 'CREATE': return 'bg-green-100 text-green-800'; case 'READ': return 'bg-blue-100 text-blue-800'; case 'UPDATE': return 'bg-yellow-100 text-yellow-800'; case 'DELETE': return 'bg-red-100 text-red-800'; case 'EXPORT': return 'bg-purple-100 text-purple-800'; case 'ANONYMIZE': return 'bg-orange-100 text-orange-800'; case 'LOGIN': return 'bg-teal-100 text-teal-800'; case 'LOGOUT': return 'bg-gray-100 text-gray-800'; case 'LOGIN_FAILED': return 'bg-red-200 text-red-900'; default: return 'bg-gray-100 text-gray-800'; } } function getSensitivityIcon(sensitivity: AuditSensitivity) { switch (sensitivity) { case 'LOW': return ; case 'MEDIUM': return ; case 'HIGH': return ; case 'CRITICAL': return ; default: return ; } } interface DetailModalProps { log: AuditLog; onClose: () => void; } function DetailModal({ log, onClose }: DetailModalProps) { const parseChanges = (changes: string | undefined): Record | null => { if (!changes) return null; try { return JSON.parse(changes); } catch { return null; } }; const before = parseChanges(log.changesBefore); const after = parseChanges(log.changesAfter); return (

Audit-Log Details

Zeitpunkt
{formatDate(log.createdAt)}
Benutzer
{log.userEmail}
Aktion
{log.action}
Ressource
{log.resourceType} {log.resourceId && `#${log.resourceId}`}
Endpoint
{log.httpMethod} {log.endpoint}
IP-Adresse
{log.ipAddress}
Sensitivität
{getSensitivityIcon(log.sensitivity)} {log.sensitivity}
Dauer
{log.durationMs ? `${log.durationMs}ms` : '-'}
{log.resourceLabel && (
Ressource-Bezeichnung
{log.resourceLabel}
)}
{log.changesEncrypted && (
Die Änderungsdaten sind verschlüsselt und können nur mit dem Encryption-Key eingesehen werden.
)} {(before || after) && !log.changesEncrypted && (

Änderungen

{before && (

Vorher

                      {JSON.stringify(before, null, 2)}
                    
)} {after && (

Nachher

                      {JSON.stringify(after, null, 2)}
                    
)}
)} {log.hash && (

Integrität

Hash: {log.hash}
{log.previousHash &&
Vorheriger: {log.previousHash}
}
)}
); } export default function AuditLogs() { const navigate = useNavigate(); const [page, setPage] = useState(1); const [filters, setFilters] = useState({ page: 1, limit: 50, }); const [selectedLog, setSelectedLog] = useState(null); const { data: logsData, isLoading, refetch } = useQuery({ queryKey: ['audit-logs', { ...filters, page }], queryFn: () => auditLogApi.search({ ...filters, page }), }); const verifyMutation = useMutation({ mutationFn: () => auditLogApi.verifyIntegrity(), onSuccess: (result) => { if (result.data?.valid) { alert('Hash-Kette ist intakt. Keine Manipulationen festgestellt.'); } else { alert(`Integritätsfehler gefunden:\n${result.data?.errors?.join('\n')}`); } }, onError: (error) => { alert(`Fehler bei der Integritätsprüfung: ${error instanceof Error ? error.message : 'Unbekannter Fehler'}`); }, }); const logs = logsData?.data || []; const pagination = logsData?.pagination; const handleFilterChange = (key: keyof AuditLogSearchParams, value: string) => { setFilters(prev => ({ ...prev, [key]: value || undefined })); setPage(1); }; const handleExport = async (format: 'json' | 'csv') => { try { const result = await auditLogApi.export({ ...filters, format }); const data = result.data; const blob = new Blob( [format === 'json' ? JSON.stringify(data, null, 2) : ''], { type: format === 'json' ? 'application/json' : 'text/csv' } ); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `audit-logs-${new Date().toISOString().split('T')[0]}.${format}`; a.click(); URL.revokeObjectURL(url); } catch (error) { alert(`Export fehlgeschlagen: ${error instanceof Error ? error.message : 'Unbekannter Fehler'}`); } }; return (

Audit-Protokoll

{/* Filter */}
handleFilterChange('search', e.target.value)} className="w-full" /> handleFilterChange('resourceType', e.target.value)} options={RESOURCE_OPTIONS} /> handleFilterChange('startDate', e.target.value)} /> handleFilterChange('endDate', e.target.value)} />
{/* Tabelle */} {isLoading ? (
Laden...
) : logs.length === 0 ? (
Keine Audit-Logs gefunden.
) : ( <>
{logs.map((log) => ( ))}
Zeitpunkt Benutzer Aktion Ressource IP
{formatDate(log.createdAt)}
{log.userEmail}
{log.userRole &&
{log.userRole}
}
{log.action}
{log.resourceType}
{log.resourceLabel && (
{log.resourceLabel}
)}
{log.ipAddress} {getSensitivityIcon(log.sensitivity)}
{/* Pagination */} {pagination && pagination.totalPages > 1 && (
Seite {pagination.page} von {pagination.totalPages} ({pagination.total} Einträge)
)} )}
{/* Detail Modal */} {selectedLog && ( setSelectedLog(null)} /> )}
); }