fix(admin-v2): Restore complete admin-v2 application

The admin-v2 application was incomplete in the repository. This commit
restores all missing components:

- Admin pages (76 pages): dashboard, ai, compliance, dsgvo, education,
  infrastructure, communication, development, onboarding, rbac
- SDK pages (45 pages): tom, dsfa, vvt, loeschfristen, einwilligungen,
  vendor-compliance, tom-generator, dsr, and more
- Developer portal (25 pages): API docs, SDK guides, frameworks
- All components, lib files, hooks, and types
- Updated package.json with all dependencies

The issue was caused by incomplete initial repository state - the full
admin-v2 codebase existed in backend/admin-v2 and docs-src/admin-v2
but was never fully synced to the main admin-v2 directory.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
BreakPilot Dev
2026-02-08 23:40:15 -08:00
parent f28244753f
commit 660295e218
385 changed files with 138126 additions and 3079 deletions

View File

@@ -0,0 +1,395 @@
'use client'
/**
* TOM - Technische und Organisatorische Massnahmen
*
* Art. 32 DSGVO - Sicherheit der Verarbeitung
*/
import { useState } from 'react'
import { PagePurpose } from '@/components/common/PagePurpose'
interface TOMCategory {
id: string
title: string
article: string
description: string
measures: {
name: string
description: string
status: 'implemented' | 'partial' | 'planned' | 'not_applicable'
evidence?: string
lastReview?: string
}[]
}
export default function TOMPage() {
const [expandedCategory, setExpandedCategory] = useState<string | null>('encryption')
const tomCategories: TOMCategory[] = [
{
id: 'encryption',
title: 'Verschluesselung',
article: 'Art. 32 Abs. 1 lit. a',
description: 'Pseudonymisierung und Verschluesselung personenbezogener Daten',
measures: [
{
name: 'TLS 1.3 fuer alle Verbindungen',
description: 'Alle HTTP-Verbindungen werden ueber HTTPS mit TLS 1.3 verschluesselt',
status: 'implemented',
evidence: 'SSL Labs A+ Rating, Nginx Config',
lastReview: '2024-12-01'
},
{
name: 'Verschluesselung ruhender Daten',
description: 'PostgreSQL-Datenbank mit AES-256 Verschluesselung (pgcrypto)',
status: 'implemented',
evidence: 'PostgreSQL Config, Encryption Keys in Vault',
lastReview: '2024-12-01'
},
{
name: 'E-Mail-Verschluesselung',
description: 'Optionale PGP-Verschluesselung fuer sensible E-Mails',
status: 'partial',
evidence: 'PGP-Keys verfuegbar, nicht fuer alle Empfaenger',
},
{
name: 'Backup-Verschluesselung',
description: 'Alle Backups werden mit AES-256 verschluesselt gespeichert',
status: 'implemented',
evidence: 'restic Backup Config',
lastReview: '2024-11-15'
},
]
},
{
id: 'access_control',
title: 'Zugriffskontrolle',
article: 'Art. 32 Abs. 1 lit. b',
description: 'Faehigkeit, Vertraulichkeit und Integritaet auf Dauer sicherzustellen',
measures: [
{
name: 'Role-Based Access Control (RBAC)',
description: 'Zugriff basierend auf Rollen: user, admin, data_protection_officer',
status: 'implemented',
evidence: 'consent-service/internal/middleware/auth.go',
lastReview: '2024-12-01'
},
{
name: 'Multi-Faktor-Authentifizierung',
description: '2FA fuer Admin-Zugaenge (TOTP)',
status: 'implemented',
evidence: 'Auth-Service Implementation',
lastReview: '2024-12-01'
},
{
name: 'Passwort-Policy',
description: 'Min. 12 Zeichen, Komplexitaetsanforderungen, bcrypt-Hashing',
status: 'implemented',
evidence: 'consent-service/internal/services/auth_service.go',
lastReview: '2024-12-01'
},
{
name: 'Session-Management',
description: 'JWT mit 24h Ablauf, Refresh-Token-Rotation',
status: 'implemented',
evidence: 'JWT Config, Token-Rotation Logic',
lastReview: '2024-12-01'
},
{
name: 'IP-Whitelisting Admin',
description: 'Admin-Zugang nur von definierten IP-Bereichen',
status: 'planned',
},
]
},
{
id: 'availability',
title: 'Verfuegbarkeit & Belastbarkeit',
article: 'Art. 32 Abs. 1 lit. b',
description: 'Faehigkeit, Verfuegbarkeit und Belastbarkeit der Systeme sicherzustellen',
measures: [
{
name: 'Automatische Backups',
description: 'Taegliche inkrementelle Backups, woechentliche Vollbackups',
status: 'implemented',
evidence: 'restic + cron Jobs',
lastReview: '2024-11-15'
},
{
name: 'Disaster Recovery Plan',
description: 'Dokumentierter Wiederherstellungsplan mit RTO < 4h',
status: 'partial',
evidence: 'DR-Dokumentation in Arbeit',
},
{
name: 'Health Monitoring',
description: 'Prometheus + Grafana fuer System-Monitoring',
status: 'implemented',
evidence: 'Monitoring Stack deployed',
lastReview: '2024-12-01'
},
{
name: 'Rate Limiting',
description: 'API Rate Limiting zum Schutz vor DDoS',
status: 'implemented',
evidence: 'Nginx Rate Limit Config',
lastReview: '2024-12-01'
},
]
},
{
id: 'restore',
title: 'Wiederherstellung',
article: 'Art. 32 Abs. 1 lit. c',
description: 'Rasche Wiederherstellung nach physischem oder technischem Zwischenfall',
measures: [
{
name: 'Backup-Restore-Tests',
description: 'Quartalsweise Tests der Backup-Wiederherstellung',
status: 'partial',
evidence: 'Letzter Test: 2024-10-15',
},
{
name: 'Dokumentierte Recovery-Prozeduren',
description: 'Schritt-fuer-Schritt Anleitungen fuer verschiedene Szenarien',
status: 'implemented',
evidence: 'docs/disaster-recovery/',
lastReview: '2024-11-01'
},
{
name: 'Redundante Datenhaltung',
description: 'Datenbank-Replikation auf zweitem Server',
status: 'planned',
},
]
},
{
id: 'review',
title: 'Regelmaessige Ueberpruefung',
article: 'Art. 32 Abs. 1 lit. d',
description: 'Verfahren zur regelmaessigen Ueberpruefung, Bewertung und Evaluierung',
measures: [
{
name: 'Security Audits',
description: 'Jaehrliche externe Security-Audits',
status: 'implemented',
evidence: 'Letzter Audit: 2024-09',
lastReview: '2024-09-15'
},
{
name: 'Penetration Tests',
description: 'Jaehrliche Penetrationstests durch externen Dienstleister',
status: 'partial',
evidence: 'Naechster Test geplant: Q1 2025',
},
{
name: 'Vulnerability Scanning',
description: 'Woechentliche automatisierte Schwachstellen-Scans',
status: 'implemented',
evidence: 'GitHub Dependabot + Trivy',
lastReview: '2024-12-01'
},
{
name: 'TOM-Review',
description: 'Jaehrliche Ueberpruefung aller TOMs',
status: 'implemented',
evidence: 'Diese Seite',
lastReview: '2024-12-01'
},
]
},
{
id: 'logging',
title: 'Protokollierung & Audit-Trail',
article: 'Art. 32 Abs. 2',
description: 'Nachweis der Einhaltung durch Protokollierung',
measures: [
{
name: 'Zugriffs-Logging',
description: 'Protokollierung aller Zugriffe auf personenbezogene Daten',
status: 'implemented',
evidence: 'consent-service Audit-Logs',
lastReview: '2024-12-01'
},
{
name: 'Aenderungs-Historie',
description: 'Vollstaendige Historie aller Datenänderungen',
status: 'implemented',
evidence: 'audit_logs Tabelle in PostgreSQL',
lastReview: '2024-12-01'
},
{
name: 'Admin-Aktionen-Log',
description: 'Protokollierung aller administrativen Aktionen',
status: 'implemented',
evidence: 'Admin Action Logger',
lastReview: '2024-12-01'
},
{
name: 'Log-Aufbewahrung',
description: 'Logs werden 2 Jahre aufbewahrt, dann automatisch geloescht',
status: 'implemented',
evidence: 'Log Retention Policy',
lastReview: '2024-11-01'
},
]
},
]
const getStatusBadge = (status: string) => {
switch (status) {
case 'implemented':
return <span className="px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800">Umgesetzt</span>
case 'partial':
return <span className="px-2 py-1 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800">Teilweise</span>
case 'planned':
return <span className="px-2 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800">Geplant</span>
case 'not_applicable':
return <span className="px-2 py-1 rounded-full text-xs font-medium bg-slate-100 text-slate-600">N/A</span>
default:
return null
}
}
const calculateCategoryScore = (category: TOMCategory) => {
const total = category.measures.length
const implemented = category.measures.filter(m => m.status === 'implemented').length
const partial = category.measures.filter(m => m.status === 'partial').length
return Math.round(((implemented + partial * 0.5) / total) * 100)
}
const calculateOverallScore = () => {
let totalMeasures = 0
let implementedScore = 0
tomCategories.forEach(cat => {
cat.measures.forEach(m => {
totalMeasures++
if (m.status === 'implemented') implementedScore += 1
else if (m.status === 'partial') implementedScore += 0.5
})
})
return Math.round((implementedScore / totalMeasures) * 100)
}
return (
<div>
<PagePurpose
title="Technische & Organisatorische Massnahmen (TOMs)"
purpose="Dokumentation aller Sicherheitsmassnahmen gemaess Art. 32 DSGVO. Diese Seite dient als Nachweis fuer Auditoren und den DSB."
audience={['DSB', 'IT-Sicherheit', 'Auditoren', 'Geschaeftsfuehrung']}
gdprArticles={['Art. 32 (Sicherheit der Verarbeitung)']}
architecture={{
services: ['consent-service (Go)', 'backend (Python)', 'Nginx', 'PostgreSQL'],
databases: ['PostgreSQL (verschluesselt)', 'MinIO (Backups)'],
}}
relatedPages={[
{ name: 'DSMS', href: '/compliance/dsms', description: 'Datenschutz-Management-System' },
{ name: 'Security', href: '/infrastructure/security', description: 'DevSecOps Dashboard' },
{ name: 'Audit', href: '/compliance/audit', description: 'Audit-Dokumentation' },
]}
collapsible={true}
defaultCollapsed={true}
/>
{/* Overall Score */}
<div className="bg-white rounded-xl border border-slate-200 p-6 mb-6">
<div className="flex items-center justify-between">
<div>
<h2 className="text-lg font-semibold text-slate-900">TOM-Umsetzungsgrad</h2>
<p className="text-sm text-slate-500 mt-1">Gesamtfortschritt aller technischen und organisatorischen Massnahmen</p>
</div>
<div className="text-right">
<div className={`text-4xl font-bold ${calculateOverallScore() >= 80 ? 'text-green-600' : calculateOverallScore() >= 50 ? 'text-yellow-600' : 'text-red-600'}`}>
{calculateOverallScore()}%
</div>
<div className="text-sm text-slate-500">Umgesetzt</div>
</div>
</div>
<div className="mt-4 h-3 bg-slate-100 rounded-full overflow-hidden">
<div
className={`h-full transition-all ${calculateOverallScore() >= 80 ? 'bg-green-500' : calculateOverallScore() >= 50 ? 'bg-yellow-500' : 'bg-red-500'}`}
style={{ width: `${calculateOverallScore()}%` }}
/>
</div>
</div>
{/* TOM Categories */}
<div className="space-y-4">
{tomCategories.map((category) => (
<div key={category.id} className="bg-white rounded-xl border border-slate-200 overflow-hidden">
<button
onClick={() => setExpandedCategory(expandedCategory === category.id ? null : category.id)}
className="w-full px-6 py-4 flex items-center justify-between hover:bg-slate-50 transition-colors"
>
<div className="flex items-center gap-4">
<div className={`w-12 h-12 rounded-lg flex items-center justify-center text-lg font-bold ${
calculateCategoryScore(category) >= 80 ? 'bg-green-100 text-green-700' :
calculateCategoryScore(category) >= 50 ? 'bg-yellow-100 text-yellow-700' :
'bg-red-100 text-red-700'
}`}>
{calculateCategoryScore(category)}%
</div>
<div className="text-left">
<h3 className="font-semibold text-slate-900">{category.title}</h3>
<p className="text-sm text-slate-500">{category.article} - {category.description}</p>
</div>
</div>
<svg
className={`w-5 h-5 text-slate-400 transition-transform ${expandedCategory === category.id ? 'rotate-180' : ''}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</button>
{expandedCategory === category.id && (
<div className="px-6 pb-6 border-t border-slate-100">
<div className="mt-4 space-y-3">
{category.measures.map((measure, idx) => (
<div key={idx} className="p-4 bg-slate-50 rounded-lg">
<div className="flex items-start justify-between mb-2">
<h4 className="font-medium text-slate-900">{measure.name}</h4>
{getStatusBadge(measure.status)}
</div>
<p className="text-sm text-slate-600 mb-2">{measure.description}</p>
{(measure.evidence || measure.lastReview) && (
<div className="flex flex-wrap gap-4 text-xs text-slate-500">
{measure.evidence && (
<span>Nachweis: <span className="font-mono">{measure.evidence}</span></span>
)}
{measure.lastReview && (
<span>Letzte Pruefung: {measure.lastReview}</span>
)}
</div>
)}
</div>
))}
</div>
</div>
)}
</div>
))}
</div>
{/* Info */}
<div className="mt-6 bg-purple-50 border border-purple-200 rounded-xl p-4">
<div className="flex gap-3">
<svg className="w-5 h-5 text-purple-600 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<div>
<h4 className="font-semibold text-purple-900">Dokumentationspflicht</h4>
<p className="text-sm text-purple-800 mt-1">
Gemaess Art. 32 Abs. 1 DSGVO muessen geeignete technische und organisatorische Massnahmen
implementiert werden, um ein dem Risiko angemessenes Schutzniveau zu gewaehrleisten.
Diese Dokumentation dient als Nachweis fuer Aufsichtsbehoerden.
</p>
</div>
</div>
</div>
</div>
)
}