- AGENTS.md und README.md auf Craftvia umgeschrieben (Regeln, Andockpunkte, Stack mit Garage, tsx-Tests, Gate, Demo-Logins) - ISMS-Dokumente entfernt (SPEC, Prototypen, Lane-Prompts, Übergaben, Konzepte Incidents/Framework); Fundament-Doku (Deploy, Sicherheit, Backup, Identity) bleibt - Fundament-Tests an Craftvia-Rollen/Branding angepasst, Resttreffer certvia/isms in Skripten und Kommentaren bereinigt Gate: prisma generate, migrate status, tsc, lint, build, 22/22 Testskripte grün. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
80 lines
3.4 KiB
TypeScript
80 lines
3.4 KiB
TypeScript
// 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.
|
|
// `report:approve_team`, `lotse:use`) 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());
|