"use server"; import { revalidatePath } from "next/cache"; import { redirect } from "next/navigation"; import { requirePermission } from "@/lib/admin/guard"; import { PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; import { siteSettings } from "@/lib/services/site-settings"; import { logStaffActivity } from "@/lib/services/staff-activity"; // Radio listener-points settings (website_settings radio_points_* keys). // Mirrors AtomCMS's RadioPoints Filament page: key/value rows in // website_settings that reward listeners for time spent on the radio. Booleans // use the string '0' / '1'. Busts the siteSettings cache so the public radio // pages pick the change up immediately. const POINTS_KEYS = [ "radio_points_enabled", "radio_points_per_minute", "radio_points_currency", "radio_points_max_per_day", "radio_points_min_listeners", ] as const; const ALLOWED_CURRENCIES = new Set([ "credits", "duckets", "diamonds", "points", ]); function str(raw: FormDataEntryValue | null): string { return typeof raw === "string" ? raw : ""; } /** Checkbox/select truthiness → '1' / '0'. */ function boolStr(raw: FormDataEntryValue | null): "0" | "1" { const v = str(raw).trim().toLowerCase(); return v === "1" || v === "true" || v === "on" ? "1" : "0"; } /** Clamp a form value to a non-negative integer string (defaulting to 0). */ function intStr(raw: FormDataEntryValue | null): string { const n = Number(str(raw).trim()); if (!Number.isFinite(n) || n < 0) return "0"; return String(Math.floor(n)); } export async function savePoints(formData: FormData): Promise { const staff = await requirePermission(PERMS.RADIO_EDIT); const currencyRaw = str(formData.get("radio_points_currency")) .trim() .toLowerCase(); const currency = ALLOWED_CURRENCIES.has(currencyRaw) ? currencyRaw : "credits"; const values: Record<(typeof POINTS_KEYS)[number], string> = { radio_points_enabled: boolStr(formData.get("radio_points_enabled")), radio_points_per_minute: intStr(formData.get("radio_points_per_minute")), radio_points_currency: currency, radio_points_max_per_day: intStr(formData.get("radio_points_max_per_day")), radio_points_min_listeners: intStr( formData.get("radio_points_min_listeners"), ), }; try { await prisma.$transaction( POINTS_KEYS.map((key) => prisma.websiteSetting.upsert({ where: { key }, // eslint-disable-next-line security/detect-object-injection -- key from POINTS_KEYS const update: { value: values[key] }, // eslint-disable-next-line security/detect-object-injection -- key from POINTS_KEYS const create: { key, value: values[key], comment: "Radio points" }, }), ), ); siteSettings.reload(); await logStaffActivity({ staffId: staff.id, action: "radio_points_update", description: `Updated radio listener-points settings (enabled=${values.radio_points_enabled}, ${values.radio_points_per_minute}/min ${currency})`, }); } catch { // DB unavailable — fail soft so the action does not throw. } revalidatePath("/admin/radio/points"); redirect("/admin/radio/points?saved=1"); }