Files
EpicNext-Cms/src/actions/admin-alerts.ts
T

72 lines
2.2 KiB
TypeScript

"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<void> {
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<void> {
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");
}