Phase 1 — Python (klausur-service): 5 monoliths → 36 files - dsfa_corpus_ingestion.py (1,828 LOC → 5 files) - cv_ocr_engines.py (2,102 LOC → 7 files) - cv_layout.py (3,653 LOC → 10 files) - vocab_worksheet_api.py (2,783 LOC → 8 files) - grid_build_core.py (1,958 LOC → 6 files) Phase 2 — Go (edu-search-service, school-service): 8 monoliths → 19 files - staff_crawler.go (1,402 → 4), policy/store.go (1,168 → 3) - policy_handlers.go (700 → 2), repository.go (684 → 2) - search.go (592 → 2), ai_extraction_handlers.go (554 → 2) - seed_data.go (591 → 2), grade_service.go (646 → 2) Phase 3 — TypeScript (admin-lehrer): 45 monoliths → 220+ files - sdk/types.ts (2,108 → 16 domain files) - ai/rag/page.tsx (2,686 → 14 files) - 22 page.tsx files split into _components/ + _hooks/ - 11 component files split into sub-components - 10 SDK data catalogs added to loc-exceptions - Deleted dead backup index_original.ts (4,899 LOC) All original public APIs preserved via re-export facades. Zero new errors: Python imports verified, Go builds clean, TypeScript tsc --noEmit shows only pre-existing errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
279 lines
8.6 KiB
TypeScript
279 lines
8.6 KiB
TypeScript
/**
|
|
* ZIP Export
|
|
* Generates a structured ZIP archive with all compliance data
|
|
*/
|
|
|
|
import JSZip from 'jszip'
|
|
import { SDKState, SDK_STEPS } from './types'
|
|
import { ExportOptions, DEFAULT_OPTIONS } from './export-types'
|
|
import { formatDate } from './export-pdf-helpers'
|
|
import { exportToPDF } from './export-pdf'
|
|
|
|
export async function exportToZIP(state: SDKState, options: ExportOptions = {}): Promise<Blob> {
|
|
const opts = { ...DEFAULT_OPTIONS, ...options }
|
|
const zip = new JSZip()
|
|
|
|
// Create folder structure
|
|
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) {
|
|
// Use Cases
|
|
phase1Folder.file('use-cases.json', JSON.stringify({
|
|
exportedAt: new Date().toISOString(),
|
|
count: state.useCases.length,
|
|
useCases: state.useCases,
|
|
}, null, 2))
|
|
|
|
// Risks
|
|
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))
|
|
|
|
// Controls
|
|
phase1Folder.file('controls.json', JSON.stringify({
|
|
exportedAt: new Date().toISOString(),
|
|
count: state.controls.length,
|
|
controls: state.controls,
|
|
}, null, 2))
|
|
|
|
// Requirements
|
|
phase1Folder.file('requirements.json', JSON.stringify({
|
|
exportedAt: new Date().toISOString(),
|
|
count: state.requirements.length,
|
|
requirements: state.requirements,
|
|
}, null, 2))
|
|
|
|
// Modules
|
|
phase1Folder.file('modules.json', JSON.stringify({
|
|
exportedAt: new Date().toISOString(),
|
|
count: state.modules.length,
|
|
modules: state.modules,
|
|
}, null, 2))
|
|
|
|
// Evidence
|
|
if (opts.includeEvidence) {
|
|
phase1Folder.file('evidence.json', JSON.stringify({
|
|
exportedAt: new Date().toISOString(),
|
|
count: state.evidence.length,
|
|
evidence: state.evidence,
|
|
}, null, 2))
|
|
}
|
|
|
|
// Checkpoints
|
|
phase1Folder.file('checkpoints.json', JSON.stringify({
|
|
exportedAt: new Date().toISOString(),
|
|
checkpoints: state.checkpoints,
|
|
}, null, 2))
|
|
|
|
// Screening
|
|
if (state.screening) {
|
|
phase1Folder.file('screening.json', JSON.stringify({
|
|
exportedAt: new Date().toISOString(),
|
|
screening: state.screening,
|
|
}, null, 2))
|
|
}
|
|
}
|
|
|
|
// ==========================================================================
|
|
// Phase 2 Files
|
|
// ==========================================================================
|
|
|
|
if (phase2Folder) {
|
|
// DSFA
|
|
if (state.dsfa) {
|
|
phase2Folder.file('dsfa.json', JSON.stringify({
|
|
exportedAt: new Date().toISOString(),
|
|
dsfa: state.dsfa,
|
|
}, null, 2))
|
|
}
|
|
|
|
// TOMs
|
|
phase2Folder.file('toms.json', JSON.stringify({
|
|
exportedAt: new Date().toISOString(),
|
|
count: state.toms.length,
|
|
toms: state.toms,
|
|
}, null, 2))
|
|
|
|
// VVT (Processing Activities)
|
|
phase2Folder.file('vvt.json', JSON.stringify({
|
|
exportedAt: new Date().toISOString(),
|
|
count: state.vvt.length,
|
|
processingActivities: state.vvt,
|
|
}, null, 2))
|
|
|
|
// Legal Documents
|
|
if (opts.includeDocuments) {
|
|
phase2Folder.file('documents.json', JSON.stringify({
|
|
exportedAt: new Date().toISOString(),
|
|
count: state.documents.length,
|
|
documents: state.documents,
|
|
}, null, 2))
|
|
}
|
|
|
|
// Cookie Banner Config
|
|
if (state.cookieBanner) {
|
|
phase2Folder.file('cookie-banner.json', JSON.stringify({
|
|
exportedAt: new Date().toISOString(),
|
|
config: state.cookieBanner,
|
|
}, null, 2))
|
|
}
|
|
|
|
// Retention Policies
|
|
phase2Folder.file('retention-policies.json', JSON.stringify({
|
|
exportedAt: new Date().toISOString(),
|
|
count: state.retentionPolicies.length,
|
|
policies: state.retentionPolicies,
|
|
}, null, 2))
|
|
|
|
// AI Act Classification
|
|
if (state.aiActClassification) {
|
|
phase2Folder.file('ai-act-classification.json', JSON.stringify({
|
|
exportedAt: new Date().toISOString(),
|
|
classification: state.aiActClassification,
|
|
}, null, 2))
|
|
}
|
|
|
|
// Obligations
|
|
phase2Folder.file('obligations.json', JSON.stringify({
|
|
exportedAt: new Date().toISOString(),
|
|
count: state.obligations.length,
|
|
obligations: state.obligations,
|
|
}, null, 2))
|
|
|
|
// Consent Records
|
|
phase2Folder.file('consents.json', JSON.stringify({
|
|
exportedAt: new Date().toISOString(),
|
|
count: state.consents.length,
|
|
consents: state.consents,
|
|
}, null, 2))
|
|
|
|
// DSR Config
|
|
if (state.dsrConfig) {
|
|
phase2Folder.file('dsr-config.json', JSON.stringify({
|
|
exportedAt: new Date().toISOString(),
|
|
config: state.dsrConfig,
|
|
}, null, 2))
|
|
}
|
|
|
|
// Escalation Workflows
|
|
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)
|
|
// Continue without PDF
|
|
}
|
|
|
|
// Generate ZIP
|
|
return zip.generateAsync({ type: 'blob', compression: 'DEFLATE' })
|
|
}
|