opencrm/backend/prisma/restore-data.ts

487 lines
13 KiB
TypeScript

/**
* 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<T>(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();
});