import { PrismaClient, Prisma } from '@prisma/client'; import { setBeforeValues, setAfterValues } from '../middleware/auditContext.js'; // Modelle die für Before/After-Tracking relevant sind const AUDITED_MODELS = [ 'Customer', 'Contract', 'Address', 'BankCard', 'IdentityDocument', 'User', 'Meter', 'MeterReading', 'StressfreiEmail', 'Provider', 'Tariff', 'ContractCategory', 'AppSetting', 'CustomerConsent', ]; // Sensible Felder die aus dem Audit-Log gefiltert werden const SENSITIVE_FIELDS = [ 'password', 'passwordHash', 'portalPasswordHash', 'portalPasswordEncrypted', 'emailPasswordEncrypted', 'internetPasswordEncrypted', 'sipPasswordEncrypted', 'pin', 'puk', 'apiKey', ]; /** * Filtert sensible Felder aus einem Objekt */ function filterSensitiveFields(obj: Record): Record { const filtered: Record = {}; for (const [key, value] of Object.entries(obj)) { if (SENSITIVE_FIELDS.includes(key)) { filtered[key] = '[REDACTED]'; } else if (typeof value === 'object' && value !== null && !Array.isArray(value)) { filtered[key] = filterSensitiveFields(value as Record); } else { filtered[key] = value; } } return filtered; } /** * Prüft ob ein Model für Audit-Tracking relevant ist */ function isAuditedModel(model: string | undefined): boolean { return model !== undefined && AUDITED_MODELS.includes(model); } /** * Erstellt einen Prisma Client mit Audit-Middleware */ function createPrismaClient(): PrismaClient { const prisma = new PrismaClient(); // Middleware für Before/After-Tracking prisma.$use(async (params: Prisma.MiddlewareParams, next: (params: Prisma.MiddlewareParams) => Promise) => { const { model, action, args } = params; // Nur relevante Modelle und Aktionen tracken if (!isAuditedModel(model)) { return next(params); } // Update-Operationen: Vorherigen Stand abrufen if (action === 'update' || action === 'updateMany') { try { const modelDelegate = (prisma as unknown as Record Promise }>)[ model!.charAt(0).toLowerCase() + model!.slice(1) ]; if (modelDelegate && args?.where) { const before = await modelDelegate.findUnique({ where: args.where }); if (before) { setBeforeValues(filterSensitiveFields(before as Record)); } } } catch { // Fehler beim Abrufen des vorherigen Stands ignorieren } } // Delete-Operationen: Datensatz vor dem Löschen abrufen if (action === 'delete' || action === 'deleteMany') { try { const modelDelegate = (prisma as unknown as Record Promise }>)[ model!.charAt(0).toLowerCase() + model!.slice(1) ]; if (modelDelegate && args?.where) { const before = await modelDelegate.findUnique({ where: args.where }); if (before) { setBeforeValues(filterSensitiveFields(before as Record)); } } } catch { // Fehler beim Abrufen ignorieren } } // Operation ausführen const result = await next(params); // Nach Update/Create: Neuen Stand speichern if ((action === 'update' || action === 'create') && result) { setAfterValues(filterSensitiveFields(result as Record)); } return result; }); return prisma; } // Singleton-Instanz const globalForPrisma = globalThis as unknown as { prisma: PrismaClient | undefined }; export const prisma = globalForPrisma.prisma ?? createPrismaClient(); if (process.env.NODE_ENV !== 'production') { globalForPrisma.prisma = prisma; } export default prisma;