'use server' import { revalidatePath } from 'next/cache' import { z } from 'zod' import { auditSchreiben } from '@/server/audit' import { NichtErlaubt, pruefeRolle } from '@/server/berechtigung' import { db } from '@/server/db' import { kontoAbgleichen } from '@/server/kalender/abgleich' export type Rueckmeldung = { ok: true; meldung: string } | { ok: false; fehler: string } const eingabe = z.object({ id: z.string().min(1) }) /** * Alle Aktionen arbeiten nur auf Konten des angemeldeten Benutzers — die * Kennung geht in die where-Bedingung, nicht in einen nachträglichen * Vergleich. */ async function eigenesKonto(id: string, benutzerId: string) { return db.kalenderkonto.findFirst({ where: { id, benutzerId }, select: { id: true, anzeigename: true, anbieter: true }, }) } async function akteurOderFehler() { try { return { akteur: await pruefeRolle('BENUTZER') } } catch (fehler) { if (fehler instanceof NichtErlaubt) { return { antwort: { ok: false as const, fehler: fehler.message } } } throw fehler } } export async function jetztAbgleichen(daten: FormData): Promise { const geprueft = await akteurOderFehler() if ('antwort' in geprueft) return geprueft.antwort const gelesen = eingabe.safeParse({ id: daten.get('id') }) if (!gelesen.success) return { ok: false, fehler: 'Ungültige Anfrage.' } const konto = await eigenesKonto(gelesen.data.id, geprueft.akteur.id) if (!konto) return { ok: false, fehler: 'Dieses Kalenderkonto gibt es nicht.' } const bericht = await kontoAbgleichen(konto.id) revalidatePath('/kalenderkonten') if (!bericht.ok) { return { ok: false, fehler: bericht.fehler ?? 'Der Abgleich schlug fehl.' } } return { ok: true, meldung: `„${konto.anzeigename}": ${bericht.geaendert} Termin(e) gelesen, ${bericht.entfernt} entfernt.`, } } const umbenennen = z.object({ id: z.string().min(1), anzeigename: z.string().trim().min(2, 'Mindestens zwei Zeichen.').max(60), }) export async function kontoUmbenennen(daten: FormData): Promise { const geprueft = await akteurOderFehler() if ('antwort' in geprueft) return geprueft.antwort const gelesen = umbenennen.safeParse({ id: daten.get('id'), anzeigename: daten.get('anzeigename'), }) if (!gelesen.success) { return { ok: false, fehler: 'Bitte einen Namen mit mindestens zwei Zeichen angeben.' } } const konto = await eigenesKonto(gelesen.data.id, geprueft.akteur.id) if (!konto) return { ok: false, fehler: 'Dieses Kalenderkonto gibt es nicht.' } await db.kalenderkonto.update({ where: { id: konto.id }, data: { anzeigename: gelesen.data.anzeigename }, }) await auditSchreiben({ akteur: { id: geprueft.akteur.id, email: geprueft.akteur.email }, aktion: 'kalenderkonto.umbenannt', objektTyp: 'Kalenderkonto', objektId: konto.id, details: { vorher: konto.anzeigename, nachher: gelesen.data.anzeigename }, }) revalidatePath('/kalenderkonten') return { ok: true, meldung: 'Name gespeichert.' } } /** * Trennt ein Kalenderkonto. Die gespiegelten Termine verschwinden mit ihm — * sie stammen aus dem Kalender und sind dort weiterhin vorhanden. */ export async function kontoTrennen(daten: FormData): Promise { const geprueft = await akteurOderFehler() if ('antwort' in geprueft) return geprueft.antwort const gelesen = eingabe.safeParse({ id: daten.get('id') }) if (!gelesen.success) return { ok: false, fehler: 'Ungültige Anfrage.' } const konto = await eigenesKonto(gelesen.data.id, geprueft.akteur.id) if (!konto) return { ok: false, fehler: 'Dieses Kalenderkonto gibt es nicht.' } await db.kalenderkonto.delete({ where: { id: konto.id } }) await auditSchreiben({ akteur: { id: geprueft.akteur.id, email: geprueft.akteur.email }, aktion: 'kalenderkonto.getrennt', objektTyp: 'Kalenderkonto', objektId: konto.id, details: { anbieter: konto.anbieter, anzeigename: konto.anzeigename }, }) revalidatePath('/kalenderkonten') revalidatePath('/') return { ok: true, meldung: `„${konto.anzeigename}" getrennt. Die Termine bleiben im Kalender selbst erhalten.`, } }