refactor(admin): split 9 more oversized lib/ files into focused modules
Files split by agents before rate limit: - dsr/api.ts (669 → barrel + helpers) - einwilligungen/context.tsx (669 → barrel + hooks/reducer) - export.ts (753 → barrel + domain exporters) - incidents/api.ts (845 → barrel + api-helpers) - tom-generator/context.tsx (720 → barrel + hooks/reducer) - vendor-compliance/context.tsx (1010 → 234 provider + hooks/reducer) - api-docs/endpoints.ts — partially split (3 domain files created) - academy/api.ts — partially split (helpers extracted) - whistleblower/api.ts — partially split (helpers extracted) next build passes. api-client.ts (885) deferred to next session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
240
admin-compliance/lib/sdk/export-zip.ts
Normal file
240
admin-compliance/lib/sdk/export-zip.ts
Normal file
@@ -0,0 +1,240 @@
|
||||
/**
|
||||
* SDK ZIP Export
|
||||
* Packages SDK state, documents, and a PDF report into a ZIP archive
|
||||
*/
|
||||
|
||||
import JSZip from 'jszip'
|
||||
import { SDKState, SDK_STEPS } from './types'
|
||||
import { ExportOptions, DEFAULT_OPTIONS, formatDate, exportToPDF } from './export-pdf'
|
||||
|
||||
// =============================================================================
|
||||
// ZIP EXPORT
|
||||
// =============================================================================
|
||||
|
||||
export async function exportToZIP(state: SDKState, options: ExportOptions = {}): Promise<Blob> {
|
||||
const opts = { ...DEFAULT_OPTIONS, ...options }
|
||||
const zip = new JSZip()
|
||||
|
||||
const rootFolder = zip.folder('ai-compliance-sdk-export')
|
||||
if (!rootFolder) throw new Error('Failed to create ZIP folder')
|
||||
|
||||
const phase1Folder = rootFolder.folder('phase1-assessment')
|
||||
const phase2Folder = rootFolder.folder('phase2-documents')
|
||||
const dataFolder = rootFolder.folder('data')
|
||||
|
||||
// Main State JSON
|
||||
if (opts.includeRawData && dataFolder) {
|
||||
dataFolder.file('state.json', JSON.stringify(state, null, 2))
|
||||
}
|
||||
|
||||
// README
|
||||
const readmeContent = `# AI Compliance SDK Export
|
||||
|
||||
Generated: ${formatDate(new Date())}
|
||||
Tenant: ${state.tenantId}
|
||||
Version: ${state.version}
|
||||
|
||||
## Folder Structure
|
||||
|
||||
- **phase1-assessment/**: Compliance Assessment Ergebnisse
|
||||
- use-cases.json: Alle Use Cases
|
||||
- risks.json: Identifizierte Risiken
|
||||
- controls.json: Definierte Controls
|
||||
- requirements.json: Compliance-Anforderungen
|
||||
|
||||
- **phase2-documents/**: Generierte Dokumente
|
||||
- dsfa.json: Datenschutz-Folgenabschaetzung
|
||||
- toms.json: Technische und organisatorische Massnahmen
|
||||
- vvt.json: Verarbeitungsverzeichnis
|
||||
- documents.json: Rechtliche Dokumente
|
||||
|
||||
- **data/**: Rohdaten
|
||||
- state.json: Kompletter SDK State
|
||||
|
||||
## Progress
|
||||
|
||||
Phase 1: ${SDK_STEPS.filter(s => s.phase === 1 && state.completedSteps.includes(s.id)).length}/${SDK_STEPS.filter(s => s.phase === 1).length} completed
|
||||
Phase 2: ${SDK_STEPS.filter(s => s.phase === 2 && state.completedSteps.includes(s.id)).length}/${SDK_STEPS.filter(s => s.phase === 2).length} completed
|
||||
|
||||
## Key Metrics
|
||||
|
||||
- Use Cases: ${state.useCases.length}
|
||||
- Risks: ${state.risks.length}
|
||||
- Controls: ${state.controls.length}
|
||||
- Requirements: ${state.requirements.length}
|
||||
- Evidence: ${state.evidence.length}
|
||||
`
|
||||
|
||||
rootFolder.file('README.md', readmeContent)
|
||||
|
||||
// Phase 1 Files
|
||||
if (phase1Folder) {
|
||||
phase1Folder.file('use-cases.json', JSON.stringify({
|
||||
exportedAt: new Date().toISOString(),
|
||||
count: state.useCases.length,
|
||||
useCases: state.useCases,
|
||||
}, null, 2))
|
||||
|
||||
phase1Folder.file('risks.json', JSON.stringify({
|
||||
exportedAt: new Date().toISOString(),
|
||||
count: state.risks.length,
|
||||
risks: state.risks,
|
||||
summary: {
|
||||
critical: state.risks.filter(r => r.severity === 'CRITICAL').length,
|
||||
high: state.risks.filter(r => r.severity === 'HIGH').length,
|
||||
medium: state.risks.filter(r => r.severity === 'MEDIUM').length,
|
||||
low: state.risks.filter(r => r.severity === 'LOW').length,
|
||||
},
|
||||
}, null, 2))
|
||||
|
||||
phase1Folder.file('controls.json', JSON.stringify({
|
||||
exportedAt: new Date().toISOString(),
|
||||
count: state.controls.length,
|
||||
controls: state.controls,
|
||||
}, null, 2))
|
||||
|
||||
phase1Folder.file('requirements.json', JSON.stringify({
|
||||
exportedAt: new Date().toISOString(),
|
||||
count: state.requirements.length,
|
||||
requirements: state.requirements,
|
||||
}, null, 2))
|
||||
|
||||
phase1Folder.file('modules.json', JSON.stringify({
|
||||
exportedAt: new Date().toISOString(),
|
||||
count: state.modules.length,
|
||||
modules: state.modules,
|
||||
}, null, 2))
|
||||
|
||||
if (opts.includeEvidence) {
|
||||
phase1Folder.file('evidence.json', JSON.stringify({
|
||||
exportedAt: new Date().toISOString(),
|
||||
count: state.evidence.length,
|
||||
evidence: state.evidence,
|
||||
}, null, 2))
|
||||
}
|
||||
|
||||
phase1Folder.file('checkpoints.json', JSON.stringify({
|
||||
exportedAt: new Date().toISOString(),
|
||||
checkpoints: state.checkpoints,
|
||||
}, null, 2))
|
||||
|
||||
if (state.screening) {
|
||||
phase1Folder.file('screening.json', JSON.stringify({
|
||||
exportedAt: new Date().toISOString(),
|
||||
screening: state.screening,
|
||||
}, null, 2))
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 2 Files
|
||||
if (phase2Folder) {
|
||||
if (state.dsfa) {
|
||||
phase2Folder.file('dsfa.json', JSON.stringify({
|
||||
exportedAt: new Date().toISOString(),
|
||||
dsfa: state.dsfa,
|
||||
}, null, 2))
|
||||
}
|
||||
|
||||
phase2Folder.file('toms.json', JSON.stringify({
|
||||
exportedAt: new Date().toISOString(),
|
||||
count: state.toms.length,
|
||||
toms: state.toms,
|
||||
}, null, 2))
|
||||
|
||||
phase2Folder.file('vvt.json', JSON.stringify({
|
||||
exportedAt: new Date().toISOString(),
|
||||
count: state.vvt.length,
|
||||
processingActivities: state.vvt,
|
||||
}, null, 2))
|
||||
|
||||
if (opts.includeDocuments) {
|
||||
phase2Folder.file('documents.json', JSON.stringify({
|
||||
exportedAt: new Date().toISOString(),
|
||||
count: state.documents.length,
|
||||
documents: state.documents,
|
||||
}, null, 2))
|
||||
}
|
||||
|
||||
if (state.cookieBanner) {
|
||||
phase2Folder.file('cookie-banner.json', JSON.stringify({
|
||||
exportedAt: new Date().toISOString(),
|
||||
config: state.cookieBanner,
|
||||
}, null, 2))
|
||||
}
|
||||
|
||||
phase2Folder.file('retention-policies.json', JSON.stringify({
|
||||
exportedAt: new Date().toISOString(),
|
||||
count: state.retentionPolicies.length,
|
||||
policies: state.retentionPolicies,
|
||||
}, null, 2))
|
||||
|
||||
if (state.aiActClassification) {
|
||||
phase2Folder.file('ai-act-classification.json', JSON.stringify({
|
||||
exportedAt: new Date().toISOString(),
|
||||
classification: state.aiActClassification,
|
||||
}, null, 2))
|
||||
}
|
||||
|
||||
phase2Folder.file('obligations.json', JSON.stringify({
|
||||
exportedAt: new Date().toISOString(),
|
||||
count: state.obligations.length,
|
||||
obligations: state.obligations,
|
||||
}, null, 2))
|
||||
|
||||
phase2Folder.file('consents.json', JSON.stringify({
|
||||
exportedAt: new Date().toISOString(),
|
||||
count: state.consents.length,
|
||||
consents: state.consents,
|
||||
}, null, 2))
|
||||
|
||||
if (state.dsrConfig) {
|
||||
phase2Folder.file('dsr-config.json', JSON.stringify({
|
||||
exportedAt: new Date().toISOString(),
|
||||
config: state.dsrConfig,
|
||||
}, null, 2))
|
||||
}
|
||||
|
||||
phase2Folder.file('escalation-workflows.json', JSON.stringify({
|
||||
exportedAt: new Date().toISOString(),
|
||||
count: state.escalationWorkflows.length,
|
||||
workflows: state.escalationWorkflows,
|
||||
}, null, 2))
|
||||
}
|
||||
|
||||
// Security Data
|
||||
if (dataFolder) {
|
||||
if (state.sbom) {
|
||||
dataFolder.file('sbom.json', JSON.stringify({
|
||||
exportedAt: new Date().toISOString(),
|
||||
sbom: state.sbom,
|
||||
}, null, 2))
|
||||
}
|
||||
|
||||
if (state.securityIssues.length > 0) {
|
||||
dataFolder.file('security-issues.json', JSON.stringify({
|
||||
exportedAt: new Date().toISOString(),
|
||||
count: state.securityIssues.length,
|
||||
issues: state.securityIssues,
|
||||
}, null, 2))
|
||||
}
|
||||
|
||||
if (state.securityBacklog.length > 0) {
|
||||
dataFolder.file('security-backlog.json', JSON.stringify({
|
||||
exportedAt: new Date().toISOString(),
|
||||
count: state.securityBacklog.length,
|
||||
backlog: state.securityBacklog,
|
||||
}, null, 2))
|
||||
}
|
||||
}
|
||||
|
||||
// Generate PDF and include in ZIP
|
||||
try {
|
||||
const pdfBlob = await exportToPDF(state, options)
|
||||
const pdfArrayBuffer = await pdfBlob.arrayBuffer()
|
||||
rootFolder.file('compliance-report.pdf', pdfArrayBuffer)
|
||||
} catch (error) {
|
||||
console.error('Failed to generate PDF for ZIP:', error)
|
||||
}
|
||||
|
||||
return zip.generateAsync({ type: 'blob', compression: 'DEFLATE' })
|
||||
}
|
||||
Reference in New Issue
Block a user