Merge branch 'phase/019-vorlagen'
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
"use server";
|
||||
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { redirect } from "next/navigation";
|
||||
import { VorlagenFehler, type VorlageEingabe } from "@/lib/vorlagen";
|
||||
import { akteurVon, fehlerZustand, textfeld, type FormularZustand } from "@/server/actions/formular";
|
||||
import { rechtErforderlich } from "@/server/auth/sitzungspflicht";
|
||||
import { vorlageAnlegen, vorlageArchivieren, vorlageBearbeiten } from "@/server/services/vorlagen";
|
||||
|
||||
/** Vorlagen-Aktionen (#019): nur Admins (Recht einstellungenVerwalten). */
|
||||
|
||||
function felder(formular: FormData): VorlageEingabe {
|
||||
const tage = textfeld(formular, "standardLaufzeitTage");
|
||||
return {
|
||||
name: textfeld(formular, "name") ?? "",
|
||||
beschreibung: textfeld(formular, "beschreibung") ?? null,
|
||||
schluesselTyp: textfeld(formular, "schluesselTyp") === "RSA4096" ? "RSA4096" : "ED25519",
|
||||
kategorie: textfeld(formular, "kategorie") === "PERSON" ? "PERSON" : "DIENST",
|
||||
standardLaufzeitTage: tage ? Number(tage) : null,
|
||||
zielbenutzer: (textfeld(formular, "zielbenutzer") ?? "").trim(),
|
||||
zielbenutzerAnlegen: formular.get("zielbenutzerAnlegen") === "ja",
|
||||
sudoModus:
|
||||
(["KEINER", "BEFEHLE", "VOLL"] as const).find((m) => m === textfeld(formular, "sudoModus")) ?? "KEINER",
|
||||
sudoBefehle: (textfeld(formular, "sudoBefehle") ?? "")
|
||||
.split("\n")
|
||||
.map((b) => b.trim())
|
||||
.filter(Boolean),
|
||||
authorizedKeysOptionen: textfeld(formular, "authorizedKeysOptionen") ?? null,
|
||||
sshConfigAlias: textfeld(formular, "sshConfigAlias") ?? null,
|
||||
knownHostsEintragen: formular.get("knownHostsEintragen") === "ja",
|
||||
privatNachAusrollenLoeschen: formular.get("privatNachAusrollenLoeschen") === "ja",
|
||||
};
|
||||
}
|
||||
|
||||
function alsZustand(fehler: unknown): FormularZustand {
|
||||
if (fehler instanceof VorlagenFehler) {
|
||||
return {
|
||||
fehler: fehler.message,
|
||||
...(fehler.feld ? { feldfehler: { [fehler.feld]: fehler.message } } : {}),
|
||||
};
|
||||
}
|
||||
return fehlerZustand(fehler);
|
||||
}
|
||||
|
||||
export async function vorlageSpeichernAktion(
|
||||
_vorher: FormularZustand,
|
||||
formular: FormData,
|
||||
): Promise<FormularZustand> {
|
||||
const id = textfeld(formular, "id");
|
||||
try {
|
||||
const { benutzer } = await rechtErforderlich("einstellungenVerwalten");
|
||||
if (id) await vorlageBearbeiten(id, felder(formular), akteurVon(benutzer));
|
||||
else await vorlageAnlegen(felder(formular), akteurVon(benutzer));
|
||||
} catch (fehler) {
|
||||
return alsZustand(fehler);
|
||||
}
|
||||
revalidatePath("/vorlagen");
|
||||
redirect("/vorlagen");
|
||||
}
|
||||
|
||||
export async function vorlageArchivierenAktion(
|
||||
_vorher: FormularZustand,
|
||||
formular: FormData,
|
||||
): Promise<FormularZustand> {
|
||||
try {
|
||||
const { benutzer } = await rechtErforderlich("einstellungenVerwalten");
|
||||
await vorlageArchivieren(textfeld(formular, "id") ?? "", akteurVon(benutzer));
|
||||
} catch (fehler) {
|
||||
return alsZustand(fehler);
|
||||
}
|
||||
revalidatePath("/vorlagen");
|
||||
redirect("/vorlagen");
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { Prisma } from "@/generated/prisma/client";
|
||||
import { prisma } from "@/lib/db";
|
||||
import { vorlagePruefen, VorlagenFehler, type VorlageEingabe } from "@/lib/vorlagen";
|
||||
import { protokollieren } from "@/server/services/protokoll";
|
||||
|
||||
/** Vorlagen anlegen, bearbeiten, archivieren (#019). Eingebaute lassen sich nur bearbeiten. */
|
||||
type Akteur = { readonly id: string; readonly name: string };
|
||||
|
||||
function daten(eingabe: VorlageEingabe) {
|
||||
const v = vorlagePruefen(eingabe);
|
||||
return {
|
||||
name: v.name.trim(),
|
||||
beschreibung: v.beschreibung?.trim() || null,
|
||||
schluesselTyp: v.schluesselTyp,
|
||||
kategorie: v.kategorie,
|
||||
standardLaufzeitTage: v.standardLaufzeitTage ?? null,
|
||||
zielbenutzer: v.zielbenutzer,
|
||||
zielbenutzerAnlegen: v.zielbenutzerAnlegen,
|
||||
sudoModus: v.sudoModus,
|
||||
sudoBefehle: v.sudoModus === "BEFEHLE" ? v.sudoBefehle.map((b) => b.trim().replace(/\s+/g, " ")) : [],
|
||||
authorizedKeysOptionen: v.authorizedKeysOptionen?.trim() || null,
|
||||
sshConfigAlias: v.sshConfigAlias?.trim() || null,
|
||||
knownHostsEintragen: v.knownHostsEintragen,
|
||||
privatNachAusrollenLoeschen: v.privatNachAusrollenLoeschen,
|
||||
};
|
||||
}
|
||||
|
||||
function eindeutig(fehler: unknown): never {
|
||||
if (fehler instanceof Prisma.PrismaClientKnownRequestError && fehler.code === "P2002") {
|
||||
throw new VorlagenFehler("Eine Vorlage mit diesem Namen gibt es bereits.", "name");
|
||||
}
|
||||
throw fehler;
|
||||
}
|
||||
|
||||
export async function vorlageAnlegen(eingabe: VorlageEingabe, akteur: Akteur): Promise<string> {
|
||||
const werte = daten(eingabe);
|
||||
const vorlage = await prisma.vorlage.create({ data: werte }).catch(eindeutig);
|
||||
await protokollieren({
|
||||
akteur,
|
||||
aktion: "vorlage.angelegt",
|
||||
ergebnis: "ERFOLG",
|
||||
details: { name: werte.name },
|
||||
});
|
||||
return vorlage.id;
|
||||
}
|
||||
|
||||
export async function vorlageBearbeiten(id: string, eingabe: VorlageEingabe, akteur: Akteur): Promise<void> {
|
||||
const werte = daten(eingabe);
|
||||
const alt = await prisma.vorlage.findUnique({ where: { id } });
|
||||
if (!alt || alt.archiviertAm) throw new VorlagenFehler("Diese Vorlage gibt es nicht (mehr).");
|
||||
await prisma.vorlage.update({ where: { id }, data: werte }).catch(eindeutig);
|
||||
const geaendert = Object.entries(werte)
|
||||
.filter(([feld, wert]) => JSON.stringify(alt[feld as keyof typeof alt]) !== JSON.stringify(wert))
|
||||
.map(([feld]) => feld);
|
||||
await protokollieren({
|
||||
akteur,
|
||||
aktion: "vorlage.geaendert",
|
||||
ergebnis: "ERFOLG",
|
||||
details: { name: werte.name, geaendert },
|
||||
});
|
||||
}
|
||||
|
||||
export async function vorlageArchivieren(id: string, akteur: Akteur): Promise<void> {
|
||||
const vorlage = await prisma.vorlage.findUnique({ where: { id } });
|
||||
if (!vorlage || vorlage.archiviertAm) throw new VorlagenFehler("Diese Vorlage gibt es nicht (mehr).");
|
||||
if (vorlage.eingebaut) throw new VorlagenFehler("Eingebaute Vorlagen lassen sich nicht archivieren.");
|
||||
await prisma.vorlage.update({ where: { id }, data: { archiviertAm: new Date() } });
|
||||
await protokollieren({
|
||||
akteur,
|
||||
aktion: "vorlage.archiviert",
|
||||
ergebnis: "ERFOLG",
|
||||
details: { name: vorlage.name },
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user