154 lines
4.4 KiB
TypeScript
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");
|
|
}
|