feat(seed): mitgelieferte Vorlagen am Schluessel erkennen (#42)
Prüfung / pruefung (push) Canceled after 0s
Prüfung / e2e (push) Canceled after 0s

This commit was merged in pull request #42.
This commit is contained in:
2026-10-04 14:49:00 +00:00
parent 88d7db8c3d
commit 0fa41b9e08
20 changed files with 316 additions and 36 deletions
+54 -15
View File
@@ -68,11 +68,61 @@ export type GesaeteVorlage = {
readonly neu: boolean;
};
/**
* Sucht die mitgelieferte Vorlage – zuerst am Schluessel, dann am Namen.
*
* Der Schluessel ueberlebt ein Umbenennen, der Name nicht: genau daran legte der
* Seed eine umbenannte Vorlage ein zweites Mal an, und der Schnellstart stand
* auf der Geraeteseite doppelt (#021). Der Rueckfall auf den Namen bleibt, weil
* eine Installation von vor diesem Feld noch keinen Schluessel hat — ohne ihn
* bekaeme sie beim ersten Lauf danach genau die Dubletten, die das Feld
* verhindern soll. Wird sie so gefunden, traegt der Seed den Schluessel nach.
*
* Auch eine **archivierte** Vorlage gilt als vorhanden: eine zweite gleichen
* Namens danebenzustellen waere keine Hilfe, sondern eine Dublette in jeder
* Liste. Wer die Beispielvorlage zurueck will, holt sie aus dem Archiv.
*/
async function mitgelieferteFinden(db: PrismaClient, dokument: Austausch) {
const mitVersion = {
versionen: {
where: { status: VersionStatus.VEROEFFENTLICHT },
orderBy: { version: "desc" as const },
take: 1,
},
};
if (dokument.seedSchluessel !== undefined) {
const amSchluessel = await db.vorlage.findFirst({
where: { seedSchluessel: dokument.seedSchluessel },
include: mitVersion,
});
if (amSchluessel) return amSchluessel;
}
const amNamen = await db.vorlage.findFirst({
// Nur eine Zeile **ohne** Schluessel darf hier zugeordnet werden. Sonst
// koennte eine fremde Vorlage, die zufaellig so heisst wie die mitgelieferte,
// deren Schluessel bekommen - und der naechste Lauf wuerde sie fuer das
// Original halten.
where: { name: dokument.name, kundeId: null, seedSchluessel: null },
include: mitVersion,
});
if (amNamen && dokument.seedSchluessel !== undefined) {
await db.vorlage.update({
where: { id: amNamen.id },
data: { seedSchluessel: dokument.seedSchluessel },
});
console.log(` Vorlage „${amNamen.name}“: Schlüssel ${dokument.seedSchluessel} nachgetragen`);
}
return amNamen;
}
/**
* Legt die Vorlagen als veroeffentlichte Version 1 an.
*
* Idempotent ueber den **Namen**: gibt es die Vorlage schon, bleibt sie
* unangetastet — auch dann, wenn jemand sie inzwischen bearbeitet hat. Ein
* Idempotent ueber den **Schluessel** (Rueckfall: Name, siehe
* `mitgelieferteFinden`): gibt es die Vorlage schon, bleibt sie unangetastet —
* auch dann, wenn jemand sie inzwischen bearbeitet oder umbenannt hat. Ein
* zweiter Seed-Lauf darf die Arbeit eines Redakteurs nicht zurueckdrehen, und
* eine veroeffentlichte Version wird ohnehin nie veraendert.
*/
@@ -82,19 +132,7 @@ export async function vorlagenSaeen(db: PrismaClient): Promise<readonly GesaeteV
let uebersprungen = 0;
for (const { dokument } of vorlagenLesen()) {
const vorhanden = await db.vorlage.findFirst({
// Auch eine **archivierte** Vorlage gilt als vorhanden: eine zweite gleichen
// Namens danebenzustellen waere keine Hilfe, sondern eine Dublette in jeder
// Liste. Wer die Beispielvorlage zurueck will, holt sie aus dem Archiv.
where: { name: dokument.name, kundeId: null },
include: {
versionen: {
where: { status: VersionStatus.VEROEFFENTLICHT },
orderBy: { version: "desc" },
take: 1,
},
},
});
const vorhanden = await mitgelieferteFinden(db, dokument);
const veroeffentlichte = vorhanden?.versionen[0];
if (vorhanden && veroeffentlichte) {
ergebnis.push({
@@ -131,6 +169,7 @@ export async function vorlagenSaeen(db: PrismaClient): Promise<readonly GesaeteV
beschreibung: dokument.beschreibung ?? null,
kategorie: dokument.kategorie ?? null,
schlagworte: [...dokument.schlagworte],
seedSchluessel: dokument.seedSchluessel ?? null,
},
}));
@@ -1,6 +1,7 @@
{
"format": 1,
"name": "PC Neuinstallation",
"seedSchluessel": "pc-neuinstallation",
"beschreibung": "Vollständige Neuinstallation eines Arbeitsplatzrechners: von der Datensicherung des alten Systems über Installation, Einbindung und Verschlüsselung bis zur Übergabe an den Benutzer.",
"kategorie": "Client",
"schlagworte": ["windows", "client", "neuinstallation", "bitlocker"],
@@ -1,6 +1,7 @@
{
"format": 1,
"name": "Mitarbeiter-Onboarding",
"seedSchluessel": "mitarbeiter-onboarding",
"beschreibung": "Neues Benutzerkonto im Active Directory oder in Entra ID anlegen, Hardware bereitstellen und den ersten Arbeitstag begleiten.",
"kategorie": "Benutzer",
"schlagworte": ["onboarding", "eintritt", "benutzerkonto", "active directory", "entra id"],
@@ -1,6 +1,7 @@
{
"format": 1,
"name": "Mitarbeiter-Offboarding",
"seedSchluessel": "mitarbeiter-offboarding",
"beschreibung": "Konto sperren, Postfach und Daten übergeben, Hardware und Zugänge einsammeln, wenn jemand das Haus verlässt.",
"kategorie": "Benutzer",
"schlagworte": ["offboarding", "austritt", "benutzerkonto", "sperrung"],
@@ -1,6 +1,7 @@
{
"format": 1,
"name": "Server-Inbetriebnahme Windows-VM",
"seedSchluessel": "server-inbetriebnahme",
"beschreibung": "Inbetriebnahme einer neuen Windows-Server-VM: Planung und Adressvergabe, Installation, Domänenbeitritt, Absicherung sowie Aufnahme in Backup und Monitoring.",
"kategorie": "Server",
"schlagworte": ["windows", "server", "vm", "inbetriebnahme"],
@@ -1,6 +1,7 @@
{
"format": 1,
"name": "Firewall-Rollout OPNsense",
"seedSchluessel": "firewall-rollout-opnsense",
"beschreibung": "Austausch oder Neuaufbau einer OPNsense-Firewall: von den Providerdaten über Regelwerk und VPN bis zur Abnahme und zur außer Betrieb genommenen Altfirewall.",
"kategorie": "Netzwerk",
"schlagworte": ["firewall", "opnsense", "netzwerk", "vpn", "rollout"],
@@ -1,6 +1,7 @@
{
"format": 1,
"name": "Monatlicher Patchday",
"seedSchluessel": "monatlicher-patchday",
"beschreibung": "Der wiederkehrende Ablauf für Updates an Servern, Hypervisor, Netzgeräten und Clients: ankündigen, einspielen, kontrollieren und berichten.",
"kategorie": "Wartung",
"schlagworte": ["patchday", "updates", "wartung", "monatlich"],
@@ -1,6 +1,7 @@
{
"format": 1,
"name": "Backup-Restore-Test",
"seedSchluessel": "backup-restore-test",
"beschreibung": "Der regelmäßige Nachweis, dass sich eine Sicherung wirklich zurückholen lässt: Wiederherstellungspunkt prüfen, in einer isolierten Umgebung zurückspielen, Funktion und Dauer festhalten.",
"kategorie": "Wartung",
"schlagworte": ["backup", "restore", "wiederherstellung", "test", "wartung"],