Backup/Restore: alle neuen Tabellen erfasst (43 Tabellen insgesamt)

Das Backup- und Restore-System kannte noch nicht alle Tabellen, die im Lauf
der letzten Wochen hinzugekommen sind. Kritischer Datenverlust im Ernstfall!

Neu im Backup + Restore:
- PdfTemplate (PDF-Auftragsvorlagen + Feldzuordnungen)
- ContractMeter (Zähler-Vertrag-Zuordnungen mit Zeiträumen)
- ContractDocument (flexible Vertragsdokumente: Auftragsformular, Lieferbestätigung ...)
- RepresentativeAuthorization (Vollmachten zwischen Kunden)
- CustomerConsent (DSGVO-Einwilligungen pro Kunde)
- DataDeletionRequest (DSGVO-Löschanfragen)
- EmailLog (SMTP-Sendeprotokoll)
- AuditRetentionPolicy (Aufbewahrungsfristen pro Ressourcentyp)
- AuditLog (vollständiges Änderungsprotokoll)

Außerdem:
- prisma/backup-data.ts: komplett neu strukturiert, korrekte Level-Hierarchie,
  nutzt aktuelles Schema (Provider statt EnergyProvider/TelecomProvider,
  InternetContractDetails statt TelecomContractDetails etc.)
- prisma/restore-data.ts: Boilerplate durch generische restoreTable()-Helper
  ersetzt – von 487 auf ~240 Zeilen
- backup.service.ts: neue Tabellen in createBackup, restoreOrder und
  deleteMany-Liste nachgetragen (Service bleibt sonst wie er ist)

Test-Backup erfolgreich: 4420 Datensätze in 37 aktiven Tabellen gesichert.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-23 16:53:26 +02:00
parent b8a3c0d11a
commit 51a25f8b0b
3 changed files with 330 additions and 398 deletions
+135 -1
View File
@@ -239,6 +239,16 @@ export async function createBackup(): Promise<BackupResult> {
{ name: 'Address', query: () => prisma.address.findMany() },
{ name: 'BankCard', query: () => prisma.bankCard.findMany() },
{ name: 'IdentityDocument', query: () => prisma.identityDocument.findMany() },
// Neue Tabellen
{ name: 'PdfTemplate', query: () => prisma.pdfTemplate.findMany() },
{ name: 'ContractMeter', query: () => prisma.contractMeter.findMany() },
{ name: 'ContractDocument', query: () => prisma.contractDocument.findMany() },
{ name: 'RepresentativeAuthorization', query: () => prisma.representativeAuthorization.findMany() },
{ name: 'CustomerConsent', query: () => prisma.customerConsent.findMany() },
{ name: 'DataDeletionRequest', query: () => prisma.dataDeletionRequest.findMany() },
{ name: 'EmailLog', query: () => prisma.emailLog.findMany() },
{ name: 'AuditRetentionPolicy', query: () => prisma.auditRetentionPolicy.findMany() },
{ name: 'AuditLog', query: () => prisma.auditLog.findMany() },
];
let totalRecords = 0;
@@ -297,6 +307,10 @@ export async function restoreBackup(backupName: string): Promise<RestoreResult>
// WICHTIG: Alle Tabellen vor dem Restore leeren, damit keine alten Daten übrig bleiben
console.log('[Restore] Lösche alle bestehenden Daten...');
// Logs & Audit zuerst (hängen an allem)
await prisma.auditLog.deleteMany({});
await prisma.emailLog.deleteMany({});
// Detail-Tabellen
await prisma.carInsuranceDetails.deleteMany({});
await prisma.tvContractDetails.deleteMany({});
@@ -309,12 +323,21 @@ export async function restoreBackup(backupName: string): Promise<RestoreResult>
await prisma.meterReading.deleteMany({});
await prisma.contractHistoryEntry.deleteMany({});
// Neue Contract-bezogene Tabellen
await prisma.contractDocument.deleteMany({});
await prisma.contractMeter.deleteMany({});
// E-Mail & Verträge
await prisma.cachedEmail.deleteMany({});
await prisma.contractTaskSubtask.deleteMany({});
await prisma.contractTask.deleteMany({});
await prisma.contract.deleteMany({});
// DSGVO + Vollmachten (abhängig von Customer)
await prisma.representativeAuthorization.deleteMany({});
await prisma.customerConsent.deleteMany({});
await prisma.dataDeletionRequest.deleteMany({});
// Kunden-bezogene Daten
await prisma.stressfreiEmail.deleteMany({});
await prisma.meter.deleteMany({});
@@ -328,7 +351,8 @@ export async function restoreBackup(backupName: string): Promise<RestoreResult>
await prisma.user.deleteMany({});
await prisma.customer.deleteMany({});
// Stammdaten
// Stammdaten & Kataloge
await prisma.pdfTemplate.deleteMany({});
await prisma.tariff.deleteMany({});
await prisma.provider.deleteMany({});
await prisma.rolePermission.deleteMany({});
@@ -340,6 +364,7 @@ export async function restoreBackup(backupName: string): Promise<RestoreResult>
await prisma.contractCategory.deleteMany({});
await prisma.emailProviderConfig.deleteMany({});
await prisma.appSetting.deleteMany({});
await prisma.auditRetentionPolicy.deleteMany({});
console.log('[Restore] Alle Daten gelöscht, starte Wiederherstellung...');
@@ -753,6 +778,115 @@ export async function restoreBackup(backupName: string): Promise<RestoreResult>
}
},
},
// Neue Tabellen
{
name: 'PdfTemplate',
restore: async (data: any[]) => {
for (const item of data) {
await prisma.pdfTemplate.upsert({
where: { id: item.id },
update: convertDates(item),
create: convertDates(item),
});
}
},
},
{
name: 'ContractMeter',
restore: async (data: any[]) => {
for (const item of data) {
await prisma.contractMeter.upsert({
where: { id: item.id },
update: convertDates(item),
create: convertDates(item),
});
}
},
},
{
name: 'ContractDocument',
restore: async (data: any[]) => {
for (const item of data) {
await prisma.contractDocument.upsert({
where: { id: item.id },
update: convertDates(item),
create: convertDates(item),
});
}
},
},
{
name: 'RepresentativeAuthorization',
restore: async (data: any[]) => {
for (const item of data) {
await prisma.representativeAuthorization.upsert({
where: { id: item.id },
update: convertDates(item),
create: convertDates(item),
});
}
},
},
{
name: 'CustomerConsent',
restore: async (data: any[]) => {
for (const item of data) {
await prisma.customerConsent.upsert({
where: { id: item.id },
update: convertDates(item),
create: convertDates(item),
});
}
},
},
{
name: 'DataDeletionRequest',
restore: async (data: any[]) => {
for (const item of data) {
await prisma.dataDeletionRequest.upsert({
where: { id: item.id },
update: convertDates(item),
create: convertDates(item),
});
}
},
},
{
name: 'EmailLog',
restore: async (data: any[]) => {
for (const item of data) {
await prisma.emailLog.upsert({
where: { id: item.id },
update: convertDates(item),
create: convertDates(item),
});
}
},
},
{
name: 'AuditRetentionPolicy',
restore: async (data: any[]) => {
for (const item of data) {
await prisma.auditRetentionPolicy.upsert({
where: { id: item.id },
update: convertDates(item),
create: convertDates(item),
});
}
},
},
{
name: 'AuditLog',
restore: async (data: any[]) => {
for (const item of data) {
await prisma.auditLog.upsert({
where: { id: item.id },
update: convertDates(item),
create: convertDates(item),
});
}
},
},
];
let totalRestored = 0;