Factory-Defaults: Export + Import von Stammdaten-Katalogen
Ein neues System um Stammdaten-Kataloge zwischen Installationen zu teilen – explizit ohne Kundendaten, Verträge oder Einstellungen. **Was wird exportiert:** - Anbieter + zugehörige Tarife - Kündigungsfristen - Vertragslaufzeiten - Vertragskategorien - PDF-Auftragsvorlagen (JSON + PDF-Dateien + Feldzuordnungen) **Was NICHT:** - Kundendaten, Verträge, Dokumente, Emails, SMTP-Einstellungen → dafür gibt es den Datenbank-Backup **Neue Einstellungsseite /settings/factory-defaults:** - Zeigt Anzahl pro Kategorie (Anbieter, Tarife, Fristen, …) - "Exportieren"-Button lädt ZIP herunter (manifest.json + JSONs + PDFs) - Import-Anleitung inline **Import-Script:** - `npm run seed:defaults` (tsx scripts/seed-factory-defaults.ts) - Liest alle JSON-Dateien aus backend/factory-defaults/*/*.json - Merged mehrere Dateien automatisch pro Kategorie (unique-key gewinnt zuletzt) - Upsertet idempotent → kann mehrfach ausgeführt werden - Kopiert PDF-Vorlagen aus factory-defaults/pdf-templates/ nach uploads/pdf-templates/ - Alte PDF-Dateien werden beim Re-Import entsorgt Backend: - services/factoryDefaults.service.ts: collectFactoryDefaults() + exportFactoryDefaults() - controllers/factoryDefaults.controller.ts: preview + export - routes/factoryDefaults.routes.ts: GET /api/factory-defaults/preview + /export - scripts/seed-factory-defaults.ts: CLI-Import-Script - .gitignore: factory-defaults/* außer .gitkeep und README.md Frontend: - pages/settings/FactoryDefaults.tsx: Übersicht + Export-Button - Settings-Karte „Factory-Defaults" im System-Abschnitt Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
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,
|
||||
},
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Preview:', error);
|
||||
res.status(500).json({ success: false, error: 'Fehler beim Laden' });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user