/** * Datenbank-Restore Script * * Stellt Daten aus einem JSON-Backup wieder her. * * Verwendung: * npx ts-node prisma/restore-data.ts [backup-ordner] * * Beispiele: * npx ts-node prisma/restore-data.ts # Letztes Backup * npx ts-node prisma/restore-data.ts 2025-01-31_14-30-00 # Bestimmtes Backup * * WICHTIG: Führe vorher 'npx prisma migrate deploy' oder 'npx prisma db push' aus! */ import { PrismaClient } from '@prisma/client'; import * as fs from 'fs'; import * as path from 'path'; const prisma = new PrismaClient(); // Hilfsfunktion: JSON-Datei lesen function readJsonFile(filePath: string): T[] { if (!fs.existsSync(filePath)) { return []; } const content = fs.readFileSync(filePath, 'utf-8'); return JSON.parse(content); } // Hilfsfunktion: Datum-Strings zu Date-Objekten konvertieren function convertDates(obj: any): any { if (obj === null || obj === undefined) return obj; if (typeof obj === 'string') { // ISO-Datumsformat erkennen if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(obj)) { return new Date(obj); } return obj; } if (Array.isArray(obj)) { return obj.map(convertDates); } if (typeof obj === 'object') { const result: any = {}; for (const [key, value] of Object.entries(obj)) { result[key] = convertDates(value); } return result; } return obj; } async function main() { // Backup-Ordner bestimmen const backupsDir = path.join(__dirname, 'backups'); let backupName = process.argv[2]; if (!backupName) { // Neuestes Backup finden if (!fs.existsSync(backupsDir)) { console.error('❌ Kein Backup-Ordner gefunden!'); process.exit(1); } const backups = fs.readdirSync(backupsDir) .filter(f => fs.statSync(path.join(backupsDir, f)).isDirectory()) .sort() .reverse(); if (backups.length === 0) { console.error('❌ Keine Backups gefunden!'); process.exit(1); } backupName = backups[0]; console.log(`📦 Verwende neuestes Backup: ${backupName}`); } const backupDir = path.join(backupsDir, backupName); if (!fs.existsSync(backupDir)) { console.error(`❌ Backup-Ordner nicht gefunden: ${backupDir}`); process.exit(1); } // Backup-Info lesen const infoPath = path.join(backupDir, '_backup-info.json'); if (fs.existsSync(infoPath)) { const info = JSON.parse(fs.readFileSync(infoPath, 'utf-8')); console.log(`\n📅 Backup vom: ${new Date(info.timestamp).toLocaleString('de-DE')}`); console.log(`📊 ${info.totalRecords} Datensätze in ${info.tables.filter((t: any) => t.count > 0).length} Tabellen\n`); } console.log(`🔄 Starte Wiederherstellung aus: ${backupDir}\n`); // Foreign Key Checks deaktivieren für MySQL await prisma.$executeRawUnsafe('SET FOREIGN_KEY_CHECKS = 0'); try { // Tabellen in Abhängigkeitsreihenfolge wiederherstellen const restoreOrder = [ // Level 0: Keine Abhängigkeiten { name: 'Permission', restore: async (data: any[]) => { for (const item of data) { await prisma.permission.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, { name: 'Role', restore: async (data: any[]) => { for (const item of data) { await prisma.role.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, { name: 'SalesPlatform', restore: async (data: any[]) => { for (const item of data) { await prisma.salesPlatform.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, { name: 'ContractCategory', restore: async (data: any[]) => { for (const item of data) { await prisma.contractCategory.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, { name: 'CancellationPeriod', restore: async (data: any[]) => { for (const item of data) { await prisma.cancellationPeriod.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, { name: 'ContractDuration', restore: async (data: any[]) => { for (const item of data) { await prisma.contractDuration.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, { name: 'AppSetting', restore: async (data: any[]) => { for (const item of data) { await prisma.appSetting.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, { name: 'EmailProviderConfig', restore: async (data: any[]) => { for (const item of data) { await prisma.emailProviderConfig.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, { name: 'EnergyProvider', restore: async (data: any[]) => { for (const item of data) { await prisma.energyProvider.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, { name: 'TelecomProvider', restore: async (data: any[]) => { for (const item of data) { await prisma.telecomProvider.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, // Level 1 { name: 'RolePermission', restore: async (data: any[]) => { for (const item of data) { await prisma.rolePermission.upsert({ where: { roleId_permissionId: { roleId: item.roleId, permissionId: item.permissionId } }, update: {}, create: convertDates(item), }); } }, }, { name: 'User', restore: async (data: any[]) => { for (const item of data) { await prisma.user.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, { name: 'Customer', restore: async (data: any[]) => { for (const item of data) { await prisma.customer.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, { name: 'Tariff', restore: async (data: any[]) => { for (const item of data) { await prisma.tariff.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, // Level 2 { name: 'UserRole', restore: async (data: any[]) => { for (const item of data) { await prisma.userRole.upsert({ where: { userId_roleId: { userId: item.userId, roleId: item.roleId } }, update: {}, create: convertDates(item), }); } }, }, { name: 'CustomerRepresentative', restore: async (data: any[]) => { for (const item of data) { await prisma.customerRepresentative.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, { name: 'StressfreiEmail', restore: async (data: any[]) => { for (const item of data) { await prisma.stressfreiEmail.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, { name: 'Contract', restore: async (data: any[]) => { for (const item of data) { await prisma.contract.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, { name: 'Meter', restore: async (data: any[]) => { for (const item of data) { await prisma.meter.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, // Level 3 { name: 'CachedEmail', restore: async (data: any[]) => { for (const item of data) { await prisma.cachedEmail.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, { name: 'ContractTask', restore: async (data: any[]) => { for (const item of data) { await prisma.contractTask.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, { name: 'MeterReading', restore: async (data: any[]) => { for (const item of data) { await prisma.meterReading.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, { name: 'ContractNote', restore: async (data: any[]) => { for (const item of data) { await prisma.contractNote.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), }); } }, }, // Level 4 { name: 'ContractTaskSubtask', restore: async (data: any[]) => { for (const item of data) { await prisma.contractTaskSubtask.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, // Vertragsdetails { name: 'EnergyContractDetails', restore: async (data: any[]) => { for (const item of data) { await prisma.energyContractDetails.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, { name: 'TelecomContractDetails', restore: async (data: any[]) => { for (const item of data) { await prisma.telecomContractDetails.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, { name: 'CarInsuranceDetails', restore: async (data: any[]) => { for (const item of data) { await prisma.carInsuranceDetails.upsert({ where: { id: item.id }, update: convertDates(item), create: convertDates(item), }); } }, }, ]; let totalRestored = 0; for (const table of restoreOrder) { const filePath = path.join(backupDir, `${table.name}.json`); const data = readJsonFile(filePath); if (data.length === 0) { console.log(`⚪ ${table.name}: Keine Daten`); continue; } try { await table.restore(data); totalRestored += data.length; console.log(`✅ ${table.name}: ${data.length} Einträge wiederhergestellt`); } catch (error: any) { console.log(`⚠️ ${table.name}: Fehler - ${error.message?.slice(0, 80)}`); } } console.log(`\n✅ Wiederherstellung abgeschlossen!`); console.log(` 📊 ${totalRestored} Datensätze wiederhergestellt\n`); } finally { // Foreign Key Checks wieder aktivieren await prisma.$executeRawUnsafe('SET FOREIGN_KEY_CHECKS = 1'); } } main() .catch((e) => { console.error('❌ Wiederherstellung fehlgeschlagen:', e); process.exit(1); }) .finally(async () => { await prisma.$disconnect(); });