import { PrismaClient } from '@prisma/client'; import bcrypt from 'bcryptjs'; import crypto from 'crypto'; const prisma = new PrismaClient(); async function main() { console.log('Seeding database...'); // ==================== PERMISSIONS ==================== // Ressourcen mit ihren erlaubten Aktionen const resourcePermissions: Record = { // Haupt-Ressourcen (CRUD) customers: ['create', 'read', 'update', 'delete'], contracts: ['create', 'read', 'update', 'delete'], users: ['create', 'read', 'update', 'delete'], platforms: ['create', 'read', 'update', 'delete'], providers: ['create', 'read', 'update', 'delete'], tariffs: ['create', 'read', 'update', 'delete'], // Konfiguration (CRUD) 'cancellation-periods': ['create', 'read', 'update', 'delete'], 'contract-durations': ['create', 'read', 'update', 'delete'], 'contract-categories': ['create', 'read', 'update', 'delete'], 'email-providers': ['create', 'read', 'update', 'delete'], // Einstellungen (nur lesen/ändern) settings: ['read', 'update'], // Spezial-Permissions developer: ['access'], emails: ['delete'], // DSGVO & Audit audit: ['read', 'export', 'admin'], gdpr: ['export', 'delete', 'admin'], }; const permissions: { resource: string; action: string }[] = []; for (const [resource, actions] of Object.entries(resourcePermissions)) { for (const action of actions) { permissions.push({ resource, action }); } } for (const perm of permissions) { await prisma.permission.upsert({ where: { resource_action: perm }, update: {}, create: perm, }); } console.log(`Permissions created (${permissions.length} total)`); // Get all permissions const allPermissions = await prisma.permission.findMany(); const customerReadPerm = allPermissions.find( (p) => p.resource === 'customers' && p.action === 'read' ); const contractReadPerm = allPermissions.find( (p) => p.resource === 'contracts' && p.action === 'read' ); const platformReadPerm = allPermissions.find( (p) => p.resource === 'platforms' && p.action === 'read' ); const providerReadPerm = allPermissions.find( (p) => p.resource === 'providers' && p.action === 'read' ); // Helper: Sync permissions for a role (adds missing, removes excess) async function syncRolePermissions(roleId: number, permissionIds: number[]) { const existing = await prisma.rolePermission.findMany({ where: { roleId }, select: { permissionId: true }, }); const existingIds = new Set(existing.map((e) => e.permissionId)); const targetIds = new Set(permissionIds); // Add missing permissions const missing = permissionIds.filter((id) => !existingIds.has(id)); if (missing.length > 0) { await prisma.rolePermission.createMany({ data: missing.map((permissionId) => ({ roleId, permissionId })), skipDuplicates: true, }); console.log(` → ${missing.length} Permissions hinzugefügt für Rolle #${roleId}`); } // Remove excess permissions const excess = existing.filter((e) => !targetIds.has(e.permissionId)).map((e) => e.permissionId); if (excess.length > 0) { await prisma.rolePermission.deleteMany({ where: { roleId, permissionId: { in: excess } }, }); console.log(` → ${excess.length} Permissions entfernt für Rolle #${roleId}`); } } // Create roles // Admin - all permissions EXCEPT developer:access and audit/gdpr (controlled separately via checkboxes) const adminPermissions = allPermissions.filter( (p) => !(p.resource === 'developer' && p.action === 'access') && p.resource !== 'audit' && p.resource !== 'gdpr' ); const adminRole = await prisma.role.upsert({ where: { name: 'Admin' }, update: {}, create: { name: 'Admin', description: 'Voller Zugriff auf alle Funktionen', permissions: { create: adminPermissions.map((p) => ({ permissionId: p.id })), }, }, }); await syncRolePermissions(adminRole.id, adminPermissions.map((p) => p.id)); // Developer - ALL permissions (developer:access + alles andere) const developerPermissions = allPermissions; const developerRole = await prisma.role.upsert({ where: { name: 'Developer' }, update: {}, create: { name: 'Developer', description: 'Voller Zugriff inkl. Entwickler-Tools', permissions: { create: developerPermissions.map((p) => ({ permissionId: p.id })), }, }, }); await syncRolePermissions(developerRole.id, developerPermissions.map((p) => p.id)); // DSGVO - audit and gdpr permissions (hidden role, controlled via hasGdprAccess) const gdprPermissions = allPermissions.filter( (p) => p.resource === 'audit' || p.resource === 'gdpr' ); const gdprRole = await prisma.role.upsert({ where: { name: 'DSGVO' }, update: {}, create: { name: 'DSGVO', description: 'DSGVO-Zugriff: Audit-Logs und Datenschutz-Verwaltung', permissions: { create: gdprPermissions.map((p) => ({ permissionId: p.id })), }, }, }); await syncRolePermissions(gdprRole.id, gdprPermissions.map((p) => p.id)); // Employee - full access to customers, contracts, read access to lookup tables const employeePermIds = allPermissions .filter( (p) => p.resource === 'customers' || p.resource === 'contracts' || // Read-only Zugriff auf Stammdaten und Konfiguration (p.action === 'read' && [ 'platforms', 'providers', 'tariffs', 'cancellation-periods', 'contract-durations', 'contract-categories', ].includes(p.resource)) ) .map((p) => p.id); const employeeRole = await prisma.role.upsert({ where: { name: 'Mitarbeiter' }, update: {}, create: { name: 'Mitarbeiter', description: 'Kann Kunden und Verträge verwalten', permissions: { create: employeePermIds.map((id) => ({ permissionId: id })), }, }, }); await syncRolePermissions(employeeRole.id, employeePermIds); // Read-only employee - read access to main entities and lookup tables const readOnlyResources = [ 'customers', 'contracts', 'platforms', 'providers', 'tariffs', 'cancellation-periods', 'contract-durations', 'contract-categories', ]; const readOnlyPermIds = allPermissions .filter((p) => p.action === 'read' && readOnlyResources.includes(p.resource)) .map((p) => p.id); const readOnlyRole = await prisma.role.upsert({ where: { name: 'Mitarbeiter (Nur-Lesen)' }, update: {}, create: { name: 'Mitarbeiter (Nur-Lesen)', description: 'Kann nur lesen, keine Änderungen', permissions: { create: readOnlyPermIds.map((id) => ({ permissionId: id })), }, }, }); await syncRolePermissions(readOnlyRole.id, readOnlyPermIds); // Customer role - read own data only (handled in middleware) const customerRole = await prisma.role.upsert({ where: { name: 'Kunde' }, update: {}, create: { name: 'Kunde', description: 'Kann nur eigene Daten lesen', permissions: { create: readOnlyPermIds.map((id) => ({ permissionId: id })), }, }, }); await syncRolePermissions(customerRole.id, readOnlyPermIds); console.log('Roles created'); // Create admin user const hashedPassword = await bcrypt.hash('admin', 10); const adminUser = await prisma.user.upsert({ where: { email: 'admin@admin.com' }, update: {}, create: { email: 'admin@admin.com', password: hashedPassword, firstName: 'Admin', lastName: 'User', roles: { create: [{ roleId: adminRole.id }], }, }, }); console.log('Admin user created: admin@admin.com / admin'); // Create some sales platforms const platforms = ['Moon Fachhandel', 'Verivox', 'Check24', 'Eigenvermittlung']; for (const name of platforms) { await prisma.salesPlatform.upsert({ where: { name }, update: {}, create: { name, isActive: true }, }); } console.log('Sales platforms created'); // ==================== STANDARD PROVIDERS ==================== const providers = [ { name: 'Vodafone', portalUrl: 'https://www.vodafone.de/meinvodafone/account/login', usernameFieldName: 'username', passwordFieldName: 'password', }, { name: 'Klarmobil', portalUrl: 'https://www.klarmobil.de/login', usernameFieldName: 'username', passwordFieldName: 'password', }, { name: 'Otelo', portalUrl: 'https://www.otelo.de/mein-otelo/login', usernameFieldName: 'username', passwordFieldName: 'password', }, { name: 'Congstar', portalUrl: 'https://www.congstar.de/login/', usernameFieldName: 'username', passwordFieldName: 'password', }, { name: 'Telekom', portalUrl: 'https://www.telekom.de/kundencenter/startseite', usernameFieldName: 'username', passwordFieldName: 'password', }, { name: 'O2', portalUrl: 'https://www.o2online.de/ecare/selfcare', usernameFieldName: 'username', passwordFieldName: 'password', }, { name: '1&1', portalUrl: 'https://control-center.1und1.de/', usernameFieldName: 'username', passwordFieldName: 'password', }, ]; for (const provider of providers) { await prisma.provider.upsert({ where: { name: provider.name }, update: { portalUrl: provider.portalUrl, usernameFieldName: provider.usernameFieldName, passwordFieldName: provider.passwordFieldName, }, create: { ...provider, isActive: true }, }); } console.log('Providers created'); // Create contract categories (matching existing enum values) const contractCategories = [ { code: 'ELECTRICITY', name: 'Strom', icon: 'Zap', color: '#FFC107', sortOrder: 1 }, { code: 'GAS', name: 'Gas', icon: 'Flame', color: '#FF5722', sortOrder: 2 }, { code: 'DSL', name: 'DSL', icon: 'Wifi', color: '#2196F3', sortOrder: 3 }, { code: 'FIBER', name: 'Glasfaser', icon: 'Cable', color: '#9C27B0', sortOrder: 4 }, { code: 'CABLE', name: 'Kabel Internet (Coax)', icon: 'Cable', color: '#00BCD4', sortOrder: 5 }, { code: 'MOBILE', name: 'Mobilfunk', icon: 'Smartphone', color: '#4CAF50', sortOrder: 6 }, { code: 'TV', name: 'TV', icon: 'Tv', color: '#E91E63', sortOrder: 7 }, { code: 'CAR_INSURANCE', name: 'KFZ-Versicherung', icon: 'Car', color: '#607D8B', sortOrder: 8 }, ]; for (const category of contractCategories) { await prisma.contractCategory.upsert({ where: { code: category.code }, update: { name: category.name, icon: category.icon, color: category.color, sortOrder: category.sortOrder }, create: category, }); } console.log('Contract categories created'); // ==================== CANCELLATION PERIODS ==================== const cancellationPeriods = [ { code: '14D', description: '14 Tage' }, { code: '1M', description: '1 Monat' }, { code: '2M', description: '2 Monate' }, { code: '3M', description: '3 Monate' }, { code: '6M', description: '6 Monate' }, { code: '12M', description: '12 Monate' }, { code: '1W', description: '1 Woche' }, { code: '2W', description: '2 Wochen' }, { code: '4W', description: '4 Wochen' }, { code: '6W', description: '6 Wochen' }, ]; for (const period of cancellationPeriods) { await prisma.cancellationPeriod.upsert({ where: { code: period.code }, update: { description: period.description }, create: period, }); } console.log('Cancellation periods created'); // ==================== CONTRACT DURATIONS ==================== const contractDurations = [ { code: '1M', description: '1 Monat' }, { code: '3M', description: '3 Monate' }, { code: '6M', description: '6 Monate' }, { code: '12M', description: '12 Monate' }, { code: '24M', description: '24 Monate' }, { code: '36M', description: '36 Monate' }, { code: '1J', description: '1 Jahr' }, { code: '2J', description: '2 Jahre' }, { code: '3J', description: '3 Jahre' }, { code: '4J', description: '4 Jahre' }, { code: '5J', description: '5 Jahre' }, { code: 'UNBEFRISTET', description: 'Unbefristet' }, ]; for (const duration of contractDurations) { await prisma.contractDuration.upsert({ where: { code: duration.code }, update: { description: duration.description }, create: duration, }); } console.log('Contract durations created'); // ==================== APP SETTINGS ==================== const appSettings = [ // Cockpit-Einstellungen (Fristen-Ampel) { key: 'deadlineCriticalDays', value: '14' }, // Rot: <= 14 Tage { key: 'deadlineWarningDays', value: '42' }, // Gelb: <= 42 Tage { key: 'deadlineOkDays', value: '90' }, // Grün: <= 90 Tage // Allgemeine Einstellungen { key: 'companyName', value: 'OpenCRM' }, { key: 'defaultEmailDomain', value: 'stressfrei-wechseln.de' }, ]; for (const setting of appSettings) { await prisma.appSetting.upsert({ where: { key: setting.key }, update: {}, // Bestehende Werte nicht überschreiben create: setting, }); } console.log('App settings created'); // ==================== AUDIT RETENTION POLICIES (DSGVO) ==================== // Standard-Policy (ohne Sensitivity) const existingDefault = await prisma.auditRetentionPolicy.findFirst({ where: { resourceType: '*', sensitivity: null }, }); if (!existingDefault) { await prisma.auditRetentionPolicy.create({ data: { resourceType: '*', sensitivity: null, retentionDays: 3650, // 10 Jahre description: 'Standard-Aufbewahrungsfrist', legalBasis: 'AO §147, HGB §257', }, }); } // Spezifische Policies mit Sensitivity const specificPolicies = [ { resourceType: 'Authentication', sensitivity: 'CRITICAL' as const, retentionDays: 730, // 2 Jahre description: 'Login-Versuche und Authentifizierung', legalBasis: 'Sicherheitsanforderungen', }, { resourceType: 'Customer', sensitivity: 'HIGH' as const, retentionDays: 3650, // 10 Jahre description: 'Kundendaten-Zugriffe', legalBasis: 'Steuerrecht (AO §147)', }, { resourceType: 'Contract', sensitivity: 'MEDIUM' as const, retentionDays: 3650, // 10 Jahre description: 'Vertragsdaten-Zugriffe', legalBasis: 'Steuerrecht (AO §147)', }, { resourceType: 'AppSetting', sensitivity: 'LOW' as const, retentionDays: 1095, // 3 Jahre description: 'Allgemeine Einstellungen', legalBasis: 'Verjährungsfrist (BGB §195)', }, ]; for (const policy of specificPolicies) { await prisma.auditRetentionPolicy.upsert({ where: { resourceType_sensitivity: { resourceType: policy.resourceType, sensitivity: policy.sensitivity, }, }, update: { retentionDays: policy.retentionDays, description: policy.description, legalBasis: policy.legalBasis, }, create: policy, }); } console.log('Audit retention policies created'); // ==================== CONSENT HASH FÜR BESTEHENDE KUNDEN ==================== const customersWithoutHash = await prisma.customer.findMany({ where: { consentHash: null }, select: { id: true }, }); for (const c of customersWithoutHash) { await prisma.customer.update({ where: { id: c.id }, data: { consentHash: crypto.randomUUID() }, }); } if (customersWithoutHash.length > 0) { console.log(`ConsentHash für ${customersWithoutHash.length} Kunden generiert`); } console.log('Seeding completed!'); } main() .catch((e) => { console.error(e); process.exit(1); }) .finally(async () => { await prisma.$disconnect(); });