feat(housekeeping): deliver system vertical
This commit is contained in:
1 parent
0117b45d74
commit
3788ecd9f1
33 files changed
+4548
-423
No files matched your search
@@ -1,20 +1,23 @@
|
||||
"use server";
|
||||
|
||||
import { eq } from "drizzle-orm";
|
||||
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 { db, WebsiteSetting } from "@/lib/db";
|
||||
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";
|
||||
import { clearOfficialHabboFurnidataCache } 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);
|
||||
|
||||
@@ -25,11 +28,47 @@ function normalizeSettingValue(key: string, value: string): string {
|
||||
return value;
|
||||
}
|
||||
|
||||
function bustGamedataCachesIfNeeded(key: string): void {
|
||||
if (key === HABBO_GAMEDATA_HOTEL_SETTING_KEY) {
|
||||
clearOfficialHabboFurnidataCache();
|
||||
clearBadgeCache();
|
||||
}
|
||||
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({
|
||||
@@ -47,27 +86,19 @@ export const saveManagedSettings = adminAction(
|
||||
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]) =>
|
||||
db
|
||||
.insert(WebsiteSetting)
|
||||
.values({ key, value })
|
||||
.onDuplicateKeyUpdate({ set: { value } }),
|
||||
),
|
||||
);
|
||||
await siteSettings.reload();
|
||||
if (entries.some(([key]) => key === HABBO_GAMEDATA_HOTEL_SETTING_KEY)) {
|
||||
clearOfficialHabboFurnidataCache();
|
||||
clearBadgeCache();
|
||||
}
|
||||
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: entries.length });
|
||||
return actionOk({ saved: mutation.saved });
|
||||
},
|
||||
);
|
||||
|
||||
export async function updateSetting(formData: FormData): Promise<void> {
|
||||
await requirePermissionRateLimited(PERMS.SETTINGS_EDIT);
|
||||
const staff = await requirePermissionRateLimited(PERMS.SETTINGS_EDIT);
|
||||
const key = String(formData.get("key") ?? "")
|
||||
.normalize("NFC")
|
||||
.trim();
|
||||
@@ -76,17 +107,16 @@ export async function updateSetting(formData: FormData): Promise<void> {
|
||||
String(formData.get("value") ?? "").normalize("NFC"),
|
||||
);
|
||||
if (!key) return;
|
||||
await db
|
||||
.insert(WebsiteSetting)
|
||||
.values({ key, value })
|
||||
.onDuplicateKeyUpdate({ set: { value } });
|
||||
await siteSettings.reload();
|
||||
bustGamedataCachesIfNeeded(key);
|
||||
await runMutation(
|
||||
checkedMutationContext(staff),
|
||||
"configuration.setting.update",
|
||||
{ key, value },
|
||||
);
|
||||
revalidatePath("/admin/settings");
|
||||
}
|
||||
|
||||
export async function createSetting(formData: FormData): Promise<void> {
|
||||
await requirePermissionRateLimited(PERMS.SETTINGS_EDIT);
|
||||
const staff = await requirePermissionRateLimited(PERMS.SETTINGS_EDIT);
|
||||
const key = String(formData.get("key") ?? "")
|
||||
.normalize("NFC")
|
||||
.trim()
|
||||
@@ -100,23 +130,24 @@ export async function createSetting(formData: FormData): Promise<void> {
|
||||
.trim()
|
||||
.slice(0, 255);
|
||||
if (!key) return;
|
||||
await db
|
||||
.insert(WebsiteSetting)
|
||||
.values({ key, value, comment: comment || null })
|
||||
.onDuplicateKeyUpdate({ set: { value } });
|
||||
await siteSettings.reload();
|
||||
bustGamedataCachesIfNeeded(key);
|
||||
await runMutation(
|
||||
checkedMutationContext(staff),
|
||||
"configuration.setting.create",
|
||||
{ key, value, comment },
|
||||
);
|
||||
revalidatePath("/admin/settings");
|
||||
}
|
||||
|
||||
export async function deleteSetting(formData: FormData): Promise<void> {
|
||||
await requirePermissionRateLimited(PERMS.SETTINGS_EDIT);
|
||||
const staff = await requirePermissionRateLimited(PERMS.SETTINGS_EDIT);
|
||||
const key = String(formData.get("key") ?? "")
|
||||
.normalize("NFC")
|
||||
.trim();
|
||||
if (!key) return;
|
||||
await db.delete(WebsiteSetting).where(eq(WebsiteSetting.key, key));
|
||||
await siteSettings.reload();
|
||||
bustGamedataCachesIfNeeded(key);
|
||||
await runMutation(
|
||||
checkedMutationContext(staff),
|
||||
"configuration.setting.delete",
|
||||
{ key },
|
||||
);
|
||||
revalidatePath("/admin/settings");
|
||||
}
|
||||
Reference in new issue
Block a user