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,186 @@
/**
* API Route: Privacy Policy Generator
*
* POST - Generiert eine Datenschutzerklaerung aus dem Datenpunktkatalog
*/
import { NextRequest, NextResponse } from 'next/server'
import {
CompanyInfo,
DataPoint,
SupportedLanguage,
ExportFormat,
GeneratedPrivacyPolicy,
} from '@/lib/sdk/einwilligungen/types'
import {
generatePrivacyPolicy,
generatePrivacyPolicySections,
} from '@/lib/sdk/einwilligungen/generator/privacy-policy'
import {
PREDEFINED_DATA_POINTS,
getDataPointById,
} from '@/lib/sdk/einwilligungen/catalog/loader'
// In-Memory Storage fuer generierte Policies
const policyStorage = new Map<string, GeneratedPrivacyPolicy>()
/**
* POST /api/sdk/v1/einwilligungen/privacy-policy/generate
*
* Generiert eine Datenschutzerklaerung
*
* Body:
* - dataPointIds: string[] - IDs der zu inkludierenden Datenpunkte
* - companyInfo: CompanyInfo - Firmeninformationen
* - language: 'de' | 'en' - Sprache
* - format: 'HTML' | 'MARKDOWN' | 'PDF' | 'DOCX' - Ausgabeformat
* - customDataPoints?: DataPoint[] - Kundenspezifische Datenpunkte
*/
export async function POST(request: NextRequest) {
try {
const tenantId = request.headers.get('X-Tenant-ID')
if (!tenantId) {
return NextResponse.json(
{ error: 'Tenant ID required' },
{ status: 400 }
)
}
const body = await request.json()
const {
dataPointIds,
companyInfo,
language = 'de',
format = 'HTML',
customDataPoints = [],
} = body
// Validierung
if (!companyInfo || !companyInfo.name || !companyInfo.address || !companyInfo.email) {
return NextResponse.json(
{ error: 'Company info (name, address, email) required' },
{ status: 400 }
)
}
if (!dataPointIds || !Array.isArray(dataPointIds) || dataPointIds.length === 0) {
return NextResponse.json(
{ error: 'At least one data point ID required' },
{ status: 400 }
)
}
// Validiere Sprache
const validLanguages: SupportedLanguage[] = ['de', 'en']
if (!validLanguages.includes(language)) {
return NextResponse.json(
{ error: 'Invalid language. Must be "de" or "en"' },
{ status: 400 }
)
}
// Validiere Format
const validFormats: ExportFormat[] = ['HTML', 'MARKDOWN', 'PDF', 'DOCX']
if (!validFormats.includes(format)) {
return NextResponse.json(
{ error: 'Invalid format. Must be HTML, MARKDOWN, PDF, or DOCX' },
{ status: 400 }
)
}
// Sammle alle Datenpunkte
const allDataPoints: DataPoint[] = [
...PREDEFINED_DATA_POINTS,
...customDataPoints,
]
// Filtere nach ausgewaehlten IDs
const selectedDataPoints = dataPointIds
.map((id: string) => allDataPoints.find((dp) => dp.id === id))
.filter((dp): dp is DataPoint => dp !== undefined)
if (selectedDataPoints.length === 0) {
return NextResponse.json(
{ error: 'No valid data points found for the provided IDs' },
{ status: 400 }
)
}
// Generiere die Privacy Policy
const policy = generatePrivacyPolicy(
tenantId,
selectedDataPoints,
companyInfo as CompanyInfo,
language as SupportedLanguage,
format as ExportFormat
)
// Speichere fuer spaeteres Abrufen
policyStorage.set(policy.id, policy)
// Fuer PDF/DOCX: Nur Metadaten zurueckgeben, Download separat
if (format === 'PDF' || format === 'DOCX') {
return NextResponse.json({
id: policy.id,
tenantId: policy.tenantId,
language: policy.language,
format: policy.format,
generatedAt: policy.generatedAt,
version: policy.version,
sections: policy.sections.map((s) => ({
id: s.id,
title: s.title,
order: s.order,
})),
downloadUrl: `/api/sdk/v1/einwilligungen/privacy-policy/${policy.id}/download`,
})
}
// Fuer HTML/Markdown: Vollstaendige Policy zurueckgeben
return NextResponse.json(policy)
} catch (error) {
console.error('Error generating privacy policy:', error)
return NextResponse.json(
{ error: 'Failed to generate privacy policy' },
{ status: 500 }
)
}
}
/**
* GET /api/sdk/v1/einwilligungen/privacy-policy/generate
*
* Liefert eine Vorschau der Abschnitte ohne vollstaendige Generierung
*/
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url)
const language = (searchParams.get('language') as SupportedLanguage) || 'de'
// Liefere die Standard-Abschnittsstruktur
const sections = [
{ id: 'controller', order: 1, title: { de: '1. Verantwortlicher', en: '1. Data Controller' } },
{ id: 'data-collection', order: 2, title: { de: '2. Erhobene personenbezogene Daten', en: '2. Personal Data We Collect' } },
{ id: 'purposes', order: 3, title: { de: '3. Zwecke der Datenverarbeitung', en: '3. Purposes of Data Processing' } },
{ id: 'legal-basis', order: 4, title: { de: '4. Rechtsgrundlagen der Verarbeitung', en: '4. Legal Basis for Processing' } },
{ id: 'recipients', order: 5, title: { de: '5. Empfaenger und Datenweitergabe', en: '5. Recipients and Data Sharing' } },
{ id: 'retention', order: 6, title: { de: '6. Speicherdauer', en: '6. Data Retention' } },
{ id: 'rights', order: 7, title: { de: '7. Ihre Rechte als betroffene Person', en: '7. Your Rights as a Data Subject' } },
{ id: 'cookies', order: 8, title: { de: '8. Cookies und aehnliche Technologien', en: '8. Cookies and Similar Technologies' } },
{ id: 'changes', order: 9, title: { de: '9. Aenderungen dieser Datenschutzerklaerung', en: '9. Changes to this Privacy Policy' } },
]
return NextResponse.json({
sections,
availableLanguages: ['de', 'en'],
availableFormats: ['HTML', 'MARKDOWN', 'PDF', 'DOCX'],
})
} catch (error) {
console.error('Error fetching sections:', error)
return NextResponse.json(
{ error: 'Failed to fetch sections' },
{ status: 500 }
)
}
}