"use server"; import { revalidatePath } from "next/cache"; import { type SystemMutationContext, systemMutationService, } from "@/features/housekeeping/domains/system/services/mutations"; import { createHousekeepingCapabilityContext } from "@/features/housekeeping/foundation/capability-context"; import { createCorrelationId } from "@/features/housekeeping/foundation/correlation"; import { requirePermission } from "@/lib/admin/guard"; import { PERMS } from "@/lib/permissions"; function grantedMutationContext( staff: { id: number; rank: number; username: string }, permission: string, ): SystemMutationContext { const matches = (slug: string) => slug === permission; return { capability: createHousekeepingCapabilityContext(staff, { isSuperAdmin: false, has: matches, hasAny: (...slugs) => slugs.some(matches), hasAll: (...slugs) => slugs.every(matches), }), correlationId: createCorrelationId(), }; } /** * Broadcast a hotel-wide alert to every online user via RCON. * * Faithful to AtomCMS: the emulator's `hotelalert` command takes a single * `message` payload. Staff-gated; the message is trimmed/bounded before send. */ export async function sendHotelAlert(formData: FormData): Promise { const staff = await requirePermission(PERMS.NOTIFICATIONS_EDIT); const message = String(formData.get("message") ?? "") .normalize("NFC") .trim() .slice(0, 1000); if (!message) return; try { await systemMutationService.execute( grantedMutationContext(staff, PERMS.NOTIFICATIONS_EDIT), "operations.alert.broadcast", { message }, ); } catch { // Best-effort delivery (dead socket / emulator offline) — never 500 the // admin page. The emulator writes its own alert_logs row on receipt. } revalidatePath("/ase/system/operations/alerts"); } /** Mark every unread ops alert as read. */ export async function markAllAlertsRead(): Promise { const staff = await requirePermission(PERMS.NOTIFICATIONS_VIEW); try { await systemMutationService.execute( grantedMutationContext(staff, PERMS.NOTIFICATIONS_VIEW), "operations.alerts.mark-read", {}, ); } catch { /* ignore */ } revalidatePath("/ase/system/operations/alerts"); }