2c7a87ccd3
Erweitert das bestehende Factory-Defaults-Bundle um vier HTML-Standardtexte (Datenschutzerklärung, Impressum, Vollmacht-Vorlage, Website-Datenschutz) und ergänzt den bisherigen CLI-Only-Import um einen Upload-Pfad in der UI. Backend: - collectFactoryDefaults() zieht jetzt auch die Whitelist-AppSettings - exportFactoryDefaults() legt sie als app-settings/app-settings.json ins ZIP - importFactoryDefaults(buffer) liest die ZIP idempotent ein – upserts pro Kategorie, Whitelist-Filter für AppSettings, Anti-Zip-Slip durch basename beim PDF-Lookup - POST /api/factory-defaults/import (multer memoryStorage, max 50 MB, settings:update) - seed-factory-defaults.ts (CLI) gleichermaßen um seedAppSettings() erweitert Frontend: - Import-Card in FactoryDefaults.tsx: Datei-Upload statt CLI-Anleitung - Erfolgs-Box mit Counts pro Kategorie + Warnings (z.B. fehlende PDFs im ZIP) - Preview zeigt jetzt auch die Anzahl HTML-Templates Live verifiziert: Round-Trip Export → DELETE privacyPolicyHtml → Import → Wert (13.6 KB) wieder vollständig hergestellt, Audit-Log zeigt EXPORT + UPDATE-Eintrag mit Detail-Counts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
102 lines
3.6 KiB
TypeScript
102 lines
3.6 KiB
TypeScript
import { Response } from 'express';
|
|
import { AuthRequest } from '../types/index.js';
|
|
import * as factoryDefaultsService from '../services/factoryDefaults.service.js';
|
|
import { createAuditLog } from '../services/audit.service.js';
|
|
|
|
/**
|
|
* Factory-Defaults als ZIP exportieren (Download).
|
|
*/
|
|
export async function exportFactoryDefaults(req: AuthRequest, res: Response) {
|
|
try {
|
|
const buffer = await factoryDefaultsService.exportFactoryDefaults();
|
|
|
|
const dateStr = new Date().toISOString().split('T')[0];
|
|
const filename = `factory-defaults-${dateStr}.zip`;
|
|
|
|
await createAuditLog({
|
|
userId: req.user?.userId,
|
|
userEmail: req.user?.email || 'unknown',
|
|
action: 'EXPORT',
|
|
resourceType: 'FactoryDefaults',
|
|
resourceId: dateStr,
|
|
resourceLabel: 'Factory-Defaults exportiert',
|
|
endpoint: req.path,
|
|
httpMethod: req.method,
|
|
ipAddress: req.socket.remoteAddress || 'unknown',
|
|
});
|
|
|
|
res.setHeader('Content-Type', 'application/zip');
|
|
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
|
|
res.setHeader('Content-Length', buffer.length);
|
|
res.send(buffer);
|
|
} catch (error) {
|
|
console.error('Fehler beim Factory-Defaults-Export:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: error instanceof Error ? error.message : 'Fehler beim Export',
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Kurze Übersicht was exportiert würde (für Frontend, ohne Download).
|
|
*/
|
|
export async function previewFactoryDefaults(req: AuthRequest, res: Response) {
|
|
try {
|
|
const data = await factoryDefaultsService.collectFactoryDefaults();
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
counts: {
|
|
providers: data.providers.length,
|
|
tariffs: data.providers.reduce((sum, p) => sum + p.tariffs.length, 0),
|
|
cancellationPeriods: data.cancellationPeriods.length,
|
|
contractDurations: data.contractDurations.length,
|
|
contractCategories: data.contractCategories.length,
|
|
pdfTemplates: data.pdfTemplates.length,
|
|
appSettings: data.appSettings.length,
|
|
},
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.error('Fehler beim Preview:', error);
|
|
res.status(500).json({ success: false, error: 'Fehler beim Laden' });
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Factory-Defaults aus ZIP importieren (Upload via multipart/form-data, Feld 'zip').
|
|
* Idempotent: bestehende Einträge werden per unique-Key aktualisiert, nichts wird gelöscht.
|
|
*/
|
|
export async function importFactoryDefaults(req: AuthRequest, res: Response) {
|
|
try {
|
|
const file = (req as any).file as Express.Multer.File | undefined;
|
|
if (!file || !file.buffer) {
|
|
return res.status(400).json({ success: false, error: 'Keine ZIP-Datei hochgeladen' });
|
|
}
|
|
|
|
const result = await factoryDefaultsService.importFactoryDefaults(file.buffer);
|
|
|
|
await createAuditLog({
|
|
userId: req.user?.userId,
|
|
userEmail: req.user?.email || 'unknown',
|
|
// 'UPDATE' weil Factory-Defaults DB-Records upserted; das Label nennt
|
|
// den Vorgang explizit als Import.
|
|
action: 'UPDATE',
|
|
resourceType: 'FactoryDefaults',
|
|
resourceLabel: `Factory-Defaults importiert: ${result.providers} Anbieter, ${result.tariffs} Tarife, ${result.pdfTemplates} PDF-Vorlagen, ${result.appSettings} HTML-Templates`,
|
|
endpoint: req.path,
|
|
httpMethod: req.method,
|
|
ipAddress: req.socket.remoteAddress || 'unknown',
|
|
});
|
|
|
|
res.json({ success: true, data: result });
|
|
} catch (error) {
|
|
console.error('Fehler beim Factory-Defaults-Import:', error);
|
|
res.status(400).json({
|
|
success: false,
|
|
error: error instanceof Error ? error.message : 'Fehler beim Import',
|
|
});
|
|
}
|
|
}
|