// Rollen-Rechte-Sync (Wartung): gleicht die Rollen→Rechte-Zuordnung ALLER bestehenden // Mandanten mit dem aktuellen Rechtekatalog (ROLE_DEFS) ab. Nötig, weil Rechte bei der // Provisionierung in die DB geschrieben werden — neu hinzugekommene Rechte (z. B. // `onboarding:use`, `validate_objects`) fehlen bestehenden Mandanten sonst, bis dieses // Skript läuft. Rein additiv (entfernt keine bestehenden Zuordnungen). // // Lauf (lokal): npx tsx scripts/sync-role-permissions.ts // Lauf (Deploy): im Container/Coolify mit gesetzter DATABASE_URL ausführen. // WICHTIG: Betroffene Nutzer müssen sich danach neu einloggen (Rechte werden im JWT // beim Login aufgelöst). import "dotenv/config"; // lädt DATABASE_URL aus .env beim lokalen `npx tsx`-Lauf import { PrismaClient } from "@prisma/client"; import { PrismaPg } from "@prisma/adapter-pg"; import { PERMISSIONS, ROLE_DEFS } from "../src/server/rbac"; const prisma = new PrismaClient({ adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }) }); async function main() { // 1. Globalen Rechtekatalog sicherstellen. for (const key of PERMISSIONS) { await prisma.permission.upsert({ where: { key }, update: {}, create: { key } }); } const permByKey = new Map((await prisma.permission.findMany()).map((p) => [p.key, p.id])); // 2. Fehlende ROLE_DEFS-Rollen je Mandant anlegen (z. B. neue Rolle „pm"). const tenants = await prisma.tenant.findMany({ select: { id: true } }); let createdRoles = 0; for (const tenant of tenants) { for (const [key, def] of Object.entries(ROLE_DEFS)) { const existing = await prisma.role.findUnique({ where: { tenantId_key: { tenantId: tenant.id, key } } }); if (!existing) { await prisma.role.create({ data: { tenantId: tenant.id, key, name: def.name } }); createdRoles++; console.log(` ${tenant.id}: Rolle „${key}" angelegt`); } } } // 3. Alle Rollen (mandantenübergreifend) gegen ROLE_DEFS abgleichen (Rechte + Name). const roles = await prisma.role.findMany({ include: { rolePermissions: true } }); let addedTotal = 0; let touchedRoles = 0; for (const role of roles) { const def = ROLE_DEFS[role.key as keyof typeof ROLE_DEFS]; if (!def) continue; // unbekannte/manuelle Rolle unangetastet lassen const have = new Set(role.rolePermissions.map((rp) => rp.permissionId)); const missing = def.permissions.filter((k) => { const id = permByKey.get(k); return id && !have.has(id); }); if (missing.length === 0) continue; for (const key of missing) { const permissionId = permByKey.get(key)!; await prisma.rolePermission.upsert({ where: { roleId_permissionId: { roleId: role.id, permissionId } }, update: {}, create: { roleId: role.id, permissionId }, }); } // Rollenname ebenfalls aktualisieren (falls in ROLE_DEFS geändert). if (role.name !== def.name) await prisma.role.update({ where: { id: role.id }, data: { name: def.name } }); addedTotal += missing.length; touchedRoles++; console.log(` ${role.tenantId}/${role.key}: +${missing.length} Recht(e) [${missing.join(", ")}]`); } console.log(`\nFertig: ${createdRoles} Rolle(n) neu angelegt, ${addedTotal} Zuordnung(en) über ${touchedRoles} Rolle(n) ergänzt. Betroffene Nutzer bitte neu einloggen.`); } main() .catch((e) => { console.error("Sync fehlgeschlagen:", e.message); process.exit(1); }) .finally(() => prisma.$disconnect());