72 lines
2.2 KiB
TypeScript
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");
|
|
}
|