Files
Epicnabbo-Catalogus-Updated…/src/actions/admin-settings.ts
T
openhands 17847545dd Improvements: remove dead config, fix ESM, add URL validation, unify types, add missing logging
- Remove .prettierrc (dead config, Biome replaces Prettier)
- Rename lighthouserc.json to lighthouserc.cjs with module.exports for ESM compat
- Add logger.warn to empty catch blocks in auth, register, site-settings, prisma-cache, redis, security, rate-limit
- Unify ActionResult type: action-helper.ts uses 'ok' consistent with safe-action-shared.ts
- Add noUnusedLocals + noUnusedParameters to tsconfig + fix 25 pre-existing unused vars
- Replace barrel export src/types/index.ts with direct @/types/common imports
- Make trustHost conditional (development only) in auth.ts
- Add pre-flight URL validation to update-Nitrov3.sh to catch image.library.url misconfigurations
- Improve NITRO_IMAGE_LIBRARY_URL content validation in pre-flight & post-compute checks
2026-07-26 20:28:11 +02:00

121 lines
3.5 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 { requirePermissionRateLimited } from "@/lib/admin/guard";
import { actionOk, adminAction } from "@/lib/foundation/action";
import {
HABBO_GAMEDATA_HOTEL_SETTING_KEY,
normalizeHabboGamedataHotel,
} from "@/lib/habbo-gamedata-hotel";
import { PERMS } from "@/lib/permissions";
import { prisma } from "@/lib/prisma";
import { clearHabboItCache } from "@/lib/services/habbo-furnidata-cache";
import { clearBadgeCache } from "@/lib/services/habboassets";
import { siteSettings } from "@/lib/services/site-settings";
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 bustGamedataCachesIfNeeded(key: string): void {
if (key === HABBO_GAMEDATA_HOTEL_SETTING_KEY) {
clearHabboItCache();
clearBadgeCache();
}
}
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);
await Promise.all(
entries.map(([key, value]) =>
prisma.websiteSetting.upsert({
where: { key },
update: { value },
create: { key, value },
}),
),
);
await siteSettings.reload();
if (entries.some(([key]) => key === HABBO_GAMEDATA_HOTEL_SETTING_KEY)) {
clearHabboItCache();
clearBadgeCache();
}
revalidatePath("/admin/settings");
revalidatePath("/admin/catalog");
return actionOk({ saved: entries.length });
},
);
export async function updateSetting(formData: FormData): Promise<void> {
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 prisma.websiteSetting.update({ where: { key }, data: { value } });
await siteSettings.reload();
bustGamedataCachesIfNeeded(key);
revalidatePath("/admin/settings");
}
export async function createSetting(formData: FormData): Promise<void> {
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 prisma.websiteSetting.upsert({
where: { key },
update: { value },
create: { key, value, comment: comment || null },
});
await siteSettings.reload();
bustGamedataCachesIfNeeded(key);
revalidatePath("/admin/settings");
}
export async function deleteSetting(formData: FormData): Promise<void> {
await requirePermissionRateLimited(PERMS.SETTINGS_EDIT);
const key = String(formData.get("key") ?? "")
.normalize("NFC")
.trim();
if (!key) return;
await prisma.websiteSetting.delete({ where: { key } });
await siteSettings.reload();
bustGamedataCachesIfNeeded(key);
revalidatePath("/admin/settings");
}