"use server"; import { revalidatePath } from "next/cache"; import { redirect } from "next/navigation"; import { requireStaff } from "@/lib/admin/guard"; import { prisma } from "@/lib/prisma"; import { logStaffActivity } from "@/lib/services/staff-activity"; import { formPositiveBigInt } from "@/lib/form-data"; import { logServerError } from "@/lib/server-log"; // website_permissions (model WebsitePermissions) is the CMS-owned permission -> // minimum-rank mapping. Editable columns are exactly: permission (unique name), // min_rank (lowest rank that may use it), description. id is BigInt and // created_at/updated_at are managed here. function parseMinRank(formData: FormData): number { const n = Number(String(formData.get("minRank") ?? "").trim()); return Number.isInteger(n) && n >= 0 ? n : 1; } export async function createPermission(formData: FormData): Promise { const staff = await requireStaff(); const permission = String(formData.get("permission") ?? "") .trim() .slice(0, 255); const minRank = parseMinRank(formData); const description = String(formData.get("description") ?? "").trim() || null; if (!permission) return; const now = new Date(); try { await prisma.websitePermissions.upsert({ where: { permission }, update: { minRank, description, updatedAt: now }, create: { permission, minRank, description, createdAt: now, updatedAt: now }, }); await logStaffActivity({ staffId: staff.id, action: "permission_create", description: `Saved permission "${permission}" (min rank ${minRank})`, targetType: "permission", }); } catch (error) { logServerError("admin.permission_create_failed", error, { staffId: staff.id, permission }); redirect("/admin/permissions?error=save"); // ignore (e.g. constraint failure) — page re-renders current state } revalidatePath("/admin/permissions"); redirect("/admin/permissions?saved=1"); } export async function updatePermission(formData: FormData): Promise { const staff = await requireStaff(); const raw = String(formData.get("id") ?? ""); if (!raw) return; const id = BigInt(raw); const permission = String(formData.get("permission") ?? "") .trim() .slice(0, 255); const minRank = parseMinRank(formData); const description = String(formData.get("description") ?? "").trim() || null; if (!permission) return; try { await prisma.websitePermissions.update({ where: { id }, data: { permission, minRank, description, updatedAt: new Date() }, }); await logStaffActivity({ staffId: staff.id, action: "permission_update", description: `Updated permission #${id} ("${permission}" min rank ${minRank})`, targetType: "permission", }); } catch (error) { logServerError("admin.permission_update_failed", error, { staffId: staff.id, permissionId: String(id), }); redirect("/admin/permissions?error=save"); // ignore (e.g. duplicate) — page re-renders current state } revalidatePath("/admin/permissions"); redirect("/admin/permissions?saved=1"); } export async function deletePermission(formData: FormData): Promise { const staff = await requireStaff(); const id = formPositiveBigInt(formData, "id"); if (!id) return; try { const deleted = await prisma.websitePermissions.delete({ where: { id }, select: { permission: true }, }); await logStaffActivity({ staffId: staff.id, action: "permission_delete", description: `Deleted permission #${id} ("${deleted.permission}")`, targetType: "permission", }); } catch (error) { logServerError("admin.permission_delete_failed", error, { staffId: staff.id, permissionId: String(id), }); redirect("/admin/permissions?error=delete"); // ignore (e.g. already removed) } revalidatePath("/admin/permissions"); redirect("/admin/permissions?saved=1"); }