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:
214
admin-v2/app/api/sdk/v1/documents/upload/route.ts
Normal file
214
admin-v2/app/api/sdk/v1/documents/upload/route.ts
Normal file
@@ -0,0 +1,214 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
|
||||
// Types
|
||||
interface ExtractedSection {
|
||||
title: string
|
||||
content: string
|
||||
type?: string
|
||||
}
|
||||
|
||||
interface ExtractedContent {
|
||||
title?: string
|
||||
version?: string
|
||||
lastModified?: string
|
||||
sections?: ExtractedSection[]
|
||||
metadata?: Record<string, string>
|
||||
}
|
||||
|
||||
interface UploadResponse {
|
||||
success: boolean
|
||||
documentId: string
|
||||
filename: string
|
||||
documentType: string
|
||||
extractedVersion?: string
|
||||
extractedContent?: ExtractedContent
|
||||
suggestedNextVersion?: string
|
||||
}
|
||||
|
||||
// Helper: Detect version from filename
|
||||
function detectVersionFromFilename(filename: string): string | undefined {
|
||||
const patterns = [
|
||||
/[vV](\d+(?:\.\d+)*)/,
|
||||
/version[_-]?(\d+(?:\.\d+)*)/i,
|
||||
/[_-]v?(\d+\.\d+(?:\.\d+)?)[_-]/,
|
||||
]
|
||||
|
||||
for (const pattern of patterns) {
|
||||
const match = filename.match(pattern)
|
||||
if (match) {
|
||||
return match[1]
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
// Helper: Suggest next version
|
||||
function suggestNextVersion(currentVersion?: string): string {
|
||||
if (!currentVersion) return '1.0'
|
||||
|
||||
const parts = currentVersion.split('.').map(Number)
|
||||
if (parts.length >= 2) {
|
||||
parts[parts.length - 1] += 1
|
||||
} else {
|
||||
parts.push(1)
|
||||
}
|
||||
return parts.join('.')
|
||||
}
|
||||
|
||||
// Helper: Extract content from PDF/DOCX (simplified - would need proper libraries in production)
|
||||
async function extractDocumentContent(
|
||||
_file: File,
|
||||
documentType: string
|
||||
): Promise<ExtractedContent> {
|
||||
// In production, this would use libraries like:
|
||||
// - pdf-parse for PDFs
|
||||
// - mammoth for DOCX
|
||||
// For now, return mock extracted content based on document type
|
||||
|
||||
const mockContentByType: Record<string, ExtractedContent> = {
|
||||
tom: {
|
||||
title: 'Technische und Organisatorische Maßnahmen',
|
||||
sections: [
|
||||
{ title: 'Vertraulichkeit', content: 'Zugangskontrollen, Zugriffsbeschränkungen...', type: 'category' },
|
||||
{ title: 'Integrität', content: 'Eingabekontrollen, Änderungsprotokolle...', type: 'category' },
|
||||
{ title: 'Verfügbarkeit', content: 'Backup-Strategien, Disaster Recovery...', type: 'category' },
|
||||
{ title: 'Belastbarkeit', content: 'Redundanz, Lasttests...', type: 'category' },
|
||||
],
|
||||
metadata: {
|
||||
lastReview: new Date().toISOString(),
|
||||
responsible: 'Datenschutzbeauftragter',
|
||||
},
|
||||
},
|
||||
dsfa: {
|
||||
title: 'Datenschutz-Folgenabschätzung',
|
||||
sections: [
|
||||
{ title: 'Beschreibung der Verarbeitung', content: 'Systematische Beschreibung...', type: 'section' },
|
||||
{ title: 'Erforderlichkeit und Verhältnismäßigkeit', content: 'Bewertung...', type: 'section' },
|
||||
{ title: 'Risiken für Betroffene', content: 'Risikoanalyse...', type: 'section' },
|
||||
{ title: 'Abhilfemaßnahmen', content: 'Geplante Maßnahmen...', type: 'section' },
|
||||
],
|
||||
},
|
||||
vvt: {
|
||||
title: 'Verzeichnis von Verarbeitungstätigkeiten',
|
||||
sections: [
|
||||
{ title: 'Verantwortlicher', content: 'Name und Kontaktdaten...', type: 'field' },
|
||||
{ title: 'Verarbeitungszwecke', content: 'Liste der Zwecke...', type: 'list' },
|
||||
{ title: 'Datenkategorien', content: 'Personenbezogene Daten...', type: 'list' },
|
||||
{ title: 'Empfängerkategorien', content: 'Interne und externe Empfänger...', type: 'list' },
|
||||
],
|
||||
},
|
||||
loeschfristen: {
|
||||
title: 'Löschkonzept und Aufbewahrungsfristen',
|
||||
sections: [
|
||||
{ title: 'Personalakten', content: '10 Jahre nach Ausscheiden', type: 'retention' },
|
||||
{ title: 'Kundendaten', content: '3 Jahre nach letzter Aktivität', type: 'retention' },
|
||||
{ title: 'Buchhaltungsbelege', content: '10 Jahre (HGB)', type: 'retention' },
|
||||
{ title: 'Bewerbungsunterlagen', content: '6 Monate nach Absage', type: 'retention' },
|
||||
],
|
||||
},
|
||||
consent: {
|
||||
title: 'Einwilligungserklärungen',
|
||||
sections: [
|
||||
{ title: 'Newsletter-Einwilligung', content: 'Vorlage für Newsletter...', type: 'template' },
|
||||
{ title: 'Marketing-Einwilligung', content: 'Vorlage für Marketing...', type: 'template' },
|
||||
],
|
||||
},
|
||||
policy: {
|
||||
title: 'Datenschutzrichtlinie',
|
||||
sections: [
|
||||
{ title: 'Geltungsbereich', content: 'Diese Richtlinie gilt für...', type: 'section' },
|
||||
{ title: 'Verantwortlichkeiten', content: 'Rollen und Pflichten...', type: 'section' },
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
return mockContentByType[documentType] || {
|
||||
title: 'Unbekanntes Dokument',
|
||||
sections: [],
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const formData = await request.formData()
|
||||
const file = formData.get('file') as File | null
|
||||
const documentType = formData.get('documentType') as string || 'custom'
|
||||
const sessionId = formData.get('sessionId') as string || 'default'
|
||||
|
||||
if (!file) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Keine Datei hochgeladen' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// Validate file type
|
||||
const allowedTypes = [
|
||||
'application/pdf',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'application/msword',
|
||||
]
|
||||
|
||||
if (!allowedTypes.includes(file.type)) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Ungültiger Dateityp. Erlaubt: PDF, DOCX, DOC' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// Generate document ID
|
||||
const documentId = `doc-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
|
||||
|
||||
// Extract version from filename
|
||||
const extractedVersion = detectVersionFromFilename(file.name)
|
||||
|
||||
// Extract content (in production, this would parse the actual file)
|
||||
const extractedContent = await extractDocumentContent(file, documentType)
|
||||
|
||||
// Add version to extracted content if found
|
||||
if (extractedVersion) {
|
||||
extractedContent.version = extractedVersion
|
||||
}
|
||||
|
||||
// Store file (in production, save to MinIO/S3)
|
||||
// For now, we just process and return metadata
|
||||
console.log(`[SDK Documents] Uploaded: ${file.name} (${file.size} bytes) for session ${sessionId}`)
|
||||
|
||||
const response: UploadResponse = {
|
||||
success: true,
|
||||
documentId,
|
||||
filename: file.name,
|
||||
documentType,
|
||||
extractedVersion,
|
||||
extractedContent,
|
||||
suggestedNextVersion: suggestNextVersion(extractedVersion),
|
||||
}
|
||||
|
||||
return NextResponse.json(response)
|
||||
} catch (error) {
|
||||
console.error('[SDK Documents] Upload error:', error)
|
||||
return NextResponse.json(
|
||||
{ error: 'Upload fehlgeschlagen' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const sessionId = searchParams.get('sessionId')
|
||||
|
||||
if (!sessionId) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Session ID erforderlich' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// In production, fetch uploaded documents from storage
|
||||
// For now, return empty list
|
||||
return NextResponse.json({
|
||||
uploads: [],
|
||||
sessionId,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user