This commit was merged in pull request #59.
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
"use server";
|
||||
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { z } from "@/lib/validierung";
|
||||
import { gewaehlteDatei, groesseText, maxGroesseBytes } from "@/lib/anhaenge";
|
||||
import { MandantFehler } from "@/server/mandant";
|
||||
import { RechteFehler, rechtErforderlich } from "@/server/auth/sitzungspflicht";
|
||||
import { LaufFehler } from "@/server/services/laeufe";
|
||||
import { AnhangFehler, anhangEntfernen, anhangSpeichern } from "@/server/services/anhaenge";
|
||||
|
||||
/**
|
||||
* Server Actions der Anhaenge (#032).
|
||||
*
|
||||
* Recht ist `laeufeBearbeiten` — wer einen Schritt abhaken darf, darf auch ein
|
||||
* Foto dazu legen. Der Archiv-Schreibschutz sitzt im Dienst, an derselben
|
||||
* Stelle wie fuer alles andere am Lauf.
|
||||
*
|
||||
* Die Datei kommt als `File` im Formular. Server Actions nehmen das von sich
|
||||
* aus; ein eigener Upload-Endpunkt waere eine zweite Tuer mit eigener
|
||||
* Rechtepruefung.
|
||||
*/
|
||||
|
||||
export type AktionsErgebnis = { readonly fehler?: string; readonly erfolg?: string };
|
||||
|
||||
const RECHTE_MELDUNG = "Dafür fehlt dir die Berechtigung.";
|
||||
|
||||
function alsErgebnis(fehler: unknown, neutral: string): AktionsErgebnis {
|
||||
if (fehler instanceof AnhangFehler || fehler instanceof LaufFehler) {
|
||||
return { fehler: fehler.message };
|
||||
}
|
||||
if (fehler instanceof MandantFehler) return { fehler: fehler.message };
|
||||
if (fehler instanceof RechteFehler) return { fehler: RECHTE_MELDUNG };
|
||||
console.error(
|
||||
`${neutral}:`,
|
||||
fehler instanceof Error ? `${fehler.name}: ${fehler.message}` : "unbekannter Fehler",
|
||||
);
|
||||
return { fehler: neutral };
|
||||
}
|
||||
|
||||
export async function anhangHochladenAktion(
|
||||
_vorher: AktionsErgebnis,
|
||||
formular: FormData,
|
||||
): Promise<AktionsErgebnis> {
|
||||
try {
|
||||
const { benutzer } = await rechtErforderlich("laeufeBearbeiten");
|
||||
|
||||
const eingabe = z.object({ laufId: z.string().min(1), vorlagenSchrittId: z.string().min(1) }).safeParse({
|
||||
laufId: formular.get("laufId"),
|
||||
vorlagenSchrittId: formular.get("vorlagenSchrittId"),
|
||||
});
|
||||
if (!eingabe.success) return { fehler: "Die Eingabe ist unvollständig." };
|
||||
|
||||
// `getAll`, nicht `get`: das Formular hat zwei Felder `datei` – siehe
|
||||
// `gewaehlteDatei`.
|
||||
const datei = gewaehlteDatei(formular.getAll("datei"));
|
||||
if (!datei) {
|
||||
return { fehler: "Keine Datei gewählt." };
|
||||
}
|
||||
/*
|
||||
* Die Groesse **vor** dem Lesen pruefen. `arrayBuffer()` zieht die ganze
|
||||
* Datei in den Speicher; bei einer 500-MB-Datei waere die Pruefung danach zu
|
||||
* spaet. Der Dienst prueft es noch einmal — er ist auch ohne diese Action
|
||||
* erreichbar.
|
||||
*/
|
||||
const grenze = maxGroesseBytes();
|
||||
if (datei.size > grenze) {
|
||||
return {
|
||||
fehler:
|
||||
`Die Datei ist ${groesseText(datei.size)} groß. Erlaubt sind höchstens ` +
|
||||
`${groesseText(grenze)} je Datei.`,
|
||||
};
|
||||
}
|
||||
|
||||
await anhangSpeichern(
|
||||
{
|
||||
laufId: eingabe.data.laufId,
|
||||
vorlagenSchrittId: eingabe.data.vorlagenSchrittId,
|
||||
dateiname: datei.name,
|
||||
medientyp: datei.type,
|
||||
inhalt: new Uint8Array(await datei.arrayBuffer()),
|
||||
},
|
||||
{ id: benutzer.id, rolle: benutzer.rolle },
|
||||
);
|
||||
|
||||
revalidatePath(`/laeufe/${eingabe.data.laufId}`);
|
||||
return { erfolg: "Angehängt." };
|
||||
} catch (fehler) {
|
||||
return alsErgebnis(fehler, "Der Anhang konnte nicht gespeichert werden.");
|
||||
}
|
||||
}
|
||||
|
||||
export async function anhangEntfernenAktion(
|
||||
_vorher: AktionsErgebnis,
|
||||
formular: FormData,
|
||||
): Promise<AktionsErgebnis> {
|
||||
try {
|
||||
const { benutzer } = await rechtErforderlich("laeufeBearbeiten");
|
||||
const eingabe = z
|
||||
.object({ anhangId: z.string().min(1), laufId: z.string().min(1) })
|
||||
.safeParse({ anhangId: formular.get("anhangId"), laufId: formular.get("laufId") });
|
||||
if (!eingabe.success) return { fehler: "Die Eingabe ist unvollständig." };
|
||||
|
||||
await anhangEntfernen(eingabe.data.anhangId, { id: benutzer.id, rolle: benutzer.rolle });
|
||||
revalidatePath(`/laeufe/${eingabe.data.laufId}`);
|
||||
return { erfolg: "Entfernt." };
|
||||
} catch (fehler) {
|
||||
return alsErgebnis(fehler, "Der Anhang konnte nicht entfernt werden.");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user