import "server-only"; import { sql, withUser, type Tx } from "./index"; import type { Database } from "@/lib/supabase/types"; // Aufruf einer Datenbankfunktion. // // Die Geschäftslogik liegt in PL/pgSQL — Eintritt, Versetzung, Austritt und // die übrigen zehn Mutationen. Daran ändert der Wechsel des Zugriffswegs // nichts: es fällt nur die API-Schicht dazwischen weg. Aufgerufen wird die // Funktion jetzt unmittelbar, innerhalb der Transaktion, in der auch der // Sitzungskontext gilt — ohne den würde require_hr_admin() darin abweisen. export type MutationFn = keyof Database["public"]["Functions"]; /** * Ruft `fn(payload)` innerhalb der laufenden Transaktion auf. * * `payload` weglassen für die Funktionen ohne Argument — * apply_due_pending_changes() ist die einzige. Mit einem jsonb-Argument * aufgerufen fände Postgres keine passende Signatur. */ export async function callFunction(tx: Tx, fn: MutationFn, payload?: Record): Promise { // Der Funktionsname stammt aus einer geschlossenen Aufzählung, nie aus // einer Eingabe — sonst wäre die Verkettung hier eine Einladung. const name = sql.raw(`"${fn}"`); const query = payload === undefined ? sql<{ result: unknown }>`select ${name}() as result` : sql<{ result: unknown }>`select ${name}(${sql.val(JSON.stringify(payload))}::jsonb) as result`; const result = await query.execute(tx); return result.rows[0]?.result; } export type ActionResult = { success: boolean; error?: string }; /** * Eine Mutation im Namen der angemeldeten Person, mit der üblichen * Fehlerbehandlung für Server Actions. * * Die Prüfung der Berechtigung passiert in der Funktion selbst * (require_hr_admin) und unabhängig davon in den RLS-Policies — nicht hier. */ export async function runMutation( userId: string | null, fn: MutationFn, payload: Record ): Promise { try { await withUser(userId, (tx) => callFunction(tx, fn, payload)); return { success: true }; } catch (err) { // Die Meldungen der Funktionen sind für die Oberfläche geschrieben // („Diese Planstelle ist bereits besetzt.") und werden durchgereicht. return { success: false, error: err instanceof Error ? err.message : "Unbekannter Fehler." }; } }