Files
craftvia/scripts/backfill-risk-5x5.ts
T
msolarczekandClaude Opus 5 c8e6f30a27
CI / build-and-check (push) Canceled after 0s
CI / audit (push) Canceled after 0s
CI / sbom (push) Canceled after 0s
Basis: Certvia dev@a48c5fb als Fundament für Craftvia
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>
2026-09-14 11:05:39 +02:00

62 lines
2.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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); });