Unveränderter Stand von certvia/dev (a48c5fb) plus Craftvia-Spezifikation und Brandbook unter docs/craftvia/. ISMS-Module werden im Folgecommit entfernt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
62 lines
2.9 KiB
TypeScript
62 lines
2.9 KiB
TypeScript
import "dotenv/config";
|
||
import { prisma } from "../src/server/db";
|
||
|
||
/**
|
||
* Backfill 5×5-Risikokriterien für Bestandsmandanten: ergänzt die 5. Eintritts-
|
||
* wahrscheinlichkeits-Stufe und die 5. Schadensstufe je Dimension, falls noch nicht
|
||
* vorhanden. Die Bewertung (Formular/Matrix/Score) war schon 5×5 — nur die Kriterien-
|
||
* daten waren 4-stufig geseedet. Idempotent (mehrfach ausführbar).
|
||
*
|
||
* npx tsx scripts/backfill-risk-5x5.ts # alle Mandanten
|
||
* TENANT_SLUG=gefim npx tsx scripts/backfill-risk-5x5.ts # nur einer
|
||
*/
|
||
|
||
// Sinnvolle Default-Texte für Stufe 5 der Standard-Schadensdimensionen (Abgleich per Name).
|
||
const LEVEL5: Record<string, string> = {
|
||
"Gesetzes-/Vertragsverstöße": "existenzbedrohende Rechtsfolgen (Lizenzentzug/Haftung)",
|
||
"Datenschutz": "besonders schützenswerte Daten in großem Umfang / existenzielle Betroffenheit",
|
||
"Persönliche Unversehrtheit": "Lebensgefahr für viele / Todesfolge",
|
||
"Aufgabenerfüllung": "vollständiger, dauerhafter Ausfall der Organisation",
|
||
"Innen-/Außenwirkung (Reputation)": "existenzbedrohender, dauerhafter Reputationsverlust",
|
||
"Störungs-/Ausfallzeit": "> 1 Monat / dauerhaft",
|
||
"Finanzielle Auswirkungen": "existenzbedrohend (> 1 Mio €)",
|
||
};
|
||
const FALLBACK5 = "(bitte definieren)";
|
||
|
||
async function main() {
|
||
const slug = process.env.TENANT_SLUG?.trim().toLowerCase();
|
||
const tenants = await prisma.tenant.findMany({ where: slug ? { slug } : {}, select: { id: true, slug: true } });
|
||
if (!tenants.length) { console.log("Keine Mandanten gefunden."); return; }
|
||
|
||
for (const t of tenants) {
|
||
let ew5 = 0, dim5 = 0;
|
||
|
||
// EW-Stufe 5 ergänzen, falls nicht vorhanden
|
||
const hasEw5 = await prisma.riskEwLevel.findFirst({ where: { tenantId: t.id, level: 5 } });
|
||
if (!hasEw5) {
|
||
// Nur ergänzen, wenn überhaupt EW-Stufen existieren (sonst provisioniert der Mandant sie neu).
|
||
const anyEw = await prisma.riskEwLevel.count({ where: { tenantId: t.id } });
|
||
if (anyEw > 0) {
|
||
await prisma.riskEwLevel.create({ data: { tenantId: t.id, level: 5, label: "Nahezu sicher", definition: "Ereignis ist praktisch dauerhaft gegeben oder tritt ständig ein." } });
|
||
ew5 = 1;
|
||
}
|
||
}
|
||
|
||
// Schadensdimensionen: je Dimension "5" ergänzen, falls fehlt
|
||
const dims = await prisma.riskDamageDimension.findMany({ where: { tenantId: t.id } });
|
||
for (const d of dims) {
|
||
const levels = (d.levels ?? {}) as Record<string, string>;
|
||
if (levels["5"] == null || levels["5"] === "") {
|
||
levels["5"] = LEVEL5[d.name] ?? FALLBACK5;
|
||
await prisma.riskDamageDimension.update({ where: { id: d.id }, data: { levels } });
|
||
dim5++;
|
||
}
|
||
}
|
||
|
||
console.log(`Mandant ${t.slug}: EW-Stufe 5 ${ew5 ? "ergänzt" : "bereits vorhanden/übersprungen"}, ${dim5} Schadensdimension(en) um Stufe 5 ergänzt.`);
|
||
}
|
||
console.log("Fertig.");
|
||
}
|
||
|
||
main().then(() => process.exit(0)).catch((e) => { console.error(e); process.exit(1); });
|