feat(datenbank): Datenmodell mit Datenbankregeln und Seed (#003)
Alle Modelle fuer die fuenf Phasen in einer Migration, damit spaetere Phasen keine Umbauten an Kerntabellen brauchen. Was auch an der Service-Schicht vorbei gelten muss, steht als CHECK, partieller Index oder Trigger in der Datenbank: Personen-Keys ohne Private Key, genau ein Instanz-Deployment-Key, ein vertrauter Host-Key je Algorithmus, shell-sichere Zielbenutzer und Pfade. Das Protokoll ist append-only und per Hash-Kette verbunden; den Hash berechnet ein Trigger, damit keine Zeile ohne Verkettung entstehen kann (#005 baut darauf auf). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
import { PrismaPg } from "@prisma/adapter-pg";
|
||||
import "dotenv/config";
|
||||
import { PrismaClient } from "../src/generated/prisma/client";
|
||||
import { PASSWORT_MINDESTLAENGE, passwortHashen } from "../src/lib/passwort-hash";
|
||||
import { EINGEBAUTE_VORLAGEN } from "./vorlagen";
|
||||
|
||||
/**
|
||||
* Grundausstattung (#003): Notfall-Admin und eingebaute Vorlagen.
|
||||
*
|
||||
* Laeuft bei jedem Start des migrate-Dienstes und ist deshalb idempotent. Ein bestehender
|
||||
* Notfall-Admin wird nie angefasst – sonst setzte jeder Neustart ein in der Oberflaeche
|
||||
* geaendertes Passwort zurueck.
|
||||
*/
|
||||
const prisma = new PrismaClient({ adapter: new PrismaPg({ connectionString: process.env["DATABASE_URL"] }) });
|
||||
|
||||
async function notfallAdmin(): Promise<void> {
|
||||
const anmeldename = process.env["SSHKV_BREAKGLASS_USER"]?.trim() || "notfall";
|
||||
const passwort = process.env["SSHKV_BREAKGLASS_PASSWORD"] ?? "";
|
||||
if (await prisma.benutzer.findFirst({ where: { istNotfall: true } })) {
|
||||
console.log("Notfall-Admin vorhanden – unverändert.");
|
||||
return;
|
||||
}
|
||||
if (passwort.length < PASSWORT_MINDESTLAENGE) {
|
||||
console.warn(
|
||||
`Kein Notfall-Admin angelegt: SSHKV_BREAKGLASS_PASSWORD fehlt oder ist kürzer als ${PASSWORT_MINDESTLAENGE} Zeichen.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
await prisma.benutzer.create({
|
||||
data: {
|
||||
anmeldename,
|
||||
name: "Notfall-Admin",
|
||||
rolle: "ADMIN",
|
||||
istNotfall: true,
|
||||
passwortHash: await passwortHashen(passwort),
|
||||
},
|
||||
});
|
||||
console.log(`Notfall-Admin „${anmeldename}“ angelegt. TOTP wird bei der ersten Anmeldung eingerichtet.`);
|
||||
}
|
||||
|
||||
async function vorlagen(): Promise<void> {
|
||||
for (const vorlage of EINGEBAUTE_VORLAGEN) {
|
||||
await prisma.vorlage.upsert({ where: { name: vorlage.name }, create: vorlage, update: {} });
|
||||
}
|
||||
console.log(`${EINGEBAUTE_VORLAGEN.length} eingebaute Vorlagen geprüft.`);
|
||||
}
|
||||
|
||||
try {
|
||||
await notfallAdmin();
|
||||
await vorlagen();
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
Reference in New Issue
Block a user