Files
Epicnabbo-Catalogus-Updated…/src/actions/admin-settings.ts
T

154 lines
4.4 KiB
TypeScript

"use server";
import { revalidatePath } from "next/cache";
import { z } from "zod";
import { MANAGED_SETTING_KEYS } from "@/app/admin/settings/cms-settings-config";
import {
type SystemMutationContext,
type SystemMutationOperation,
systemMutationService,
} from "@/features/housekeeping/domains/system/services/mutations";
import { createHousekeepingCapabilityContext } from "@/features/housekeeping/foundation/capability-context";
import { createCorrelationId } from "@/features/housekeeping/foundation/correlation";
import { requirePermissionRateLimited } from "@/lib/admin/guard";
import { actionOk, adminAction } from "@/lib/foundation/action";
import type { AdminActionContext } from "@/lib/foundation/types";
import {
HABBO_GAMEDATA_HOTEL_SETTING_KEY,
normalizeHabboGamedataHotel,
} from "@/lib/habbo-gamedata-hotel";
import { PERMS } from "@/lib/permissions";
const managedKeySet = new Set(MANAGED_SETTING_KEYS);
function normalizeSettingValue(key: string, value: string): string {
if (key === HABBO_GAMEDATA_HOTEL_SETTING_KEY) {
return normalizeHabboGamedataHotel(value);
}
return value;
}
function actionMutationContext(
ctx: Pick<AdminActionContext, "session" | "permissions" | "requestId">,
): SystemMutationContext {
return {
capability: createHousekeepingCapabilityContext(
{
id: Number(ctx.session.user.id),
rank: Number(ctx.session.user.rank),
username: ctx.session.user.username,
},
ctx.permissions,
),
correlationId: String(ctx.requestId),
};
}
function checkedMutationContext(staff: {
id: number;
rank: number;
username: string;
}): SystemMutationContext {
const matches = (slug: string) => slug === PERMS.SETTINGS_EDIT;
return {
capability: createHousekeepingCapabilityContext(staff, {
isSuperAdmin: false,
has: matches,
hasAny: (...slugs) => slugs.some(matches),
hasAll: (...slugs) => slugs.every(matches),
}),
correlationId: createCorrelationId(),
};
}
async function runMutation(
context: SystemMutationContext,
operation: SystemMutationOperation,
input: unknown,
): Promise<unknown> {
const result = await systemMutationService.execute(context, operation, input);
if (!result.ok) throw new Error(result.error.messageKey);
return result.data;
}
const saveManagedSchema = z.object({
settings: z.record(z.string(), z.string()),
});
export const saveManagedSettings = adminAction(
{
permission: PERMS.SETTINGS_EDIT,
schema: saveManagedSchema,
rateLimitKey: "admin-settings-save",
rateLimitMax: 30,
},
async (ctx) => {
const entries = Object.entries(ctx.data.settings)
.filter(([key]) => managedKeySet.has(key))
.map(([key, value]) => [key, normalizeSettingValue(key, value)] as const);
const mutation = (await runMutation(
actionMutationContext(ctx),
"configuration.settings.save",
{ settings: Object.fromEntries(entries) },
)) as { saved: number };
revalidatePath("/admin/settings");
revalidatePath("/admin/catalog");
return actionOk({ saved: mutation.saved });
},
);
export async function updateSetting(formData: FormData): Promise<void> {
const staff = await requirePermissionRateLimited(PERMS.SETTINGS_EDIT);
const key = String(formData.get("key") ?? "")
.normalize("NFC")
.trim();
const value = normalizeSettingValue(
key,
String(formData.get("value") ?? "").normalize("NFC"),
);
if (!key) return;
await runMutation(
checkedMutationContext(staff),
"configuration.setting.update",
{ key, value },
);
revalidatePath("/admin/settings");
}
export async function createSetting(formData: FormData): Promise<void> {
const staff = await requirePermissionRateLimited(PERMS.SETTINGS_EDIT);
const key = String(formData.get("key") ?? "")
.normalize("NFC")
.trim()
.slice(0, 255);
const value = normalizeSettingValue(
key,
String(formData.get("value") ?? "").normalize("NFC"),
);
const comment = String(formData.get("comment") ?? "")
.normalize("NFC")
.trim()
.slice(0, 255);
if (!key) return;
await runMutation(
checkedMutationContext(staff),
"configuration.setting.create",
{ key, value, comment },
);
revalidatePath("/admin/settings");
}
export async function deleteSetting(formData: FormData): Promise<void> {
const staff = await requirePermissionRateLimited(PERMS.SETTINGS_EDIT);
const key = String(formData.get("key") ?? "")
.normalize("NFC")
.trim();
if (!key) return;
await runMutation(
checkedMutationContext(staff),
"configuration.setting.delete",
{ key },
);
revalidatePath("/admin/settings");
}