feat(seed): mitgelieferte Vorlagen am Schluessel erkennen
Der Seed erkannte seine sieben Vorlagen am Namen. Wer eine umbenannte, bekam beim naechsten Lauf eine zweite -- und der Schnellstart stand auf der Geraeteseite doppelt. Vorlage.seedSchluessel traegt die Kennung, eindeutig wo gesetzt (partieller Index, weil eigene Vorlagen keinen tragen). Das Austauschformat kennt das Feld als seedSchluessel; nicht als schluessel, wie das Issue plante, denn so heisst dort schon das Feld einer Variablen. Der Rueckfall auf den Namen bleibt, sonst bekaeme eine Installation von vor diesem Feld beim ersten Lauf danach genau die Dubletten, die es verhindern soll. Wird sie so gefunden, traegt der Seed den Schluessel nach -- an der Entwicklungsdatenbank gefahren: sieben nachgetragen, nichts neu angelegt. Der Import setzt den Schluessel nicht, auch nicht wenn das Dokument ihn mitbringt, und der Export schreibt ihn nicht: eine eingespielte Kopie soll nicht den Platz des Originals einnehmen. Gegenprobe gefahren: ohne die Suche am Schluessel werden vier Tests rot. Closes #32
This commit is contained in:
@@ -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"],
|
||||
|
||||
Reference in New Issue
Block a user