Files
sshkeyvault/prisma/seed.ts
T
florian.wildeandClaude Opus 5.5 c8a415f1ca chore(hosts): Demo-Daten, Screenshots und Issue-Notizen (#008)
SSHKV_SEED_DEMO=1 legt sechs Demo-Hosts, drei Gruppen und einen Kunden an,
damit Entwicklung, Screenshots und Handbuch ohne echte Server auskommen.
Nur mit der Variable, damit eine echte Instanz nie Phantom-Hosts bekommt;
Upsert nach Name haelt den Seed idempotent.

Das Screenshot-Skript kennt jetzt Seiten mit wechselnder ID (folgen: erster
passender Link) und nimmt Hostliste und Hostdetail auf.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:50:53 +02:00

134 lines
4.6 KiB
TypeScript
Raw Permalink 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 { 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; optional Demo-Hosts (#008).
*
* 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.`);
}
/**
* Demo-Hosts fuer Entwicklung, Screenshots und Handbuch (#008) – nur mit SSHKV_SEED_DEMO=1,
* damit eine echte Instanz nie Phantom-Hosts bekommt. Upsert nach Name: ein zweiter Lauf
* aendert nichts, und in der Oberflaeche geaenderte Demo-Hosts bleiben, wie sie sind.
* Bewusst ohne Protokolleintraege: Seed-Daten sind keine Handlung eines Menschen.
*/
const DEMO_GRUPPEN = [
{ name: "Proxmox", beschreibung: "Virtualisierungs-Cluster im Rechenzentrum" },
{ name: "DMZ", beschreibung: "Von außen erreichbare Systeme" },
{ name: "Kunde X", beschreibung: "Systeme, die wir für Kunde X betreiben" },
] as const;
const DEMO_HOSTS: {
name: string;
adresse: string;
port?: number;
tags: string[];
beschreibung?: string;
anmeldeart?: "DIENSTBENUTZER" | "ROOT";
kunde?: string;
gruppen: string[];
}[] = [
{ name: "srv-pve01", adresse: "10.0.10.11", tags: ["pve", "produktion"], gruppen: ["Proxmox"] },
{ name: "srv-pve02", adresse: "10.0.10.12", tags: ["pve", "produktion"], gruppen: ["Proxmox"] },
{
name: "srv-backup01",
adresse: "10.0.10.30",
tags: ["backup"],
beschreibung: "Proxmox Backup Server, zieht nachts von pve01/pve02",
gruppen: ["Proxmox"],
},
{
name: "srv-app01",
adresse: "app01.kunde-x.example",
port: 2222,
tags: ["web"],
kunde: "Kunde X",
gruppen: ["Kunde X"],
},
{
name: "fw-edge01",
adresse: "192.168.1.1",
tags: ["firewall"],
anmeldeart: "ROOT",
beschreibung: "Appliance ohne sudo – Anmeldung direkt als root",
gruppen: ["DMZ"],
},
{ name: "srv-rz-claude-linux", adresse: "fd00:10::42", tags: ["test"], gruppen: [] },
];
async function demoDaten(): Promise<void> {
if (process.env["SSHKV_SEED_DEMO"] !== "1") return;
const gruppen = new Map<string, string>();
for (const g of DEMO_GRUPPEN) {
const gruppe = await prisma.hostgruppe.upsert({ where: { name: g.name }, create: g, update: {} });
gruppen.set(g.name, gruppe.id);
}
for (const h of DEMO_HOSTS) {
const kunde = h.kunde
? await prisma.kunde.upsert({ where: { name: h.kunde }, create: { name: h.kunde }, update: {} })
: null;
await prisma.host.upsert({
where: { name: h.name },
update: {},
create: {
name: h.name,
adresse: h.adresse,
port: h.port ?? 22,
tags: h.tags,
beschreibung: h.beschreibung ?? null,
anmeldeart: h.anmeldeart ?? "DIENSTBENUTZER",
kundeId: kunde?.id ?? null,
gruppen: { create: h.gruppen.map((name) => ({ hostgruppeId: gruppen.get(name) ?? "" })) },
},
});
}
console.log(`Demo-Daten geprüft: ${DEMO_HOSTS.length} Hosts, ${DEMO_GRUPPEN.length} Gruppen.`);
}
try {
await notfallAdmin();
await vorlagen();
await demoDaten();
} finally {
await prisma.$disconnect();
}