Files
breakpilot-compliance/admin-compliance/app/api/sdk/v1/documents/upload/route.ts
Benjamin Boenisch 4435e7ea0a Initial commit: breakpilot-compliance - Compliance SDK Platform
Services: Admin-Compliance, Backend-Compliance,
AI-Compliance-SDK, Consent-SDK, Developer-Portal,
PCA-Platform, DSMS

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 23:47:28 +01:00

215 lines
6.7 KiB
TypeScript

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,
})
}