feat(housekeeping): deliver system vertical

This commit is contained in:
Simo committed 2026-08-28 23:31:47 +02:00
1 parent 0117b45d74
commit 3788ecd9f1
33 files changed
+4548 -423

No files matched your search

@@ -0,0 +1,228 @@
import { describe, expect, it, vi } from "vitest";
import { PERMS } from "@/lib/permission-slugs";
import { confirmHousekeepingCommand } from "../../../foundation/commands/confirmation";
import type { HousekeepingCommand } from "../../../foundation/commands/registry";
import type { HousekeepingCapabilityContext } from "../../../foundation/contracts";
import {
createSystemMutationService,
type SystemMutationAdapter,
} from "../services/mutations";
import {
createSystemCommands,
SYSTEM_COMMAND_IDS,
SYSTEM_COMMANDS,
} from "./system-commands";
const expectedCommandIds = [
"system.access.rank.create",
"system.access.rank.delete",
"system.access.rank.update",
"system.access.permissions.update",
"system.access.permissions.repair",
"system.configuration.settings.save",
"system.configuration.setting.create",
"system.configuration.setting.update",
"system.configuration.setting.delete",
"system.configuration.emulator-setting.update",
"system.configuration.emulator-text.update",
"system.operations.alerts.mark-read",
"system.operations.alert.broadcast",
"system.operations.rcon.update-catalog",
"system.operations.rcon.update-word-filter",
"system.operations.rcon.update-navigator",
"system.operations.rcon.hotel-alert",
"system.operations.rcon.disconnect-user",
"system.operations.rcon.alert-user",
"system.operations.rcon.forward-user",
"system.operations.rcon.give-credits",
"system.operations.rcon.give-duckets",
"system.operations.rcon.give-diamonds",
"system.operations.rcon.give-badge",
"system.operations.rcon.set-motto",
"system.operations.rcon.set-rank",
"system.operations.rcon.execute-command",
"system.operations.rcon.send-gift",
"system.operations.maintenance.update",
] as const;
const reasonRequiredIds = expectedCommandIds.filter(
(id) =>
id.startsWith("system.access.") ||
id.startsWith("system.configuration.setting") ||
id === "system.configuration.settings.save" ||
id === "system.operations.alert.broadcast" ||
id.startsWith("system.operations.rcon.") ||
id === "system.operations.maintenance.update",
);
const iterableSystemCommands =
SYSTEM_COMMANDS as unknown as readonly HousekeepingCommand<
unknown,
unknown
>[];
function capabilityContext(
granted: readonly string[],
): HousekeepingCapabilityContext {
const permissions = new Set(granted);
return {
actor: { id: 42, username: "operator", rank: 500 },
isSuperAdmin: false,
has: (slug) => permissions.has(slug),
hasAny: (...slugs) => slugs.some((slug) => permissions.has(slug)),
hasAll: (...slugs) => slugs.every((slug) => permissions.has(slug)),
};
}
describe("SYSTEM_COMMANDS", () => {
it("declares the complete deterministic command list", () => {
expect(SYSTEM_COMMAND_IDS).toEqual(expectedCommandIds);
expect(iterableSystemCommands.map((command) => command.id)).toEqual(
expectedCommandIds,
);
expect(
iterableSystemCommands.every((command) => command.risk === "sensitive"),
).toBe(true);
});
it("requires confirmation reasons for access, global settings, RCON, and maintenance", () => {
expect(
iterableSystemCommands
.filter((command) => command.requiresReason)
.map((command) => command.id),
).toEqual(reasonRequiredIds);
for (const command of iterableSystemCommands.filter(
(command) => command.requiresReason,
)) {
expect(
confirmHousekeepingCommand(command, " ", "correlation-test"),
).toMatchObject({
ok: false,
error: {
code: "VALIDATION",
fieldErrors: { reason: ["errors.validation.required"] },
},
});
}
});
it("uses the authoritative mutation capability per command family", () => {
const byId = new Map(
iterableSystemCommands.map((command) => [command.id, command]),
);
expect(byId.get("system.access.rank.create")?.capability.slugs).toEqual([
PERMS.PERMISSIONS_MANAGE,
]);
expect(
byId.get("system.configuration.setting.update")?.capability.slugs,
).toEqual([PERMS.SETTINGS_EDIT]);
expect(
byId.get("system.operations.alerts.mark-read")?.capability.slugs,
).toEqual([PERMS.NOTIFICATIONS_VIEW]);
expect(
byId.get("system.operations.alert.broadcast")?.capability.slugs,
).toEqual([PERMS.NOTIFICATIONS_EDIT]);
expect(
byId.get("system.operations.rcon.update-catalog")?.capability.slugs,
).toEqual([PERMS.RCON_EXECUTE]);
});
it.each([
["system.access.rank.create", { name: " ", level: 3 }],
["system.operations.alert.broadcast", { message: " " }],
["system.operations.rcon.disconnect-user", { userId: 7, username: " " }],
["system.operations.rcon.give-badge", { userId: 7, badge: " " }],
["system.operations.rcon.set-motto", { userId: 7, motto: " " }],
["system.operations.rcon.execute-command", { userId: 7, command: " " }],
] as const)("rejects whitespace-only text for %s", (commandId, input) => {
const command = iterableSystemCommands.find(
(entry) => entry.id === commandId,
);
if (!command) throw new Error(`command missing: ${commandId}`);
expect(command.input.safeParse(input).success).toBe(false);
});
it("delegates parsed command input to the shared mutation service", async () => {
const service = {
execute: vi.fn(async (context, operation, input) => ({
ok: true as const,
data: { context, operation, input },
correlationId: context.correlationId,
})),
};
const commands = createSystemCommands(
service,
) as unknown as readonly HousekeepingCommand<unknown, unknown>[];
const command = commands.find(
(entry) => entry.id === "system.operations.rcon.give-credits",
);
if (!command) throw new Error("command missing");
const parsed = command.input.parse({ userId: 7, amount: 25 });
const result = await command.execute(
{
capability: capabilityContext([PERMS.RCON_EXECUTE]),
correlationId: "command-correlation",
ipAddress: "198.51.100.8",
},
parsed,
);
expect(result).toMatchObject({
ok: true,
data: {
operation: "rcon.give-credits",
input: { userId: 7, amount: 25 },
},
correlationId: "command-correlation",
});
});
});
describe("System mutation service boundary", () => {
it("returns FORBIDDEN without invoking the adapter when permission is absent", async () => {
const execute = vi.fn(async () => ({ saved: true }));
const service = createSystemMutationService({ execute });
const result = await service.execute(
{
capability: capabilityContext([]),
correlationId: "denied-correlation",
},
"configuration.setting.update",
{ key: "hotel_name", value: "Hotel" },
);
expect(result).toMatchObject({
ok: false,
error: { code: "FORBIDDEN" },
correlationId: "denied-correlation",
});
expect(execute).not.toHaveBeenCalled();
});
it("maps adapter outages to DEPENDENCY_UNAVAILABLE and preserves correlation", async () => {
const adapter: SystemMutationAdapter = {
execute: async () => {
throw new Error("database unavailable");
},
};
const service = createSystemMutationService(adapter);
const result = await service.execute(
{
capability: capabilityContext([PERMS.SETTINGS_EDIT]),
correlationId: "failure-correlation",
},
"configuration.setting.update",
{ key: "hotel_name", value: "Hotel" },
);
expect(result).toMatchObject({
ok: false,
error: { code: "DEPENDENCY_UNAVAILABLE" },
correlationId: "failure-correlation",
});
});
});
@@ -0,0 +1,315 @@
import "server-only";
import { z } from "zod";
import { PERMS } from "@/lib/permission-slugs";
import type { HousekeepingCommand } from "../../../foundation/commands/registry";
import { anyCapability } from "../../../foundation/contracts";
import {
type SystemMutationOperation,
type SystemMutationService,
systemMutationService,
} from "../services/mutations";
export const SYSTEM_COMMAND_IDS = [
"system.access.rank.create",
"system.access.rank.delete",
"system.access.rank.update",
"system.access.permissions.update",
"system.access.permissions.repair",
"system.configuration.settings.save",
"system.configuration.setting.create",
"system.configuration.setting.update",
"system.configuration.setting.delete",
"system.configuration.emulator-setting.update",
"system.configuration.emulator-text.update",
"system.operations.alerts.mark-read",
"system.operations.alert.broadcast",
"system.operations.rcon.update-catalog",
"system.operations.rcon.update-word-filter",
"system.operations.rcon.update-navigator",
"system.operations.rcon.hotel-alert",
"system.operations.rcon.disconnect-user",
"system.operations.rcon.alert-user",
"system.operations.rcon.forward-user",
"system.operations.rcon.give-credits",
"system.operations.rcon.give-duckets",
"system.operations.rcon.give-diamonds",
"system.operations.rcon.give-badge",
"system.operations.rcon.set-motto",
"system.operations.rcon.set-rank",
"system.operations.rcon.execute-command",
"system.operations.rcon.send-gift",
"system.operations.maintenance.update",
] as const;
interface CommandOptions<I> {
readonly id: (typeof SYSTEM_COMMAND_IDS)[number];
readonly operation: SystemMutationOperation;
readonly capability: string;
readonly input: z.ZodType<I>;
readonly requiresReason: boolean;
readonly attempts?: number;
}
function systemCommand<I>(
service: Pick<SystemMutationService, "execute">,
options: CommandOptions<I>,
): HousekeepingCommand<I, unknown> {
return {
id: options.id,
owner: "system",
risk: "sensitive",
capability: anyCapability(options.capability),
input: options.input,
requiresReason: options.requiresReason,
rateLimit: { attempts: options.attempts ?? 10, windowMs: 60_000 },
execute: (context, input) =>
service.execute(
{
capability: context.capability,
correlationId: context.correlationId,
},
options.operation,
input,
),
};
}
const empty = z.object({});
const positiveId = z.number().int().positive();
const requiredText = (max: number) => z.string().min(1).max(max).regex(/\S/u);
const settingInput = z.object({
key: requiredText(255),
value: z.string().max(65_535),
});
export function createSystemCommands(
service: Pick<SystemMutationService, "execute">,
) {
return [
systemCommand(service, {
id: "system.access.rank.create",
operation: "access.rank.create",
capability: PERMS.PERMISSIONS_MANAGE,
input: z.object({ name: requiredText(25), level: positiveId }),
requiresReason: true,
}),
systemCommand(service, {
id: "system.access.rank.delete",
operation: "access.rank.delete",
capability: PERMS.PERMISSIONS_MANAGE,
input: z.object({ id: positiveId }),
requiresReason: true,
}),
systemCommand(service, {
id: "system.access.rank.update",
operation: "access.rank.update",
capability: PERMS.PERMISSIONS_MANAGE,
input: z.object({
id: positiveId,
fields: z.record(z.string(), z.union([z.string(), z.number()])),
}),
requiresReason: true,
}),
systemCommand(service, {
id: "system.access.permissions.update",
operation: "access.permissions.update",
capability: PERMS.PERMISSIONS_MANAGE,
input: z.object({
roleId: positiveId,
permissionSlugs: z.array(requiredText(160)).max(500),
}),
requiresReason: true,
}),
systemCommand(service, {
id: "system.access.permissions.repair",
operation: "access.permissions.repair",
capability: PERMS.PERMISSIONS_MANAGE,
input: empty,
requiresReason: true,
}),
systemCommand(service, {
id: "system.configuration.settings.save",
operation: "configuration.settings.save",
capability: PERMS.SETTINGS_EDIT,
input: z.object({ settings: z.record(z.string(), z.string()) }),
requiresReason: true,
}),
systemCommand(service, {
id: "system.configuration.setting.create",
operation: "configuration.setting.create",
capability: PERMS.SETTINGS_EDIT,
input: settingInput.extend({ comment: z.string().max(255).optional() }),
requiresReason: true,
}),
systemCommand(service, {
id: "system.configuration.setting.update",
operation: "configuration.setting.update",
capability: PERMS.SETTINGS_EDIT,
input: settingInput,
requiresReason: true,
}),
systemCommand(service, {
id: "system.configuration.setting.delete",
operation: "configuration.setting.delete",
capability: PERMS.SETTINGS_EDIT,
input: z.object({ key: requiredText(255) }),
requiresReason: true,
}),
systemCommand(service, {
id: "system.configuration.emulator-setting.update",
operation: "configuration.emulator-setting.update",
capability: PERMS.SETTINGS_EDIT,
input: z.object({ key: requiredText(100), value: z.string().max(512) }),
requiresReason: false,
}),
systemCommand(service, {
id: "system.configuration.emulator-text.update",
operation: "configuration.emulator-text.update",
capability: PERMS.SETTINGS_EDIT,
input: z.object({ key: requiredText(100), value: z.string().max(4096) }),
requiresReason: false,
}),
systemCommand(service, {
id: "system.operations.alerts.mark-read",
operation: "operations.alerts.mark-read",
capability: PERMS.NOTIFICATIONS_VIEW,
input: empty,
requiresReason: false,
}),
systemCommand(service, {
id: "system.operations.alert.broadcast",
operation: "operations.alert.broadcast",
capability: PERMS.NOTIFICATIONS_EDIT,
input: z.object({ message: requiredText(1000) }),
requiresReason: true,
}),
systemCommand(service, {
id: "system.operations.rcon.update-catalog",
operation: "rcon.update-catalog",
capability: PERMS.RCON_EXECUTE,
input: empty,
requiresReason: true,
attempts: 5,
}),
systemCommand(service, {
id: "system.operations.rcon.update-word-filter",
operation: "rcon.update-word-filter",
capability: PERMS.RCON_EXECUTE,
input: empty,
requiresReason: true,
attempts: 5,
}),
systemCommand(service, {
id: "system.operations.rcon.update-navigator",
operation: "rcon.update-navigator",
capability: PERMS.RCON_EXECUTE,
input: empty,
requiresReason: true,
attempts: 5,
}),
systemCommand(service, {
id: "system.operations.rcon.hotel-alert",
operation: "rcon.hotel-alert",
capability: PERMS.RCON_EXECUTE,
input: z.object({ message: requiredText(512) }),
requiresReason: true,
attempts: 5,
}),
systemCommand(service, {
id: "system.operations.rcon.disconnect-user",
operation: "rcon.disconnect-user",
capability: PERMS.RCON_EXECUTE,
input: z.object({ userId: positiveId, username: requiredText(255) }),
requiresReason: true,
attempts: 5,
}),
systemCommand(service, {
id: "system.operations.rcon.alert-user",
operation: "rcon.alert-user",
capability: PERMS.RCON_EXECUTE,
input: z.object({ userId: positiveId, message: requiredText(512) }),
requiresReason: true,
attempts: 5,
}),
systemCommand(service, {
id: "system.operations.rcon.forward-user",
operation: "rcon.forward-user",
capability: PERMS.RCON_EXECUTE,
input: z.object({ userId: positiveId, roomId: positiveId }),
requiresReason: true,
attempts: 5,
}),
...[
["give-credits", "rcon.give-credits"],
["give-duckets", "rcon.give-duckets"],
["give-diamonds", "rcon.give-diamonds"],
].map(([suffix, operation]) =>
systemCommand(service, {
id: `system.operations.rcon.${suffix}` as (typeof SYSTEM_COMMAND_IDS)[number],
operation: operation as SystemMutationOperation,
capability: PERMS.RCON_EXECUTE,
input: z.object({ userId: positiveId, amount: positiveId }),
requiresReason: true,
attempts: 5,
}),
),
systemCommand(service, {
id: "system.operations.rcon.give-badge",
operation: "rcon.give-badge",
capability: PERMS.RCON_EXECUTE,
input: z.object({ userId: positiveId, badge: requiredText(32) }),
requiresReason: true,
attempts: 5,
}),
systemCommand(service, {
id: "system.operations.rcon.set-motto",
operation: "rcon.set-motto",
capability: PERMS.RCON_EXECUTE,
input: z.object({ userId: positiveId, motto: requiredText(127) }),
requiresReason: true,
attempts: 5,
}),
systemCommand(service, {
id: "system.operations.rcon.set-rank",
operation: "rcon.set-rank",
capability: PERMS.RCON_EXECUTE,
input: z.object({ userId: positiveId, rank: positiveId.max(9999) }),
requiresReason: true,
attempts: 5,
}),
systemCommand(service, {
id: "system.operations.rcon.execute-command",
operation: "rcon.execute-command",
capability: PERMS.RCON_EXECUTE,
input: z.object({ userId: positiveId, command: requiredText(100) }),
requiresReason: true,
attempts: 5,
}),
systemCommand(service, {
id: "system.operations.rcon.send-gift",
operation: "rcon.send-gift",
capability: PERMS.RCON_EXECUTE,
input: z.object({
userId: positiveId,
itemId: positiveId,
message: z.string().max(255).optional(),
}),
requiresReason: true,
attempts: 5,
}),
systemCommand(service, {
id: "system.operations.maintenance.update",
operation: "operations.maintenance.update",
capability: PERMS.SETTINGS_EDIT,
input: z.object({
enabled: z.boolean(),
message: z.string().max(65_535),
minimumLoginRank: z.number().int().min(0),
}),
requiresReason: true,
}),
] as const;
}
export const SYSTEM_COMMANDS = createSystemCommands(systemMutationService);
@@ -3,6 +3,7 @@ import {
anyCapability,
type HousekeepingDomainManifest,
} from "../../foundation/contracts";
import { SYSTEM_ROUTES } from "./routes";
export const systemManifest = {
id: "system",
@@ -21,7 +22,7 @@ export const systemManifest = {
PERMS.SETTINGS_EDIT,
PERMS.NOTIFICATIONS_EDIT,
),
routes: [],
routes: SYSTEM_ROUTES,
searchProviders: [],
inboxSources: [],
widgets: [],
@@ -0,0 +1,193 @@
import type { ReactNode } from "react";
import {
createCorrelationId,
fail,
type HousekeepingResult,
} from "../../../foundation/contracts";
import { HousekeepingPageShell } from "../../../foundation/page/housekeeping-page-shell";
import { HousekeepingPageState } from "../../../foundation/page/housekeeping-page-state";
import type { HousekeepingPageInput } from "../../../route-handlers";
import {
type SystemAccessQueryData,
type SystemAccessQueryInput,
systemAccessQuery,
} from "../queries/access";
interface SystemAccessPageProps {
readonly result?: HousekeepingResult<SystemAccessQueryData>;
}
function state(
kind: "loading" | "empty" | "error",
title: string,
description: string,
) {
return (
<div data-housekeeping-state={kind}>
<HousekeepingPageState
state={kind}
title={title}
description={description}
/>
</div>
);
}
function forbidden() {
return (
<section
role="alert"
data-housekeeping-state="forbidden"
className="rounded-lg border border-[var(--admin-error)] bg-[var(--admin-surface)] p-4"
>
<h2 className="font-medium text-[var(--admin-text)]">Access denied</h2>
<p className="mt-1 text-sm text-[var(--admin-text-muted)]">
Your account cannot manage permissions.
</p>
</section>
);
}
function accessContent(data: SystemAccessQueryData) {
if (data.kind === "permission-detail") {
return (
<div className="grid gap-4 lg:grid-cols-2">
<section className="rounded-lg border border-[var(--admin-border)] bg-[var(--admin-surface)] p-4">
<h2 className="font-medium text-[var(--admin-text)]">
{data.rank.name}
</h2>
<dl className="mt-3 grid grid-cols-2 gap-2 text-sm text-[var(--admin-text-muted)]">
<dt>Rank ID</dt>
<dd>{data.rank.id}</dd>
<dt>Level</dt>
<dd>{data.rank.level}</dd>
<dt>Users</dt>
<dd>{data.rank.userCount}</dd>
</dl>
</section>
<section className="rounded-lg border border-[var(--admin-border)] bg-[var(--admin-surface)] p-4">
<h2 className="font-medium text-[var(--admin-text)]">
CMS permissions
</h2>
<ul className="mt-3 space-y-1 text-sm text-[var(--admin-text-muted)]">
{data.rank.cmsRole?.permissionSlugs.map((slug) => (
<li key={slug}>{slug}</li>
)) ?? <li>No CMS role is linked.</li>}
</ul>
</section>
</div>
);
}
return (
<div className="grid gap-4 lg:grid-cols-[2fr_1fr]">
<section className="rounded-lg border border-[var(--admin-border)] bg-[var(--admin-surface)] p-4">
<h2 className="font-medium text-[var(--admin-text)]">
Permission ranks
</h2>
<ul className="mt-3 divide-y divide-[var(--admin-border)]">
{data.ranks.map((rank) => (
<li
key={rank.id}
className="flex items-center justify-between py-2 text-sm"
>
<a
href={`/ase/system/access/permissions/${rank.id}`}
className="font-medium text-[var(--admin-text)]"
>
{rank.name}
</a>
<span className="text-[var(--admin-text-muted)]">
{rank.userCount} users
</span>
</li>
))}
</ul>
</section>
<section className="rounded-lg border border-[var(--admin-border)] bg-[var(--admin-surface)] p-4">
<h2 className="font-medium text-[var(--admin-text)]">ACL summary</h2>
<p className="mt-3 text-sm text-[var(--admin-text-muted)]">
{data.acl.roles} roles and {data.acl.permissions} permissions
</p>
</section>
</div>
);
}
export function SystemAccessPage({ result }: SystemAccessPageProps) {
let body: ReactNode;
if (!result) {
body = state(
"loading",
"Loading access data",
"Reading ranks and ACL assignments.",
);
} else if (!result.ok) {
body =
result.error.code === "FORBIDDEN"
? forbidden()
: state(
"error",
"Access data unavailable",
`Request ${result.correlationId} could not be completed.`,
);
} else {
const data = result.data;
const empty =
data.kind === "permissions" &&
data.ranks.length === 0 &&
data.acl.roles === 0 &&
data.acl.permissions === 0;
if (empty) {
body = state(
"empty",
"No access data",
"No ranks or ACL assignments were returned.",
);
} else {
body = (
<div
data-housekeeping-state={
data.partialDependencies.length > 0 ? "partial" : "ready"
}
className="space-y-4"
>
{data.partialDependencies.length > 0 ? (
<HousekeepingPageState
state="partial"
partialLabel="Partial data"
title="Some access data is unavailable"
description={`Unavailable: ${data.partialDependencies.join(", ")}`}
/>
) : null}
{accessContent(data)}
</div>
);
}
}
return (
<HousekeepingPageShell
title="Access control"
description="Review emulator ranks and CMS ACL assignments."
>
{body}
</HousekeepingPageShell>
);
}
export async function renderSystemAccessPage(input: HousekeepingPageInput) {
const queryInput: SystemAccessQueryInput | null =
input.match.routeId === "system.access.permissions"
? { routeId: "system.access.permissions" }
: input.match.routeId === "system.access.permission-detail"
? {
routeId: "system.access.permission-detail",
rankId: input.match.params.id ?? "",
}
: null;
const result = queryInput
? await systemAccessQuery.run(input.context, queryInput)
: fail("NOT_FOUND", "errors.housekeeping.notFound", createCorrelationId());
return <SystemAccessPage result={result} />;
}
@@ -0,0 +1,165 @@
import type { ReactNode } from "react";
import {
createCorrelationId,
fail,
type HousekeepingResult,
} from "../../../foundation/contracts";
import { HousekeepingPageShell } from "../../../foundation/page/housekeeping-page-shell";
import { HousekeepingPageState } from "../../../foundation/page/housekeeping-page-state";
import type { HousekeepingPageInput } from "../../../route-handlers";
import {
type SystemConfigurationQueryData,
type SystemConfigurationQueryInput,
type SystemSettingRow,
systemConfigurationQuery,
} from "../queries/configuration";
interface SystemConfigurationPageProps {
readonly result?: HousekeepingResult<SystemConfigurationQueryData>;
}
function pageState(
kind: "loading" | "empty" | "error",
title: string,
description: string,
) {
return (
<div data-housekeeping-state={kind}>
<HousekeepingPageState
state={kind}
title={title}
description={description}
/>
</div>
);
}
function forbiddenState() {
return (
<section
role="alert"
data-housekeeping-state="forbidden"
className="rounded-lg border border-[var(--admin-error)] bg-[var(--admin-surface)] p-4"
>
<h2 className="font-medium text-[var(--admin-text)]">Access denied</h2>
<p className="mt-1 text-sm text-[var(--admin-text-muted)]">
Your account cannot view system configuration.
</p>
</section>
);
}
function settingList(title: string, rows: readonly SystemSettingRow[]) {
return (
<section className="rounded-lg border border-[var(--admin-border)] bg-[var(--admin-surface)] p-4">
<h2 className="font-medium text-[var(--admin-text)]">{title}</h2>
<dl className="mt-3 divide-y divide-[var(--admin-border)]">
{rows.map((row) => (
<div
key={row.key}
className="grid gap-1 py-2 text-sm md:grid-cols-[minmax(12rem,1fr)_2fr]"
>
<dt className="font-medium text-[var(--admin-text)]">{row.key}</dt>
<dd className="break-words text-[var(--admin-text-muted)]">
{row.value}
</dd>
</div>
))}
</dl>
</section>
);
}
function configurationContent(data: SystemConfigurationQueryData) {
return data.kind === "settings" ? (
settingList("Website settings", data.settings)
) : (
<div className="grid gap-4 xl:grid-cols-2">
{settingList("Emulator settings", data.settings)}
<div className="space-y-2">
{settingList("Emulator texts", data.texts)}
<p className="text-xs text-[var(--admin-text-muted)]">
Showing {data.texts.length} of {data.textsTotal} text entries.
</p>
</div>
</div>
);
}
export function SystemConfigurationPage({
result,
}: SystemConfigurationPageProps) {
let body: ReactNode;
if (!result) {
body = pageState(
"loading",
"Loading configuration",
"Reading CMS and emulator settings.",
);
} else if (!result.ok) {
body =
result.error.code === "FORBIDDEN"
? forbiddenState()
: pageState(
"error",
"Configuration unavailable",
`Request ${result.correlationId} could not be completed.`,
);
} else {
const data = result.data;
const empty =
data.kind === "settings"
? data.settings.length === 0
: data.settings.length === 0 && data.texts.length === 0;
if (empty) {
body = pageState(
"empty",
"No configuration entries",
"The selected configuration source returned no entries.",
);
} else {
body = (
<div
data-housekeeping-state={
data.partialDependencies.length > 0 ? "partial" : "ready"
}
className="space-y-4"
>
{data.partialDependencies.length > 0 ? (
<HousekeepingPageState
state="partial"
partialLabel="Partial data"
title="Some configuration data is unavailable"
description={`Unavailable: ${data.partialDependencies.join(", ")}`}
/>
) : null}
{configurationContent(data)}
</div>
);
}
}
return (
<HousekeepingPageShell
title="System configuration"
description="Review CMS and emulator configuration sources."
>
{body}
</HousekeepingPageShell>
);
}
export async function renderSystemConfigurationPage(
input: HousekeepingPageInput,
) {
const queryInput: SystemConfigurationQueryInput | null =
input.match.routeId === "system.configuration.settings"
? { routeId: "system.configuration.settings" }
: input.match.routeId === "system.configuration.emulator"
? { routeId: "system.configuration.emulator" }
: null;
const result = queryInput
? await systemConfigurationQuery.run(input.context, queryInput)
: fail("NOT_FOUND", "errors.housekeeping.notFound", createCorrelationId());
return <SystemConfigurationPage result={result} />;
}
@@ -0,0 +1,212 @@
import type { ReactNode } from "react";
import {
createCorrelationId,
fail,
type HousekeepingResult,
} from "../../../foundation/contracts";
import { HousekeepingPageShell } from "../../../foundation/page/housekeeping-page-shell";
import { HousekeepingPageState } from "../../../foundation/page/housekeeping-page-state";
import type { HousekeepingPageInput } from "../../../route-handlers";
import {
type SystemObservabilityQueryData,
type SystemObservabilityQueryInput,
systemObservabilityQuery,
} from "../queries/observability";
interface SystemObservabilityPageProps {
readonly result?: HousekeepingResult<SystemObservabilityQueryData>;
}
function pageState(
kind: "loading" | "empty" | "error",
title: string,
description: string,
) {
return (
<div data-housekeeping-state={kind}>
<HousekeepingPageState
state={kind}
title={title}
description={description}
/>
</div>
);
}
function forbiddenState() {
return (
<section
role="alert"
data-housekeeping-state="forbidden"
className="rounded-lg border border-[var(--admin-error)] bg-[var(--admin-surface)] p-4"
>
<h2 className="font-medium text-[var(--admin-text)]">Access denied</h2>
<p className="mt-1 text-sm text-[var(--admin-text-muted)]">
Your account cannot view this observability source.
</p>
</section>
);
}
function observabilityContent(data: SystemObservabilityQueryData) {
if (data.kind === "devops") {
return (
<div className="grid gap-4 lg:grid-cols-2">
<section className="rounded-lg border border-[var(--admin-border)] bg-[var(--admin-surface)] p-4">
<h2 className="font-medium text-[var(--admin-text)]">
Service health
</h2>
{data.health ? (
<dl className="mt-3 grid grid-cols-2 gap-2 text-sm text-[var(--admin-text-muted)]">
<dt>Database</dt>
<dd>{data.health.dbOk ? "Available" : "Unavailable"}</dd>
<dt>Redis</dt>
<dd>
{data.health.redisOk === null
? "Not configured"
: data.health.redisOk
? "Available"
: "Unavailable"}
</dd>
<dt>Emulator</dt>
<dd>{data.health.emulatorOk ? "Available" : "Unavailable"}</dd>
<dt>Online users</dt>
<dd>{data.health.onlineUsers}</dd>
</dl>
) : (
<p className="mt-3 text-sm text-[var(--admin-text-muted)]">
Health data is unavailable.
</p>
)}
</section>
{observationRows("Recent emulator errors", data.errors)}
</div>
);
}
if (data.kind === "devops-errors") {
return observationRows("Emulator errors", data.errors);
}
return (
<div className="space-y-4">
<div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
{data.metrics.map((metric) => (
<section
key={metric.label}
className="rounded-lg border border-[var(--admin-border)] bg-[var(--admin-surface)] p-4"
>
<p className="text-xs text-[var(--admin-text-muted)]">
{metric.label}
</p>
<p className="mt-1 text-xl font-semibold text-[var(--admin-text)]">
{metric.value}
</p>
</section>
))}
</div>
{observationRows(
data.kind === "logs" ? "Recent entries" : "Details",
data.rows,
)}
</div>
);
}
function observationRows(
title: string,
rows: readonly { id: string; primary: string; secondary?: string }[],
) {
return (
<section className="rounded-lg border border-[var(--admin-border)] bg-[var(--admin-surface)] p-4">
<h2 className="font-medium text-[var(--admin-text)]">{title}</h2>
<ul className="mt-3 divide-y divide-[var(--admin-border)]">
{rows.map((row) => (
<li key={row.id} className="py-2 text-sm">
<p className="font-medium text-[var(--admin-text)]">
{row.primary}
</p>
{row.secondary ? (
<p className="break-words text-[var(--admin-text-muted)]">
{row.secondary}
</p>
) : null}
</li>
))}
</ul>
</section>
);
}
function isEmpty(data: SystemObservabilityQueryData): boolean {
if (data.kind === "devops")
return data.health === null && data.errors.length === 0;
if (data.kind === "devops-errors") return data.errors.length === 0;
return data.metrics.length === 0 && data.rows.length === 0;
}
export function SystemObservabilityPage({
result,
}: SystemObservabilityPageProps) {
let body: ReactNode;
if (!result) {
body = pageState(
"loading",
"Loading observability data",
"Reading metrics and operational logs.",
);
} else if (!result.ok) {
body =
result.error.code === "FORBIDDEN"
? forbiddenState()
: pageState(
"error",
"Observability data unavailable",
`Request ${result.correlationId} could not be completed.`,
);
} else if (isEmpty(result.data)) {
body = pageState(
"empty",
"No observations",
"The selected source returned no records.",
);
} else {
body = (
<div
data-housekeeping-state={
result.data.partialDependencies.length > 0 ? "partial" : "ready"
}
className="space-y-4"
>
{result.data.partialDependencies.length > 0 ? (
<HousekeepingPageState
state="partial"
partialLabel="Partial data"
title="Some observability data is unavailable"
description={`Unavailable: ${result.data.partialDependencies.join(", ")}`}
/>
) : null}
{observabilityContent(result.data)}
</div>
);
}
return (
<HousekeepingPageShell
title="System observability"
description="Review metrics, service health, and operational records."
>
{body}
</HousekeepingPageShell>
);
}
export async function renderSystemObservabilityPage(
input: HousekeepingPageInput,
) {
const routeId = input.match
.routeId as SystemObservabilityQueryInput["routeId"];
const supported = routeId.startsWith("system.observability.");
const result = supported
? await systemObservabilityQuery.run(input.context, { routeId })
: fail("NOT_FOUND", "errors.housekeeping.notFound", createCorrelationId());
return <SystemObservabilityPage result={result} />;
}
@@ -0,0 +1,182 @@
import type { ReactNode } from "react";
import {
createCorrelationId,
fail,
type HousekeepingResult,
} from "../../../foundation/contracts";
import { HousekeepingPageShell } from "../../../foundation/page/housekeeping-page-shell";
import { HousekeepingPageState } from "../../../foundation/page/housekeeping-page-state";
import type { HousekeepingPageInput } from "../../../route-handlers";
import {
type SystemOperationsQueryData,
type SystemOperationsQueryInput,
systemOperationsQuery,
} from "../queries/operations";
interface SystemOperationsPageProps {
readonly result?: HousekeepingResult<SystemOperationsQueryData>;
}
function pageState(
kind: "loading" | "empty" | "error",
title: string,
description: string,
) {
return (
<div data-housekeeping-state={kind}>
<HousekeepingPageState
state={kind}
title={title}
description={description}
/>
</div>
);
}
function forbiddenState() {
return (
<section
role="alert"
data-housekeeping-state="forbidden"
className="rounded-lg border border-[var(--admin-error)] bg-[var(--admin-surface)] p-4"
>
<h2 className="font-medium text-[var(--admin-text)]">Access denied</h2>
<p className="mt-1 text-sm text-[var(--admin-text-muted)]">
Your account cannot use this system operation.
</p>
</section>
);
}
function maintenanceSummary(data: {
readonly enabled: boolean;
readonly message: string;
readonly minimumLoginRank: number;
}) {
return (
<section className="rounded-lg border border-[var(--admin-border)] bg-[var(--admin-surface)] p-4">
<h2 className="font-medium text-[var(--admin-text)]">Maintenance</h2>
<dl className="mt-3 grid grid-cols-2 gap-2 text-sm text-[var(--admin-text-muted)]">
<dt>Status</dt>
<dd>{data.enabled ? "Enabled" : "Disabled"}</dd>
<dt>Minimum login rank</dt>
<dd>{data.minimumLoginRank}</dd>
<dt>Message</dt>
<dd>{data.message || "No message configured"}</dd>
</dl>
</section>
);
}
function operationsContent(data: SystemOperationsQueryData) {
if (data.kind === "alerts") {
return (
<section className="rounded-lg border border-[var(--admin-border)] bg-[var(--admin-surface)] p-4">
<h2 className="font-medium text-[var(--admin-text)]">
Operational alerts
</h2>
<ul className="mt-3 divide-y divide-[var(--admin-border)]">
{data.alerts.map((alert) => (
<li key={alert.id} className="py-2 text-sm">
<p className="font-medium text-[var(--admin-text)]">
{alert.type} - {alert.severity}
</p>
<p className="text-[var(--admin-text-muted)]">{alert.message}</p>
</li>
))}
</ul>
</section>
);
}
if (data.kind === "maintenance") return maintenanceSummary(data.maintenance);
return (
<div className="grid gap-4 lg:grid-cols-3">
<section className="rounded-lg border border-[var(--admin-border)] bg-[var(--admin-surface)] p-4">
<h2 className="font-medium text-[var(--admin-text)]">Command center</h2>
<p className="mt-3 text-sm text-[var(--admin-text-muted)]">
{data.onlineUsers.count} users online
</p>
<ul className="mt-2 space-y-1 text-sm text-[var(--admin-text-muted)]">
{data.onlineUsers.users.map((user) => (
<li key={user.id}>{user.username}</li>
))}
</ul>
</section>
<section className="rounded-lg border border-[var(--admin-border)] bg-[var(--admin-surface)] p-4">
<h2 className="font-medium text-[var(--admin-text)]">Service health</h2>
<p className="mt-3 text-sm text-[var(--admin-text-muted)]">
{data.health
? `Database ${data.health.dbOk ? "available" : "unavailable"}; emulator ${data.health.emulatorOk ? "available" : "unavailable"}.`
: "Health data is unavailable."}
</p>
</section>
{data.maintenance ? maintenanceSummary(data.maintenance) : null}
</div>
);
}
export function SystemOperationsPage({ result }: SystemOperationsPageProps) {
let body: ReactNode;
if (!result) {
body = pageState(
"loading",
"Loading system operations",
"Reading operational status and queues.",
);
} else if (!result.ok) {
body =
result.error.code === "FORBIDDEN"
? forbiddenState()
: pageState(
"error",
"System operations unavailable",
`Request ${result.correlationId} could not be completed.`,
);
} else if (result.data.kind === "alerts" && result.data.alerts.length === 0) {
body = pageState(
"empty",
"No operational alerts",
"There are no alerts to review.",
);
} else {
body = (
<div
data-housekeeping-state={
result.data.partialDependencies.length > 0 ? "partial" : "ready"
}
className="space-y-4"
>
{result.data.partialDependencies.length > 0 ? (
<HousekeepingPageState
state="partial"
partialLabel="Partial data"
title="Some operation data is unavailable"
description={`Unavailable: ${result.data.partialDependencies.join(", ")}`}
/>
) : null}
{operationsContent(result.data)}
</div>
);
}
return (
<HousekeepingPageShell
title="System operations"
description="Review alerts, command-center status, and maintenance availability."
>
{body}
</HousekeepingPageShell>
);
}
export async function renderSystemOperationsPage(input: HousekeepingPageInput) {
const routeId = input.match.routeId as SystemOperationsQueryInput["routeId"];
const supported =
routeId === "system.operations.alerts" ||
routeId === "system.operations.command-center" ||
routeId === "system.operations.maintenance";
const result = supported
? await systemOperationsQuery.run(input.context, { routeId })
: fail("NOT_FOUND", "errors.housekeeping.notFound", createCorrelationId());
return <SystemOperationsPage result={result} />;
}
@@ -0,0 +1,157 @@
import { renderToStaticMarkup } from "react-dom/server";
import { describe, expect, it } from "vitest";
import {
fail,
type HousekeepingResult,
ok,
} from "../../../foundation/contracts";
import { SystemAccessPage } from "./access";
import { SystemConfigurationPage } from "./configuration";
import { SystemObservabilityPage } from "./observability";
import { SystemOperationsPage } from "./operations";
const correlationId = "system-pages-test";
type PageCase = {
readonly name: string;
readonly render: (result?: HousekeepingResult<unknown>) => string;
readonly empty: unknown;
readonly partial: unknown;
readonly ready: unknown;
};
const pageCases: readonly PageCase[] = [
{
name: "access",
render: (result) =>
renderToStaticMarkup(<SystemAccessPage result={result as never} />),
empty: {
kind: "permissions",
ranks: [],
acl: { roles: 0, permissions: 0 },
partialDependencies: [],
},
partial: {
kind: "permissions",
ranks: [{ id: 4, name: "Moderator", level: 4, userCount: 2 }],
acl: { roles: 0, permissions: 0 },
partialDependencies: ["acl"],
},
ready: {
kind: "permissions",
ranks: [{ id: 4, name: "Moderator", level: 4, userCount: 2 }],
acl: { roles: 3, permissions: 18 },
partialDependencies: [],
},
},
{
name: "configuration",
render: (result) =>
renderToStaticMarkup(
<SystemConfigurationPage result={result as never} />,
),
empty: { kind: "settings", settings: [], partialDependencies: [] },
partial: {
kind: "emulator",
settings: [{ key: "hotel.name", value: "Epic" }],
texts: [],
textsTotal: 0,
partialDependencies: ["emulator-texts"],
},
ready: {
kind: "settings",
settings: [{ key: "hotel.name", value: "Epic" }],
partialDependencies: [],
},
},
{
name: "observability",
render: (result) =>
renderToStaticMarkup(
<SystemObservabilityPage result={result as never} />,
),
empty: {
kind: "logs",
metrics: [],
rows: [],
partialDependencies: [],
},
partial: {
kind: "devops",
health: null,
errors: [{ id: "1", primary: "Connection error" }],
partialDependencies: ["health"],
},
ready: {
kind: "analytics",
metrics: [{ label: "Online users", value: 12 }],
rows: [],
partialDependencies: [],
},
},
{
name: "operations",
render: (result) =>
renderToStaticMarkup(<SystemOperationsPage result={result as never} />),
empty: { kind: "alerts", alerts: [], partialDependencies: [] },
partial: {
kind: "command-center",
health: null,
onlineUsers: { count: 0, users: [] },
maintenance: {
enabled: false,
message: "",
minimumLoginRank: 5,
},
partialDependencies: ["health"],
},
ready: {
kind: "alerts",
alerts: [
{
id: 7,
severity: "warning",
type: "emulator",
message: "Connection restored",
isRead: false,
},
],
partialDependencies: [],
},
},
];
describe.each(pageCases)(
"System $name page",
({ render, empty, partial, ready }) => {
it("renders loading, forbidden, and dependency errors explicitly", () => {
expect(render()).toContain('data-housekeeping-state="loading"');
expect(
render(
fail("FORBIDDEN", "errors.housekeeping.forbidden", correlationId),
),
).toContain('data-housekeeping-state="forbidden"');
expect(
render(
fail(
"DEPENDENCY_UNAVAILABLE",
"errors.housekeeping.dependencyUnavailable",
correlationId,
),
),
).toContain('data-housekeeping-state="error"');
});
it("renders empty, partial, and ready query results explicitly", () => {
expect(render(ok(empty, correlationId))).toContain(
'data-housekeeping-state="empty"',
);
expect(render(ok(partial, correlationId))).toContain(
'data-housekeeping-state="partial"',
);
expect(render(ok(ready, correlationId))).toContain(
'data-housekeeping-state="ready"',
);
});
},
);
@@ -0,0 +1,257 @@
import "server-only";
import { PERMS } from "@/lib/permission-slugs";
import { authorizeHousekeeping } from "../../../foundation/authorization";
import {
anyCapability,
fail,
type HousekeepingQuery,
ok,
} from "../../../foundation/contracts";
export interface SystemAccessRank {
readonly id: number;
readonly name: string;
readonly level: number;
readonly userCount: number;
}
export interface SystemAccessRankDetail extends SystemAccessRank {
readonly badge: string;
readonly permissions: Readonly<Record<string, string>>;
readonly cmsRole: {
readonly id: number;
readonly slug: string;
readonly title: string;
readonly permissionSlugs: readonly string[];
} | null;
readonly users: readonly { id: number; username: string }[];
}
export interface SystemAclOverview {
readonly roles: number;
readonly permissions: number;
}
export interface SystemAccessAdapters {
loadRanks(): Promise<readonly SystemAccessRank[]>;
loadRankDetail(rankId: number): Promise<SystemAccessRankDetail | null>;
loadAclOverview(): Promise<SystemAclOverview>;
}
export type SystemAccessQueryInput =
| { readonly routeId: "system.access.permissions" }
| {
readonly routeId: "system.access.permission-detail";
readonly rankId: string;
};
export type SystemAccessQueryData =
| {
readonly kind: "permissions";
readonly ranks: readonly SystemAccessRank[];
readonly acl: SystemAclOverview;
readonly partialDependencies: readonly string[];
}
| {
readonly kind: "permission-detail";
readonly rank: SystemAccessRankDetail;
readonly partialDependencies: readonly string[];
};
const accessCapability = anyCapability(PERMS.PERMISSIONS_MANAGE);
function dependencyUnavailable(correlationId: string) {
return fail(
"DEPENDENCY_UNAVAILABLE",
"errors.housekeeping.dependencyUnavailable",
correlationId,
);
}
export function createSystemAccessQuery(
adapters: SystemAccessAdapters,
): HousekeepingQuery<SystemAccessQueryInput, SystemAccessQueryData> {
return {
id: "system.access.query",
owner: "system",
capability: accessCapability,
async run(context, input) {
const authorization = authorizeHousekeeping(context, accessCapability);
if (!authorization.ok) return authorization;
const correlationId = authorization.correlationId;
if (input.routeId === "system.access.permission-detail") {
const rankId = Number(input.rankId);
if (!Number.isInteger(rankId) || rankId <= 0) {
return fail(
"VALIDATION",
"errors.housekeeping.validation",
correlationId,
{ rankId: ["errors.validation.invalid"] },
);
}
try {
const rank = await adapters.loadRankDetail(rankId);
return rank
? ok(
{
kind: "permission-detail" as const,
rank,
partialDependencies: [],
},
correlationId,
)
: fail("NOT_FOUND", "errors.housekeeping.notFound", correlationId);
} catch {
return dependencyUnavailable(correlationId);
}
}
const [ranksResult, aclResult] = await Promise.allSettled([
adapters.loadRanks(),
adapters.loadAclOverview(),
]);
if (
ranksResult.status === "rejected" &&
aclResult.status === "rejected"
) {
return dependencyUnavailable(correlationId);
}
const partialDependencies: string[] = [];
if (ranksResult.status === "rejected") partialDependencies.push("ranks");
if (aclResult.status === "rejected") partialDependencies.push("acl");
return ok(
{
kind: "permissions",
ranks: ranksResult.status === "fulfilled" ? ranksResult.value : [],
acl:
aclResult.status === "fulfilled"
? aclResult.value
: { roles: 0, permissions: 0 },
partialDependencies,
},
correlationId,
);
},
};
}
export const systemAccessAdapters: SystemAccessAdapters = {
async loadRanks() {
const [{ db }, { sql }, { fetchEmulatorRankSummaries }] = await Promise.all(
[
import("@/lib/db"),
import("drizzle-orm"),
import("@/lib/services/permission-ranks"),
],
);
const [ranks, countResult] = await Promise.all([
fetchEmulatorRankSummaries(db),
db.execute(
sql`SELECT \`rank\`, COUNT(*) AS total FROM users GROUP BY \`rank\``,
),
]);
const countRows = countResult[0] as unknown as {
rank: number | bigint;
total: number | bigint;
}[];
const counts = new Map(
countRows.map((row) => [Number(row.rank), Number(row.total)]),
);
return ranks.map((rank) => ({
id: rank.id,
name: rank.rank_name,
level: rank.level,
userCount: counts.get(rank.id) ?? 0,
}));
},
async loadRankDetail(rankId) {
const [{ db }, { sql }, { fetchEmulatorRankForEdit }] = await Promise.all([
import("@/lib/db"),
import("drizzle-orm"),
import("@/lib/services/permission-ranks"),
]);
const rank = await fetchEmulatorRankForEdit(db, rankId);
if (!rank) return null;
const [userResult, roleResult] = await Promise.all([
db.execute(sql`
SELECT id, username
FROM users
WHERE \`rank\` = ${rankId}
ORDER BY username ASC
LIMIT 200
`),
db.execute(sql`
SELECT ar.id, ar.slug, ar.title, ap.slug AS permission_slug
FROM acl_roles ar
LEFT JOIN acl_model_permissions amp
ON amp.model_type = 'Role' AND amp.model_id = ar.id
LEFT JOIN acl_permissions ap ON ap.id = amp.permission_id
WHERE ar.slug = ${`rank_${rankId}`}
ORDER BY ap.slug ASC
`),
]);
const users = userResult[0] as unknown as {
id: number | bigint;
username: string;
}[];
const roles = roleResult[0] as unknown as {
id: number | bigint;
slug: string;
title: string;
permission_slug: string | null;
}[];
const role = roles[0];
return {
id: rank.id,
name: rank.rank_name,
level: rank.level,
userCount: users.length,
badge: rank.badge,
permissions: rank.permissions,
cmsRole: role
? {
id: Number(role.id),
slug: role.slug,
title: role.title,
permissionSlugs: roles.flatMap((row) =>
row.permission_slug ? [row.permission_slug] : [],
),
}
: null,
users: users.map((user) => ({
id: Number(user.id),
username: user.username,
})),
};
},
async loadAclOverview() {
const [{ db }, { sql }] = await Promise.all([
import("@/lib/db"),
import("drizzle-orm"),
]);
const [result] = await db.execute(sql`
SELECT
(SELECT COUNT(*) FROM acl_roles) AS roles,
(SELECT COUNT(*) FROM acl_permissions) AS permissions
`);
const row = (
result as unknown as {
roles: number | bigint;
permissions: number | bigint;
}[]
)[0];
return {
roles: Number(row?.roles ?? 0),
permissions: Number(row?.permissions ?? 0),
};
},
};
export const systemAccessQuery = createSystemAccessQuery(systemAccessAdapters);
@@ -0,0 +1,172 @@
import "server-only";
import { PERMS } from "@/lib/permission-slugs";
import { authorizeHousekeeping } from "../../../foundation/authorization";
import {
anyCapability,
fail,
type HousekeepingQuery,
ok,
} from "../../../foundation/contracts";
export interface SystemSettingRow {
readonly key: string;
readonly value: string;
readonly comment?: string | null;
}
export interface SystemEmulatorTextResult {
readonly rows: readonly SystemSettingRow[];
readonly total: number;
}
export interface SystemConfigurationAdapters {
loadWebsiteSettings(): Promise<readonly SystemSettingRow[]>;
loadEmulatorSettings(): Promise<readonly SystemSettingRow[]>;
loadEmulatorTexts(): Promise<SystemEmulatorTextResult>;
}
export type SystemConfigurationQueryInput =
| { readonly routeId: "system.configuration.settings" }
| { readonly routeId: "system.configuration.emulator" };
export type SystemConfigurationQueryData =
| {
readonly kind: "settings";
readonly settings: readonly SystemSettingRow[];
readonly partialDependencies: readonly string[];
}
| {
readonly kind: "emulator";
readonly settings: readonly SystemSettingRow[];
readonly texts: readonly SystemSettingRow[];
readonly textsTotal: number;
readonly partialDependencies: readonly string[];
};
const configurationCapability = anyCapability(PERMS.SETTINGS_VIEW);
function unavailable(correlationId: string) {
return fail(
"DEPENDENCY_UNAVAILABLE",
"errors.housekeeping.dependencyUnavailable",
correlationId,
);
}
export function createSystemConfigurationQuery(
adapters: SystemConfigurationAdapters,
): HousekeepingQuery<
SystemConfigurationQueryInput,
SystemConfigurationQueryData
> {
return {
id: "system.configuration.query",
owner: "system",
capability: configurationCapability,
async run(context, input) {
const authorization = authorizeHousekeeping(
context,
configurationCapability,
);
if (!authorization.ok) return authorization;
const correlationId = authorization.correlationId;
if (input.routeId === "system.configuration.settings") {
try {
return ok(
{
kind: "settings" as const,
settings: await adapters.loadWebsiteSettings(),
partialDependencies: [],
},
correlationId,
);
} catch {
return unavailable(correlationId);
}
}
const [settingsResult, textsResult] = await Promise.allSettled([
adapters.loadEmulatorSettings(),
adapters.loadEmulatorTexts(),
]);
if (
settingsResult.status === "rejected" &&
textsResult.status === "rejected"
) {
return unavailable(correlationId);
}
const partialDependencies: string[] = [];
if (settingsResult.status === "rejected") {
partialDependencies.push("emulator-settings");
}
if (textsResult.status === "rejected") {
partialDependencies.push("emulator-texts");
}
const textData =
textsResult.status === "fulfilled"
? textsResult.value
: { rows: [], total: 0 };
return ok(
{
kind: "emulator",
settings:
settingsResult.status === "fulfilled" ? settingsResult.value : [],
texts: textData.rows,
textsTotal: textData.total,
partialDependencies,
},
correlationId,
);
},
};
}
export const systemConfigurationAdapters: SystemConfigurationAdapters = {
async loadWebsiteSettings() {
const [{ asc }, { db, WebsiteSetting }] = await Promise.all([
import("drizzle-orm"),
import("@/lib/db"),
]);
return db
.select({
key: WebsiteSetting.key,
value: WebsiteSetting.value,
comment: WebsiteSetting.comment,
})
.from(WebsiteSetting)
.orderBy(asc(WebsiteSetting.key));
},
async loadEmulatorSettings() {
const [{ asc }, { db, EmulatorSettings }] = await Promise.all([
import("drizzle-orm"),
import("@/lib/db"),
]);
return db
.select({ key: EmulatorSettings.key, value: EmulatorSettings.value })
.from(EmulatorSettings)
.orderBy(asc(EmulatorSettings.key));
},
async loadEmulatorTexts() {
const [{ asc, count }, { db, EmulatorTexts }] = await Promise.all([
import("drizzle-orm"),
import("@/lib/db"),
]);
const [rows, totals] = await Promise.all([
db
.select({ key: EmulatorTexts.key, value: EmulatorTexts.value })
.from(EmulatorTexts)
.orderBy(asc(EmulatorTexts.key))
.limit(300),
db.select({ total: count() }).from(EmulatorTexts),
]);
return { rows, total: Number(totals[0]?.total ?? 0) };
},
};
export const systemConfigurationQuery = createSystemConfigurationQuery(
systemConfigurationAdapters,
);
@@ -0,0 +1,427 @@
import "server-only";
import { PERMS } from "@/lib/permission-slugs";
import { authorizeHousekeeping } from "../../../foundation/authorization";
import {
anyCapability,
fail,
type HousekeepingQuery,
ok,
} from "../../../foundation/contracts";
export interface SystemMetric {
readonly label: string;
readonly value: number | string;
}
export interface SystemObservationRow {
readonly id: string;
readonly primary: string;
readonly secondary?: string;
readonly timestamp?: number | string;
}
export interface SystemObservationList {
readonly metrics: readonly SystemMetric[];
readonly rows: readonly SystemObservationRow[];
}
export interface SystemHealthSnapshot {
readonly dbOk: boolean;
readonly dbLatencyMs: number;
readonly redisOk: boolean | null;
readonly emulatorOk: boolean;
readonly onlineUsers: number;
}
export interface SystemObservabilityAdapters {
loadAnalytics(
view: "overview" | "activity" | "economy",
): Promise<SystemObservationList>;
loadLogs(
view: "staff" | "audit" | "chat" | "commands" | "trades",
): Promise<SystemObservationList>;
loadHealth(): Promise<SystemHealthSnapshot>;
loadErrors(): Promise<readonly SystemObservationRow[]>;
}
export type SystemObservabilityQueryInput = {
readonly routeId:
| "system.observability.analytics"
| "system.observability.analytics-activity"
| "system.observability.analytics-economy"
| "system.observability.devops"
| "system.observability.devops-errors"
| "system.observability.logs-staff"
| "system.observability.logs-audit"
| "system.observability.logs-chat"
| "system.observability.logs-commands"
| "system.observability.logs-trades";
};
export type SystemObservabilityQueryData =
| {
readonly kind: "analytics" | "logs";
readonly metrics: readonly SystemMetric[];
readonly rows: readonly SystemObservationRow[];
readonly partialDependencies: readonly string[];
}
| {
readonly kind: "devops";
readonly health: SystemHealthSnapshot | null;
readonly errors: readonly SystemObservationRow[];
readonly partialDependencies: readonly string[];
}
| {
readonly kind: "devops-errors";
readonly errors: readonly SystemObservationRow[];
readonly partialDependencies: readonly string[];
};
const broadCapability = anyCapability(
PERMS.ANALYTICS_VIEW,
PERMS.DEVOPS_VIEW,
PERMS.LOGS_VIEW,
);
function capabilityForRoute(routeId: SystemObservabilityQueryInput["routeId"]) {
if (routeId.startsWith("system.observability.analytics")) {
return anyCapability(PERMS.ANALYTICS_VIEW);
}
if (routeId.startsWith("system.observability.devops")) {
return anyCapability(PERMS.DEVOPS_VIEW);
}
return anyCapability(PERMS.LOGS_VIEW);
}
function unavailable(correlationId: string) {
return fail(
"DEPENDENCY_UNAVAILABLE",
"errors.housekeeping.dependencyUnavailable",
correlationId,
);
}
export function createSystemObservabilityQuery(
adapters: SystemObservabilityAdapters,
): HousekeepingQuery<
SystemObservabilityQueryInput,
SystemObservabilityQueryData
> {
return {
id: "system.observability.query",
owner: "system",
capability: broadCapability,
async run(context, input) {
const authorization = authorizeHousekeeping(
context,
capabilityForRoute(input.routeId),
);
if (!authorization.ok) return authorization;
const correlationId = authorization.correlationId;
if (input.routeId === "system.observability.devops") {
const [healthResult, errorsResult] = await Promise.allSettled([
adapters.loadHealth(),
adapters.loadErrors(),
]);
if (
healthResult.status === "rejected" &&
errorsResult.status === "rejected"
) {
return unavailable(correlationId);
}
const partialDependencies: string[] = [];
if (healthResult.status === "rejected") {
partialDependencies.push("health");
}
if (errorsResult.status === "rejected") {
partialDependencies.push("emulator-errors");
}
return ok(
{
kind: "devops",
health:
healthResult.status === "fulfilled" ? healthResult.value : null,
errors:
errorsResult.status === "fulfilled" ? errorsResult.value : [],
partialDependencies,
},
correlationId,
);
}
if (input.routeId === "system.observability.devops-errors") {
try {
return ok(
{
kind: "devops-errors" as const,
errors: await adapters.loadErrors(),
partialDependencies: [],
},
correlationId,
);
} catch {
return unavailable(correlationId);
}
}
const analyticsView = {
"system.observability.analytics": "overview",
"system.observability.analytics-activity": "activity",
"system.observability.analytics-economy": "economy",
} as const;
const analytics =
analyticsView[input.routeId as keyof typeof analyticsView];
try {
if (analytics) {
const data = await adapters.loadAnalytics(analytics);
return ok(
{
kind: "analytics" as const,
...data,
partialDependencies: [],
},
correlationId,
);
}
const logViews = {
"system.observability.logs-staff": "staff",
"system.observability.logs-audit": "audit",
"system.observability.logs-chat": "chat",
"system.observability.logs-commands": "commands",
"system.observability.logs-trades": "trades",
} as const;
const data = await adapters.loadLogs(
logViews[input.routeId as keyof typeof logViews],
);
return ok(
{
kind: "logs" as const,
...data,
partialDependencies: [],
},
correlationId,
);
} catch {
return unavailable(correlationId);
}
},
};
}
async function rawRows<T>(query: unknown): Promise<T[]> {
const { db } = await import("@/lib/db");
const [rows] = await db.execute(query as never);
return (rows ?? []) as unknown as T[];
}
export const systemObservabilityAdapters: SystemObservabilityAdapters = {
async loadAnalytics(view) {
const { sql } = await import("drizzle-orm");
const weekAgo = Math.floor(Date.now() / 1000) - 7 * 86_400;
if (view === "overview") {
const rows = await rawRows<Record<string, number | bigint>>(sql`
SELECT
(SELECT COUNT(*) FROM users) AS totalUsers,
(SELECT COUNT(*) FROM users WHERE online = '1') AS onlineUsers,
(SELECT COUNT(*) FROM rooms) AS totalRooms,
(SELECT COUNT(*) FROM chatlogs_room WHERE timestamp >= ${weekAgo}) AS weekChats,
(SELECT COUNT(*) FROM room_trade_log WHERE timestamp >= ${weekAgo}) AS weekTrades
`);
const row = rows[0] ?? {};
return {
metrics: [
{ label: "Total users", value: Number(row.totalUsers ?? 0) },
{ label: "Online users", value: Number(row.onlineUsers ?? 0) },
{ label: "Total rooms", value: Number(row.totalRooms ?? 0) },
{ label: "Weekly chats", value: Number(row.weekChats ?? 0) },
{ label: "Weekly trades", value: Number(row.weekTrades ?? 0) },
],
rows: [],
};
}
if (view === "activity") {
const rows = await rawRows<Record<string, number | bigint>>(sql`
SELECT
(SELECT COUNT(*) FROM chatlogs_room WHERE timestamp >= ${weekAgo}) AS chats,
(SELECT COUNT(*) FROM commandlogs WHERE timestamp >= ${weekAgo}) AS commands,
(SELECT COUNT(*) FROM bans WHERE timestamp >= ${weekAgo}) AS bans,
(SELECT COUNT(*) FROM users WHERE account_created >= ${weekAgo}) AS registrations
`);
const row = rows[0] ?? {};
return {
metrics: [
{ label: "Chats", value: Number(row.chats ?? 0) },
{ label: "Commands", value: Number(row.commands ?? 0) },
{ label: "Bans", value: Number(row.bans ?? 0) },
{ label: "Registrations", value: Number(row.registrations ?? 0) },
],
rows: [],
};
}
const rows = await rawRows<Record<string, number | bigint>>(sql`
SELECT
COALESCE(SUM(credits), 0) AS credits,
COALESCE(SUM(pixels), 0) AS pixels,
COALESCE(SUM(points), 0) AS points,
(SELECT COUNT(*) FROM logs_shop_purchases WHERE timestamp >= ${weekAgo}) AS purchases,
(SELECT COUNT(*) FROM room_trade_log WHERE timestamp >= ${weekAgo}) AS trades
FROM users
`);
const row = rows[0] ?? {};
return {
metrics: [
{ label: "Credits", value: Number(row.credits ?? 0) },
{ label: "Pixels", value: Number(row.pixels ?? 0) },
{ label: "Points", value: Number(row.points ?? 0) },
{ label: "Purchases", value: Number(row.purchases ?? 0) },
{ label: "Trades", value: Number(row.trades ?? 0) },
],
rows: [],
};
},
async loadLogs(view) {
if (view === "audit") {
const { getAuditLogs } = await import("@/lib/services/audit");
const result = await getAuditLogs({ page: 1, perPage: 50 });
return {
metrics: [{ label: "Audit entries", value: result.total }],
rows: result.rows.map((row) => ({
id: String(row.id),
primary: `${row.username}: ${row.action}`,
secondary: row.target,
timestamp: row.createdAt,
})),
};
}
const { sql } = await import("drizzle-orm");
if (view === "staff") {
const rows = await rawRows<{
id: number;
action: string;
description: string;
username: string | null;
createdAt: string;
}>(sql`
SELECT sa.id, sa.action, sa.description, u.username,
sa.created_at AS createdAt
FROM staff_activities sa
LEFT JOIN users u ON u.id = sa.user_id
ORDER BY sa.id DESC LIMIT 50
`);
return {
metrics: [{ label: "Staff activities", value: rows.length }],
rows: rows.map((row) => ({
id: String(row.id),
primary: `${row.username ?? "Unknown"}: ${row.action}`,
secondary: row.description,
timestamp: row.createdAt,
})),
};
}
if (view === "chat") {
const rows = await rawRows<{
id: number;
username: string | null;
message: string;
timestamp: number;
}>(sql`
SELECT c.id, u.username, c.message, c.timestamp
FROM chatlogs_room c
LEFT JOIN users u ON u.id = c.user_from_id
ORDER BY c.timestamp DESC LIMIT 50
`);
return {
metrics: [{ label: "Chat entries", value: rows.length }],
rows: rows.map((row) => ({
id: String(row.id),
primary: row.username ?? "Unknown",
secondary: row.message,
timestamp: row.timestamp,
})),
};
}
if (view === "commands") {
const rows = await rawRows<{
id: number;
username: string | null;
command: string;
params: string;
timestamp: number;
}>(sql`
SELECT c.id, u.username, c.command, c.params, c.timestamp
FROM commandlogs c
LEFT JOIN users u ON u.id = c.user_id
ORDER BY c.timestamp DESC LIMIT 50
`);
return {
metrics: [{ label: "Command entries", value: rows.length }],
rows: rows.map((row) => ({
id: String(row.id),
primary: `${row.username ?? "Unknown"}: ${row.command}`,
secondary: row.params,
timestamp: row.timestamp,
})),
};
}
const rows = await rawRows<{
id: number;
userOne: string | null;
userTwo: string | null;
timestamp: number;
}>(sql`
SELECT t.id, u1.username AS userOne, u2.username AS userTwo, t.timestamp
FROM room_trade_log t
LEFT JOIN users u1 ON u1.id = t.user_one_id
LEFT JOIN users u2 ON u2.id = t.user_two_id
ORDER BY t.timestamp DESC LIMIT 50
`);
return {
metrics: [{ label: "Trade entries", value: rows.length }],
rows: rows.map((row) => ({
id: String(row.id),
primary: `${row.userOne ?? "Unknown"} ↔ ${row.userTwo ?? "Unknown"}`,
secondary: "Completed",
timestamp: row.timestamp,
})),
};
},
async loadHealth() {
const { fetchOpsHealth } = await import("@/lib/admin/ops-health");
return fetchOpsHealth();
},
async loadErrors() {
const { sql } = await import("drizzle-orm");
const rows = await rawRows<{
id: number;
type: string;
version: string;
stacktrace: Uint8Array | string;
timestamp: number;
}>(sql`
SELECT id, type, version, stacktrace, timestamp
FROM emulator_errors
ORDER BY id DESC LIMIT 50
`);
return rows.map((row) => ({
id: String(row.id),
primary: `${row.type} (${row.version})`,
secondary:
typeof row.stacktrace === "string"
? row.stacktrace
: Buffer.from(row.stacktrace).toString("utf8"),
timestamp: row.timestamp,
}));
},
};
export const systemObservabilityQuery = createSystemObservabilityQuery(
systemObservabilityAdapters,
);
@@ -0,0 +1,246 @@
import "server-only";
import { PERMS } from "@/lib/permission-slugs";
import { authorizeHousekeeping } from "../../../foundation/authorization";
import {
anyCapability,
fail,
type HousekeepingQuery,
ok,
} from "../../../foundation/contracts";
import type { SystemHealthSnapshot } from "./observability";
export interface SystemAlertRow {
readonly id: number;
readonly severity: string;
readonly type: string;
readonly message: string;
readonly isRead: boolean;
readonly createdAt?: string | Date | null;
}
export interface SystemOnlineRoster {
readonly count: number;
readonly users: readonly {
readonly id: number;
readonly username: string;
readonly roomId?: number | null;
readonly roomName?: string | null;
}[];
}
export interface SystemMaintenanceSnapshot {
readonly enabled: boolean;
readonly message: string;
readonly minimumLoginRank: number;
}
export interface SystemOperationsAdapters {
loadAlerts(): Promise<readonly SystemAlertRow[]>;
loadHealth(): Promise<SystemHealthSnapshot>;
loadOnlineUsers(): Promise<SystemOnlineRoster>;
loadMaintenance(): Promise<SystemMaintenanceSnapshot>;
}
export type SystemOperationsQueryInput = {
readonly routeId:
| "system.operations.alerts"
| "system.operations.command-center"
| "system.operations.maintenance";
};
export type SystemOperationsQueryData =
| {
readonly kind: "alerts";
readonly alerts: readonly SystemAlertRow[];
readonly partialDependencies: readonly string[];
}
| {
readonly kind: "command-center";
readonly health: SystemHealthSnapshot | null;
readonly onlineUsers: SystemOnlineRoster;
readonly maintenance: SystemMaintenanceSnapshot | null;
readonly partialDependencies: readonly string[];
}
| {
readonly kind: "maintenance";
readonly maintenance: SystemMaintenanceSnapshot;
readonly partialDependencies: readonly string[];
};
const broadCapability = anyCapability(
PERMS.NOTIFICATIONS_VIEW,
PERMS.RCON_EXECUTE,
PERMS.SETTINGS_VIEW,
);
function capabilityForRoute(routeId: SystemOperationsQueryInput["routeId"]) {
if (routeId === "system.operations.alerts") {
return anyCapability(PERMS.NOTIFICATIONS_VIEW);
}
if (routeId === "system.operations.command-center") {
return anyCapability(PERMS.RCON_EXECUTE);
}
return anyCapability(PERMS.SETTINGS_VIEW);
}
function unavailable(correlationId: string) {
return fail(
"DEPENDENCY_UNAVAILABLE",
"errors.housekeeping.dependencyUnavailable",
correlationId,
);
}
export function createSystemOperationsQuery(
adapters: SystemOperationsAdapters,
): HousekeepingQuery<SystemOperationsQueryInput, SystemOperationsQueryData> {
return {
id: "system.operations.query",
owner: "system",
capability: broadCapability,
async run(context, input) {
const authorization = authorizeHousekeeping(
context,
capabilityForRoute(input.routeId),
);
if (!authorization.ok) return authorization;
const correlationId = authorization.correlationId;
if (input.routeId === "system.operations.alerts") {
try {
return ok(
{
kind: "alerts" as const,
alerts: await adapters.loadAlerts(),
partialDependencies: [],
},
correlationId,
);
} catch {
return unavailable(correlationId);
}
}
if (input.routeId === "system.operations.maintenance") {
try {
return ok(
{
kind: "maintenance" as const,
maintenance: await adapters.loadMaintenance(),
partialDependencies: [],
},
correlationId,
);
} catch {
return unavailable(correlationId);
}
}
const [healthResult, onlineResult, maintenanceResult] =
await Promise.allSettled([
adapters.loadHealth(),
adapters.loadOnlineUsers(),
adapters.loadMaintenance(),
]);
if (
healthResult.status === "rejected" &&
onlineResult.status === "rejected" &&
maintenanceResult.status === "rejected"
) {
return unavailable(correlationId);
}
const partialDependencies: string[] = [];
if (healthResult.status === "rejected")
partialDependencies.push("health");
if (onlineResult.status === "rejected") {
partialDependencies.push("online-users");
}
if (maintenanceResult.status === "rejected") {
partialDependencies.push("maintenance");
}
return ok(
{
kind: "command-center",
health:
healthResult.status === "fulfilled" ? healthResult.value : null,
onlineUsers:
onlineResult.status === "fulfilled"
? onlineResult.value
: { count: 0, users: [] },
maintenance:
maintenanceResult.status === "fulfilled"
? maintenanceResult.value
: null,
partialDependencies,
},
correlationId,
);
},
};
}
export const systemOperationsAdapters: SystemOperationsAdapters = {
async loadAlerts() {
const [{ desc }, { AlertLogs, db }] = await Promise.all([
import("drizzle-orm"),
import("@/lib/db"),
]);
const rows = await db
.select({
id: AlertLogs.id,
severity: AlertLogs.severity,
type: AlertLogs.type,
message: AlertLogs.message,
isRead: AlertLogs.isRead,
createdAt: AlertLogs.createdAt,
})
.from(AlertLogs)
.orderBy(desc(AlertLogs.id))
.limit(100);
return rows.map((row) => ({ ...row, id: Number(row.id) }));
},
async loadHealth() {
const { fetchOpsHealth } = await import("@/lib/admin/ops-health");
return fetchOpsHealth();
},
async loadOnlineUsers() {
const { fetchOpsOnlineUsers } = await import(
"@/lib/admin/ops-online-users"
);
return fetchOpsOnlineUsers(40);
},
async loadMaintenance() {
const [{ inArray }, { db, WebsiteSetting }] = await Promise.all([
import("drizzle-orm"),
import("@/lib/db"),
]);
const rows = await db
.select({ key: WebsiteSetting.key, value: WebsiteSetting.value })
.from(WebsiteSetting)
.where(
inArray(WebsiteSetting.key, [
"maintenance_enabled",
"maintenance_message",
"min_maintenance_login_rank",
]),
);
const values = new Map(rows.map((row) => [row.key, row.value]));
const enabled = values.get("maintenance_enabled") ?? "0";
const parsedRank = Number.parseInt(
values.get("min_maintenance_login_rank") ?? "5",
10,
);
return {
enabled: enabled === "1" || enabled.toLowerCase() === "true",
message: values.get("maintenance_message") ?? "",
minimumLoginRank: Number.isFinite(parsedRank) ? parsedRank : 5,
};
},
};
export const systemOperationsQuery = createSystemOperationsQuery(
systemOperationsAdapters,
);
@@ -0,0 +1,207 @@
import { describe, expect, it, vi } from "vitest";
import { PERMS } from "@/lib/permission-slugs";
import type { HousekeepingCapabilityContext } from "../../../foundation/contracts";
import { createSystemAccessQuery } from "./access";
import { createSystemConfigurationQuery } from "./configuration";
import { createSystemObservabilityQuery } from "./observability";
import { createSystemOperationsQuery } from "./operations";
function context(granted: readonly string[]): HousekeepingCapabilityContext {
const permissions = new Set(granted);
return {
actor: { id: 42, username: "operator", rank: 99 },
isSuperAdmin: false,
has: (slug) => permissions.has(slug),
hasAny: (...slugs) => slugs.some((slug) => permissions.has(slug)),
hasAll: (...slugs) => slugs.every((slug) => permissions.has(slug)),
};
}
describe("System access query", () => {
it("combines live rank and ACL data through injected adapters", async () => {
const query = createSystemAccessQuery({
loadRanks: async () => [
{ id: 7, name: "Administrator", level: 7, userCount: 2 },
],
loadRankDetail: async () => null,
loadAclOverview: async () => ({ roles: 3, permissions: 18 }),
});
const result = await query.run(context([PERMS.PERMISSIONS_MANAGE]), {
routeId: "system.access.permissions",
});
expect(result).toMatchObject({
ok: true,
data: {
kind: "permissions",
ranks: [{ id: 7, name: "Administrator", level: 7, userCount: 2 }],
acl: { roles: 3, permissions: 18 },
partialDependencies: [],
},
});
});
it("fails closed before calling adapters and maps total dependency failure", async () => {
const loadRanks = vi.fn(async () => {
throw new Error("database offline");
});
const loadAclOverview = vi.fn(async () => {
throw new Error("acl offline");
});
const query = createSystemAccessQuery({
loadRanks,
loadRankDetail: async () => null,
loadAclOverview,
});
const forbidden = await query.run(context([]), {
routeId: "system.access.permissions",
});
expect(forbidden).toMatchObject({
ok: false,
error: { code: "FORBIDDEN" },
});
expect(loadRanks).not.toHaveBeenCalled();
const unavailable = await query.run(context([PERMS.PERMISSIONS_MANAGE]), {
routeId: "system.access.permissions",
});
expect(unavailable).toMatchObject({
ok: false,
error: { code: "DEPENDENCY_UNAVAILABLE" },
});
});
});
describe("System configuration query", () => {
it("returns usable emulator settings as partial when texts are unavailable", async () => {
const query = createSystemConfigurationQuery({
loadWebsiteSettings: async () => [],
loadEmulatorSettings: async () => [{ key: "version", value: "1.0" }],
loadEmulatorTexts: async () => {
throw new Error("emulator_texts unavailable");
},
});
const result = await query.run(context([PERMS.SETTINGS_VIEW]), {
routeId: "system.configuration.emulator",
});
expect(result).toMatchObject({
ok: true,
data: {
kind: "emulator",
settings: [{ key: "version", value: "1.0" }],
texts: [],
textsTotal: 0,
partialDependencies: ["emulator-texts"],
},
});
});
});
describe("System observability query", () => {
it("keeps health evidence when the error feed is unavailable", async () => {
const query = createSystemObservabilityQuery({
loadAnalytics: async () => ({ metrics: [], rows: [] }),
loadLogs: async () => ({ metrics: [], rows: [] }),
loadHealth: async () => ({
dbOk: true,
dbLatencyMs: 4,
redisOk: null,
emulatorOk: true,
onlineUsers: 12,
}),
loadErrors: async () => {
throw new Error("errors table unavailable");
},
});
const result = await query.run(context([PERMS.DEVOPS_VIEW]), {
routeId: "system.observability.devops",
});
expect(result).toMatchObject({
ok: true,
data: {
kind: "devops",
health: { dbOk: true, onlineUsers: 12 },
errors: [],
partialDependencies: ["emulator-errors"],
},
});
});
});
describe("System operations query", () => {
it("maps an unavailable command-center dependency set to a typed failure", async () => {
const down = async () => {
throw new Error("dependency unavailable");
};
const query = createSystemOperationsQuery({
loadAlerts: down,
loadHealth: down,
loadOnlineUsers: down,
loadMaintenance: down,
});
const result = await query.run(context([PERMS.RCON_EXECUTE]), {
routeId: "system.operations.command-center",
});
expect(result).toMatchObject({
ok: false,
error: {
code: "DEPENDENCY_UNAVAILABLE",
messageKey: "errors.housekeeping.dependencyUnavailable",
},
});
});
it("loads alert and maintenance workflows from their dedicated adapters", async () => {
const query = createSystemOperationsQuery({
loadAlerts: async () => [
{
id: 1,
severity: "warning",
type: "runtime",
message: "High load",
isRead: false,
},
],
loadHealth: async () => ({
dbOk: true,
dbLatencyMs: 1,
redisOk: true,
emulatorOk: true,
onlineUsers: 1,
}),
loadOnlineUsers: async () => ({ count: 1, users: [] }),
loadMaintenance: async () => ({
enabled: false,
message: "",
minimumLoginRank: 5,
}),
});
const alerts = await query.run(context([PERMS.NOTIFICATIONS_VIEW]), {
routeId: "system.operations.alerts",
});
const maintenance = await query.run(context([PERMS.SETTINGS_VIEW]), {
routeId: "system.operations.maintenance",
});
expect(alerts).toMatchObject({
ok: true,
data: { kind: "alerts", alerts: [{ message: "High load" }] },
});
expect(maintenance).toMatchObject({
ok: true,
data: {
kind: "maintenance",
maintenance: { enabled: false, minimumLoginRank: 5 },
},
});
});
});
@@ -0,0 +1,26 @@
import type { HousekeepingRouteHandler } from "../../route-handlers";
import { renderSystemAccessPage } from "./pages/access";
import { renderSystemConfigurationPage } from "./pages/configuration";
import { renderSystemObservabilityPage } from "./pages/observability";
import { renderSystemOperationsPage } from "./pages/operations";
import { SYSTEM_ROUTE_IDS, type SystemRouteId } from "./routes";
function rendererFor(
routeId: SystemRouteId,
): HousekeepingRouteHandler["render"] {
if (routeId.startsWith("system.access.")) return renderSystemAccessPage;
if (routeId.startsWith("system.configuration.")) {
return renderSystemConfigurationPage;
}
if (routeId.startsWith("system.observability.")) {
return renderSystemObservabilityPage;
}
return renderSystemOperationsPage;
}
export const SYSTEM_ROUTE_HANDLERS: readonly HousekeepingRouteHandler[] =
Object.freeze(
SYSTEM_ROUTE_IDS.map((routeId) =>
Object.freeze({ routeId, render: rendererFor(routeId) }),
),
);
@@ -0,0 +1,147 @@
import { describe, expect, it } from "vitest";
import { PERMS } from "@/lib/permission-slugs";
import { systemMigrationEntries } from "../../migration/system";
import { SYSTEM_ROUTE_IDS, SYSTEM_ROUTES } from "./routes";
const expectedRoutes = [
[
"system.access.permissions",
"/ase/system/access/permissions",
"pages.admin.hubs.tabs.permissions",
PERMS.PERMISSIONS_MANAGE,
],
[
"system.access.permission-detail",
"/ase/system/access/permissions/:id",
"pages.admin.hubs.tabs.permissions",
PERMS.PERMISSIONS_MANAGE,
],
[
"system.configuration.settings",
"/ase/system/configuration/settings",
"pages.admin.hubs.tabs.cms",
PERMS.SETTINGS_VIEW,
],
[
"system.configuration.emulator",
"/ase/system/configuration/emulator",
"pages.admin.hubs.tabs.emulator",
PERMS.SETTINGS_VIEW,
],
[
"system.observability.analytics",
"/ase/system/observability/analytics",
"pages.admin.hubs.tabs.analytics",
PERMS.ANALYTICS_VIEW,
],
[
"system.observability.analytics-activity",
"/ase/system/observability/analytics/activity",
"pages.admin.hubs.tabs.activity",
PERMS.ANALYTICS_VIEW,
],
[
"system.observability.analytics-economy",
"/ase/system/observability/analytics/economy",
"pages.admin.hubs.tabs.economy",
PERMS.ANALYTICS_VIEW,
],
[
"system.observability.devops",
"/ase/system/observability/devops",
"pages.admin.hubs.tabs.devops",
PERMS.DEVOPS_VIEW,
],
[
"system.observability.devops-errors",
"/ase/system/observability/devops/errors",
"pages.admin.hubs.tabs.errors",
PERMS.DEVOPS_VIEW,
],
[
"system.observability.logs-staff",
"/ase/system/observability/logs/staff",
"pages.admin.hubs.tabs.logs",
PERMS.LOGS_VIEW,
],
[
"system.observability.logs-audit",
"/ase/system/observability/logs/audit",
"pages.admin.hubs.tabs.audit",
PERMS.LOGS_VIEW,
],
[
"system.observability.logs-chat",
"/ase/system/observability/logs/chat",
"pages.admin.hubs.tabs.chat",
PERMS.LOGS_VIEW,
],
[
"system.observability.logs-commands",
"/ase/system/observability/logs/commands",
"pages.admin.hubs.tabs.commands",
PERMS.LOGS_VIEW,
],
[
"system.observability.logs-trades",
"/ase/system/observability/logs/trades",
"pages.admin.hubs.tabs.trades",
PERMS.LOGS_VIEW,
],
[
"system.operations.alerts",
"/ase/system/operations/alerts",
"pages.admin.hubs.tabs.alerts",
PERMS.NOTIFICATIONS_VIEW,
],
[
"system.operations.command-center",
"/ase/system/operations/command-center",
"pages.admin.hubs.tabs.commando",
PERMS.RCON_EXECUTE,
],
[
"system.operations.maintenance",
"/ase/system/operations/maintenance",
"pages.admin.hubs.tabs.maintenance",
PERMS.SETTINGS_VIEW,
],
] as const;
describe("SYSTEM_ROUTES", () => {
it("declares the exact ordered System route IDs", () => {
expect(SYSTEM_ROUTE_IDS).toEqual(expectedRoutes.map(([id]) => id));
expect(SYSTEM_ROUTES.map((route) => route.id)).toEqual(SYSTEM_ROUTE_IDS);
});
it("uses the canonical destinations, stable labels, and read capabilities", () => {
expect(
SYSTEM_ROUTES.map((route) => [
route.id,
route.href,
route.labelKey,
route.capability.mode,
route.capability.slugs,
]),
).toEqual(
expectedRoutes.map(([id, href, labelKey, capability]) => [
id,
href,
labelKey,
"any",
[capability],
]),
);
});
it("covers every non-removed System migration destination exactly once", () => {
const plannedTargets = systemMigrationEntries
.filter((entry) => entry.targetPath !== null)
.map((entry) => entry.targetPath)
.sort();
const routeTargets = SYSTEM_ROUTES.map((route) => route.href).sort();
expect(routeTargets).toEqual(plannedTargets);
expect(new Set(routeTargets).size).toBe(routeTargets.length);
});
});
@@ -0,0 +1,132 @@
import { PERMS } from "@/lib/permission-slugs";
import {
anyCapability,
type HousekeepingRouteDefinition,
} from "../../foundation/contracts";
export const SYSTEM_ROUTE_IDS = [
"system.access.permissions",
"system.access.permission-detail",
"system.configuration.settings",
"system.configuration.emulator",
"system.observability.analytics",
"system.observability.analytics-activity",
"system.observability.analytics-economy",
"system.observability.devops",
"system.observability.devops-errors",
"system.observability.logs-staff",
"system.observability.logs-audit",
"system.observability.logs-chat",
"system.observability.logs-commands",
"system.observability.logs-trades",
"system.operations.alerts",
"system.operations.command-center",
"system.operations.maintenance",
] as const;
export type SystemRouteId = (typeof SYSTEM_ROUTE_IDS)[number];
export const SYSTEM_ROUTES = [
{
id: "system.access.permissions",
labelKey: "pages.admin.hubs.tabs.permissions",
href: "/ase/system/access/permissions",
capability: anyCapability(PERMS.PERMISSIONS_MANAGE),
},
{
id: "system.access.permission-detail",
labelKey: "pages.admin.hubs.tabs.permissions",
href: "/ase/system/access/permissions/:id",
capability: anyCapability(PERMS.PERMISSIONS_MANAGE),
},
{
id: "system.configuration.settings",
labelKey: "pages.admin.hubs.tabs.cms",
href: "/ase/system/configuration/settings",
capability: anyCapability(PERMS.SETTINGS_VIEW),
},
{
id: "system.configuration.emulator",
labelKey: "pages.admin.hubs.tabs.emulator",
href: "/ase/system/configuration/emulator",
capability: anyCapability(PERMS.SETTINGS_VIEW),
},
{
id: "system.observability.analytics",
labelKey: "pages.admin.hubs.tabs.analytics",
href: "/ase/system/observability/analytics",
capability: anyCapability(PERMS.ANALYTICS_VIEW),
},
{
id: "system.observability.analytics-activity",
labelKey: "pages.admin.hubs.tabs.activity",
href: "/ase/system/observability/analytics/activity",
capability: anyCapability(PERMS.ANALYTICS_VIEW),
},
{
id: "system.observability.analytics-economy",
labelKey: "pages.admin.hubs.tabs.economy",
href: "/ase/system/observability/analytics/economy",
capability: anyCapability(PERMS.ANALYTICS_VIEW),
},
{
id: "system.observability.devops",
labelKey: "pages.admin.hubs.tabs.devops",
href: "/ase/system/observability/devops",
capability: anyCapability(PERMS.DEVOPS_VIEW),
},
{
id: "system.observability.devops-errors",
labelKey: "pages.admin.hubs.tabs.errors",
href: "/ase/system/observability/devops/errors",
capability: anyCapability(PERMS.DEVOPS_VIEW),
},
{
id: "system.observability.logs-staff",
labelKey: "pages.admin.hubs.tabs.logs",
href: "/ase/system/observability/logs/staff",
capability: anyCapability(PERMS.LOGS_VIEW),
},
{
id: "system.observability.logs-audit",
labelKey: "pages.admin.hubs.tabs.audit",
href: "/ase/system/observability/logs/audit",
capability: anyCapability(PERMS.LOGS_VIEW),
},
{
id: "system.observability.logs-chat",
labelKey: "pages.admin.hubs.tabs.chat",
href: "/ase/system/observability/logs/chat",
capability: anyCapability(PERMS.LOGS_VIEW),
},
{
id: "system.observability.logs-commands",
labelKey: "pages.admin.hubs.tabs.commands",
href: "/ase/system/observability/logs/commands",
capability: anyCapability(PERMS.LOGS_VIEW),
},
{
id: "system.observability.logs-trades",
labelKey: "pages.admin.hubs.tabs.trades",
href: "/ase/system/observability/logs/trades",
capability: anyCapability(PERMS.LOGS_VIEW),
},
{
id: "system.operations.alerts",
labelKey: "pages.admin.hubs.tabs.alerts",
href: "/ase/system/operations/alerts",
capability: anyCapability(PERMS.NOTIFICATIONS_VIEW),
},
{
id: "system.operations.command-center",
labelKey: "pages.admin.hubs.tabs.commando",
href: "/ase/system/operations/command-center",
capability: anyCapability(PERMS.RCON_EXECUTE),
},
{
id: "system.operations.maintenance",
labelKey: "pages.admin.hubs.tabs.maintenance",
href: "/ase/system/operations/maintenance",
capability: anyCapability(PERMS.SETTINGS_VIEW),
},
] as const satisfies readonly HousekeepingRouteDefinition[];
@@ -0,0 +1,108 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { PERMS } from "@/lib/permission-slugs";
import type { HousekeepingCapabilityContext } from "../../../foundation/contracts";
const { markReadWhere, rconSend } = vi.hoisted(() => ({
markReadWhere: vi.fn(),
rconSend: vi.fn(),
}));
vi.mock("@/lib/db", async (importOriginal) => {
const actual = await importOriginal<typeof import("@/lib/db")>();
return {
...actual,
db: {
...actual.db,
update: vi.fn(() => ({
set: vi.fn(() => ({ where: markReadWhere })),
})),
},
};
});
vi.mock("@/lib/services/rcon", async (importOriginal) => {
const actual = await importOriginal<typeof import("@/lib/services/rcon")>();
return {
...actual,
rcon: { send: rconSend },
};
});
import { systemMutationService } from "./mutations";
function capabilityContext(
granted: readonly string[],
): HousekeepingCapabilityContext {
const permissions = new Set(granted);
return {
actor: { id: 42, username: "operator", rank: 500 },
isSuperAdmin: false,
has: (slug) => permissions.has(slug),
hasAny: (...slugs) => slugs.some((slug) => permissions.has(slug)),
hasAll: (...slugs) => slugs.every((slug) => permissions.has(slug)),
};
}
describe("System production mutation failures", () => {
beforeEach(() => {
markReadWhere.mockReset();
rconSend.mockReset();
});
it("maps a failed alert broadcast to dependency unavailable", async () => {
rconSend.mockResolvedValue(false);
const result = await systemMutationService.execute(
{
capability: capabilityContext([PERMS.NOTIFICATIONS_EDIT]),
correlationId: "broadcast-failure",
},
"operations.alert.broadcast",
{ message: "Hotel notice" },
);
expect(result).toMatchObject({
ok: false,
error: { code: "DEPENDENCY_UNAVAILABLE" },
correlationId: "broadcast-failure",
});
});
it("maps a thrown alert broadcast to dependency unavailable", async () => {
rconSend.mockRejectedValue(new Error("RCON unavailable"));
const result = await systemMutationService.execute(
{
capability: capabilityContext([PERMS.NOTIFICATIONS_EDIT]),
correlationId: "broadcast-error",
},
"operations.alert.broadcast",
{ message: "Hotel notice" },
);
expect(result).toMatchObject({
ok: false,
error: { code: "DEPENDENCY_UNAVAILABLE" },
correlationId: "broadcast-error",
});
});
it("maps mark-read persistence failure to dependency unavailable", async () => {
markReadWhere.mockRejectedValue(new Error("database unavailable"));
const result = await systemMutationService.execute(
{
capability: capabilityContext([PERMS.NOTIFICATIONS_VIEW]),
correlationId: "mark-read-error",
},
"operations.alerts.mark-read",
{},
);
expect(result).toMatchObject({
ok: false,
error: { code: "DEPENDENCY_UNAVAILABLE" },
correlationId: "mark-read-error",
});
});
});
@@ -0,0 +1,763 @@
import "server-only";
import { and, count, eq, inArray, sql } from "drizzle-orm";
import type { ResultSetHeader } from "mysql2";
import { MANAGED_SETTING_KEYS } from "@/app/admin/settings/cms-settings-config";
import {
AclModelPermission,
AclModelRole,
AclPermission,
AclRole,
AlertLogs,
db,
EmulatorSettings,
EmulatorTexts,
User,
WebsiteSetting,
} from "@/lib/db";
import {
HABBO_GAMEDATA_HOTEL_SETTING_KEY,
normalizeHabboGamedataHotel,
} from "@/lib/habbo-gamedata-hotel";
import { PERMS } from "@/lib/permission-slugs";
import { clearOfficialHabboFurnidataCache } from "@/lib/services/habbo-furnidata-cache";
import { clearBadgeCache } from "@/lib/services/habboassets";
import {
createEmulatorRank,
deleteEmulatorRank,
updateEmulatorRank,
} from "@/lib/services/permission-ranks";
import { rcon } from "@/lib/services/rcon";
import { siteSettings } from "@/lib/services/site-settings";
import { logStaffActivity } from "@/lib/services/staff-activity";
import { satisfiesCapability } from "../../../foundation/capability-context";
import {
anyCapability,
fail,
type HousekeepingCapabilityContext,
type HousekeepingErrorCode,
type HousekeepingResult,
ok,
} from "../../../foundation/contracts";
export type SystemMutationOperation =
| "access.rank.create"
| "access.rank.delete"
| "access.rank.update"
| "access.permissions.update"
| "access.permissions.repair"
| "configuration.settings.save"
| "configuration.setting.create"
| "configuration.setting.update"
| "configuration.setting.delete"
| "configuration.emulator-setting.update"
| "configuration.emulator-text.update"
| "operations.alerts.mark-read"
| "operations.alert.broadcast"
| "rcon.update-catalog"
| "rcon.update-word-filter"
| "rcon.update-navigator"
| "rcon.hotel-alert"
| "rcon.disconnect-user"
| "rcon.alert-user"
| "rcon.forward-user"
| "rcon.give-credits"
| "rcon.give-duckets"
| "rcon.give-diamonds"
| "rcon.give-badge"
| "rcon.set-motto"
| "rcon.set-rank"
| "rcon.execute-command"
| "rcon.send-gift"
| "operations.maintenance.update";
export interface SystemMutationContext {
readonly capability: HousekeepingCapabilityContext;
readonly correlationId: string;
}
export interface SystemMutationAdapter {
execute(
operation: SystemMutationOperation,
input: unknown,
context: SystemMutationContext,
): Promise<unknown>;
}
export interface SystemMutationService {
execute(
context: SystemMutationContext,
operation: SystemMutationOperation,
input: unknown,
): Promise<HousekeepingResult<unknown>>;
}
class SystemMutationFailure extends Error {
constructor(
readonly code: HousekeepingErrorCode,
readonly messageKey: string,
readonly fieldErrors?: Readonly<Record<string, readonly string[]>>,
) {
super(messageKey);
this.name = "SystemMutationFailure";
}
}
function operationCapability(operation: SystemMutationOperation) {
if (operation.startsWith("access.")) {
return anyCapability(PERMS.PERMISSIONS_MANAGE);
}
if (operation.startsWith("configuration.")) {
return anyCapability(PERMS.SETTINGS_EDIT);
}
if (operation === "operations.alerts.mark-read") {
return anyCapability(PERMS.NOTIFICATIONS_VIEW);
}
if (operation === "operations.alert.broadcast") {
return anyCapability(PERMS.NOTIFICATIONS_EDIT);
}
if (operation.startsWith("rcon.")) {
return anyCapability(PERMS.RCON_EXECUTE);
}
return anyCapability(PERMS.SETTINGS_EDIT);
}
export function createSystemMutationService(
adapter: SystemMutationAdapter,
): SystemMutationService {
return {
async execute(context, operation, input) {
if (
!satisfiesCapability(context.capability, operationCapability(operation))
) {
return fail(
"FORBIDDEN",
"errors.housekeeping.forbidden",
context.correlationId,
);
}
try {
return ok(
await adapter.execute(operation, input, context),
context.correlationId,
);
} catch (error) {
if (error instanceof SystemMutationFailure) {
return fail(
error.code,
error.messageKey,
context.correlationId,
error.fieldErrors,
);
}
return fail(
"DEPENDENCY_UNAVAILABLE",
"errors.housekeeping.dependencyUnavailable",
context.correlationId,
);
}
},
};
}
function record(input: unknown): Record<string, unknown> {
if (typeof input !== "object" || input === null || Array.isArray(input)) {
throw new SystemMutationFailure(
"VALIDATION",
"errors.housekeeping.validation",
);
}
return input as Record<string, unknown>;
}
function text(value: unknown, maxLength: number, trim = true): string {
const normalized = String(value ?? "").normalize("NFC");
return (trim ? normalized.trim() : normalized).slice(0, maxLength);
}
function positiveInteger(value: unknown): number {
const parsed = Number(value);
if (!Number.isInteger(parsed) || parsed <= 0) {
throw new SystemMutationFailure(
"VALIDATION",
"errors.housekeeping.validation",
);
}
return parsed;
}
function normalizeSettingValue(key: string, value: string): string {
return key === HABBO_GAMEDATA_HOTEL_SETTING_KEY
? normalizeHabboGamedataHotel(value)
: value;
}
function bustGamedataCachesIfNeeded(key: string): void {
if (key !== HABBO_GAMEDATA_HOTEL_SETTING_KEY) return;
clearOfficialHabboFurnidataCache();
clearBadgeCache();
}
async function requireRcon(result: boolean): Promise<void> {
if (!result) {
throw new SystemMutationFailure(
"DEPENDENCY_UNAVAILABLE",
"errors.housekeeping.dependencyUnavailable",
);
}
}
async function upsertWebsiteSetting(
key: string,
value: string,
comment?: string | null,
): Promise<void> {
await db
.insert(WebsiteSetting)
.values({ key, value, ...(comment === undefined ? {} : { comment }) })
.onDuplicateKeyUpdate({ set: { value } });
}
async function executeAccessMutation(
operation: Extract<SystemMutationOperation, `access.${string}`>,
input: unknown,
context: SystemMutationContext,
): Promise<unknown> {
const data = record(input);
if (operation === "access.rank.create") {
const name = text(data.name, 25);
const level = positiveInteger(data.level);
const id = await createEmulatorRank(db, { rank_name: name, level });
await db
.insert(AclRole)
.values({
slug: `rank_${id}`,
title: name,
description: "CMS role synchronized from permission_ranks",
})
.onDuplicateKeyUpdate({ set: { title: name } });
await logStaffActivity({
staffId: context.capability.actor.id,
action: "rank_create",
description: `Created rank #${id}`,
targetType: "rank",
targetId: id,
});
await rcon.send("updatepermissions");
return { id };
}
const id = positiveInteger(data.id);
if (operation === "access.rank.delete") {
const [userCount] = await db
.select({ total: count() })
.from(User)
.where(eq(User.rank, id));
const users = Number(userCount?.total ?? 0);
if (users > 0) {
throw new SystemMutationFailure(
"CONFLICT",
"errors.housekeeping.system.rankInUse",
{ rank: [String(users)] },
);
}
const [role] = await db
.select({ id: AclRole.id })
.from(AclRole)
.where(eq(AclRole.slug, `rank_${id}`))
.limit(1);
await deleteEmulatorRank(db, id);
if (role) {
await db.transaction(async (tx) => {
await tx
.delete(AclModelPermission)
.where(
and(
eq(AclModelPermission.modelId, role.id),
eq(AclModelPermission.modelType, "Role"),
),
);
await tx.delete(AclModelRole).where(eq(AclModelRole.roleId, role.id));
await tx.delete(AclRole).where(eq(AclRole.id, role.id));
});
}
await logStaffActivity({
staffId: context.capability.actor.id,
action: "rank_delete",
description: `Deleted rank #${id}`,
targetType: "rank",
targetId: id,
});
await rcon.send("updatepermissions");
return null;
}
if (operation === "access.rank.update") {
const fields = record(data.fields);
const normalizedFields = Object.fromEntries(
Object.entries(fields).flatMap(([key, value]) =>
typeof value === "string" || typeof value === "number"
? [[key, value] as const]
: [],
),
);
await updateEmulatorRank(db, id, normalizedFields);
if (typeof normalizedFields.rank_name === "string") {
await db
.update(AclRole)
.set({ title: normalizedFields.rank_name })
.where(eq(AclRole.slug, `rank_${id}`));
}
await logStaffActivity({
staffId: context.capability.actor.id,
action: "rank_update",
description: `Updated rank #${id}`,
targetType: "rank",
targetId: id,
});
await rcon.send("updatepermissions");
return null;
}
if (operation === "access.permissions.update") {
const roleId = positiveInteger(data.roleId);
const slugs = Array.isArray(data.permissionSlugs)
? data.permissionSlugs.map((slug) => text(slug, 160)).filter(Boolean)
: [];
const [role] = await db
.select({ id: AclRole.id, slug: AclRole.slug })
.from(AclRole)
.where(eq(AclRole.id, roleId))
.limit(1);
if (!role) {
throw new SystemMutationFailure(
"NOT_FOUND",
"errors.housekeeping.system.roleNotFound",
);
}
const permissions = slugs.length
? await db
.select({ id: AclPermission.id })
.from(AclPermission)
.where(inArray(AclPermission.slug, slugs))
: [];
await db.transaction(async (tx) => {
await tx
.delete(AclModelPermission)
.where(
and(
eq(AclModelPermission.modelId, role.id),
eq(AclModelPermission.modelType, "Role"),
),
);
if (permissions.length) {
await tx.insert(AclModelPermission).values(
permissions.map((permission) => ({
modelId: role.id,
modelType: "Role",
permissionId: permission.id,
})),
);
}
});
await logStaffActivity({
staffId: context.capability.actor.id,
action: "acl_role_permissions_update",
description: `Updated ${permissions.length} permissions for ${role.slug}`,
targetType: "acl_role",
targetId: role.id,
});
return { updated: permissions.length };
}
const [dashboardFillResult] = await db.execute(sql`
INSERT INTO acl_model_permissions (model_type, model_id, permission_id)
SELECT 'Role', ar.id, ap.id
FROM acl_roles ar
JOIN acl_permissions ap ON ap.slug LIKE 'admin.%'
WHERE EXISTS (
SELECT 1 FROM acl_model_permissions amp
JOIN acl_permissions apdash ON apdash.id = amp.permission_id
WHERE amp.model_type = 'Role' AND amp.model_id = ar.id
AND apdash.slug = 'admin.dashboard'
)
AND NOT EXISTS (
SELECT 1 FROM acl_model_permissions amp2
WHERE amp2.model_type = 'Role' AND amp2.model_id = ar.id
AND amp2.permission_id = ap.id
)
`);
const [midRankViewsResult] = await db.execute(sql`
INSERT INTO acl_model_permissions (model_type, model_id, permission_id)
SELECT 'Role', ar.id, ap.id
FROM permission_ranks pr
JOIN acl_roles ar ON ar.slug = CONCAT('rank_', pr.id)
JOIN acl_permissions ap ON (
ap.slug = 'admin.dashboard'
OR (ap.slug LIKE 'admin.%' AND ap.slug LIKE '%.view')
)
WHERE pr.id >= 6
AND NOT EXISTS (
SELECT 1
FROM acl_model_permissions amp
WHERE amp.model_type = 'Role'
AND amp.model_id = ar.id
AND amp.permission_id = ap.id
)
`);
const [highRankToolsResult] = await db.execute(sql`
INSERT INTO acl_model_permissions (model_type, model_id, permission_id)
SELECT 'Role', ar.id, ap.id
FROM permission_ranks pr
JOIN acl_roles ar ON ar.slug = CONCAT('rank_', pr.id)
JOIN acl_permissions ap ON (
(ap.slug LIKE 'admin.%' AND ap.slug LIKE '%.edit')
OR ap.slug IN (
'admin.permissions.manage',
'admin.rcon.execute',
'admin.assets.import',
'admin.export',
'admin.analytics.export',
'admin.users.ban',
'admin.users.reset_password',
'admin.room.delete'
)
)
WHERE pr.id >= 7
AND NOT EXISTS (
SELECT 1
FROM acl_model_permissions amp
WHERE amp.model_type = 'Role'
AND amp.model_id = ar.id
AND amp.permission_id = ap.id
)
`);
const inserted =
Number((dashboardFillResult as ResultSetHeader).affectedRows) +
Number((midRankViewsResult as ResultSetHeader).affectedRows) +
Number((highRankToolsResult as ResultSetHeader).affectedRows);
await logStaffActivity({
staffId: context.capability.actor.id,
action: "acl_nav_grants_repair",
description: `Repaired admin nav ACL grants (${inserted} rows inserted)`,
targetType: "acl",
targetId: 0,
});
return { inserted };
}
async function executeConfigurationMutation(
operation: Extract<SystemMutationOperation, `configuration.${string}`>,
input: unknown,
): Promise<unknown> {
const data = record(input);
if (operation === "configuration.settings.save") {
const settings = record(data.settings);
const allowedKeys = new Set<string>(MANAGED_SETTING_KEYS);
const entries = Object.entries(settings).flatMap(([key, value]) =>
allowedKeys.has(key) && typeof value === "string"
? [[key, normalizeSettingValue(key, value)] as const]
: [],
);
await Promise.all(
entries.map(([key, value]) => upsertWebsiteSetting(key, value)),
);
await siteSettings.reload();
if (entries.some(([key]) => key === HABBO_GAMEDATA_HOTEL_SETTING_KEY)) {
clearOfficialHabboFurnidataCache();
clearBadgeCache();
}
return { saved: entries.length };
}
if (operation === "configuration.emulator-setting.update") {
const key = text(data.key, 100);
if (!key) {
throw new SystemMutationFailure(
"VALIDATION",
"errors.housekeeping.validation",
);
}
const value = text(data.value, 512, false);
await db
.insert(EmulatorSettings)
.values({ key, value })
.onDuplicateKeyUpdate({ set: { value } });
return null;
}
if (operation === "configuration.emulator-text.update") {
const key = text(data.key, 100);
if (!key) {
throw new SystemMutationFailure(
"VALIDATION",
"errors.housekeeping.validation",
);
}
const value = text(data.value, 4096, false);
await db
.insert(EmulatorTexts)
.values({ key, value })
.onDuplicateKeyUpdate({ set: { value } });
return null;
}
const key = text(data.key, 255);
if (!key) {
throw new SystemMutationFailure(
"VALIDATION",
"errors.housekeeping.validation",
);
}
if (operation === "configuration.setting.delete") {
await db.delete(WebsiteSetting).where(eq(WebsiteSetting.key, key));
await siteSettings.reload();
bustGamedataCachesIfNeeded(key);
return null;
}
const value = normalizeSettingValue(key, text(data.value, 65_535, false));
const comment =
operation === "configuration.setting.create"
? text(data.comment, 255) || null
: undefined;
await upsertWebsiteSetting(key, value, comment);
await siteSettings.reload();
bustGamedataCachesIfNeeded(key);
return null;
}
async function executeRconMutation(
operation: Extract<SystemMutationOperation, `rcon.${string}`>,
input: unknown,
context: SystemMutationContext,
): Promise<unknown> {
const data = record(input);
switch (operation) {
case "rcon.update-catalog":
await requireRcon(await rcon.updateCatalog());
break;
case "rcon.update-word-filter":
await requireRcon(await rcon.updateWordFilter());
break;
case "rcon.update-navigator":
await requireRcon(await rcon.send("updatenavigator", null));
break;
case "rcon.hotel-alert":
await requireRcon(
await rcon.send("hotelalert", { message: text(data.message, 512) }),
);
break;
case "rcon.disconnect-user":
await requireRcon(
await rcon.disconnectUser(
positiveInteger(data.userId),
text(data.username, 255),
),
);
break;
case "rcon.alert-user":
await requireRcon(
await rcon.alertUser(
positiveInteger(data.userId),
text(data.message, 512),
),
);
break;
case "rcon.forward-user":
await requireRcon(
await rcon.forwardUser(
positiveInteger(data.userId),
positiveInteger(data.roomId),
),
);
break;
case "rcon.give-credits":
await requireRcon(
await rcon.giveCredits(
positiveInteger(data.userId),
positiveInteger(data.amount),
),
);
break;
case "rcon.give-duckets":
await requireRcon(
await rcon.giveDuckets(
positiveInteger(data.userId),
positiveInteger(data.amount),
),
);
break;
case "rcon.give-diamonds":
await requireRcon(
await rcon.giveDiamonds(
positiveInteger(data.userId),
positiveInteger(data.amount),
),
);
break;
case "rcon.give-badge":
await requireRcon(
await rcon.giveBadge(
positiveInteger(data.userId),
text(data.badge, 32),
),
);
break;
case "rcon.set-motto":
await requireRcon(
await rcon.setMotto(
positiveInteger(data.userId),
text(data.motto, 127),
),
);
break;
case "rcon.set-rank": {
const userId = positiveInteger(data.userId);
const rank = positiveInteger(data.rank);
const [target] = await db
.select({ rank: User.rank })
.from(User)
.where(eq(User.id, userId))
.limit(1);
if (!target) {
throw new SystemMutationFailure(
"NOT_FOUND",
"errors.housekeeping.system.userNotFound",
);
}
let rankRows: { id: number }[] = [];
try {
const [rows] = await db.execute(
sql`SELECT id FROM permission_ranks WHERE id = ${rank} LIMIT 1`,
);
rankRows = rows as unknown as { id: number }[];
} catch {
rankRows = [];
}
if (rankRows.length === 0) {
throw new SystemMutationFailure(
"NOT_FOUND",
"errors.housekeeping.system.rankNotFound",
);
}
if (!context.capability.isSuperAdmin) {
const actorRank = context.capability.actor.rank;
if (target.rank >= actorRank) {
throw new SystemMutationFailure(
"FORBIDDEN",
"errors.housekeeping.system.cannotChangePeerRank",
);
}
if (rank >= actorRank) {
throw new SystemMutationFailure(
"FORBIDDEN",
"errors.housekeeping.system.cannotAssignPeerRank",
);
}
}
await requireRcon(await rcon.setRank(userId, rank));
await db.update(User).set({ rank }).where(eq(User.id, userId));
break;
}
case "rcon.execute-command":
await requireRcon(
await rcon.executeCommand(
positiveInteger(data.userId),
text(data.command, 100),
),
);
break;
case "rcon.send-gift":
await requireRcon(
await rcon.sendGift(
positiveInteger(data.userId),
positiveInteger(data.itemId),
text(data.message || "Here is a gift.", 255) || "Here is a gift.",
),
);
break;
}
return null;
}
const systemProductionMutationAdapter: SystemMutationAdapter = {
async execute(operation, input, context) {
if (operation.startsWith("access.")) {
return executeAccessMutation(
operation as Extract<SystemMutationOperation, `access.${string}`>,
input,
context,
);
}
if (operation.startsWith("configuration.")) {
return executeConfigurationMutation(
operation as Extract<
SystemMutationOperation,
`configuration.${string}`
>,
input,
);
}
if (operation.startsWith("rcon.")) {
return executeRconMutation(
operation as Extract<SystemMutationOperation, `rcon.${string}`>,
input,
context,
);
}
if (operation === "operations.alerts.mark-read") {
await db
.update(AlertLogs)
.set({ isRead: true, updatedAt: new Date() })
.where(eq(AlertLogs.isRead, false));
return null;
}
if (operation === "operations.alert.broadcast") {
const message = text(record(input).message, 1000);
if (!message) {
throw new SystemMutationFailure(
"VALIDATION",
"errors.housekeeping.validation",
);
}
await requireRcon(await rcon.send("hotelalert", { message }));
return { delivered: true };
}
const data = record(input);
const enabled = Boolean(data.enabled);
const message = text(data.message, 65_535, false);
const parsedRank = Number(data.minimumLoginRank);
const minimumLoginRank =
Number.isInteger(parsedRank) && parsedRank >= 0 ? parsedRank : 5;
const rows = [
[
"maintenance_enabled",
enabled ? "1" : "0",
"Determines whether maintenance is enabled or not",
],
[
"maintenance_message",
message,
"The maintenance message displayed to users while maintenance is activated",
],
[
"min_maintenance_login_rank",
String(minimumLoginRank),
"The minimum rank required to login to the hotel during maintenance",
],
] as const;
for (const [key, value, comment] of rows) {
await upsertWebsiteSetting(key, value, comment);
}
await siteSettings.reload();
return null;
},
};
export const systemMutationService = createSystemMutationService(
systemProductionMutationAdapter,
);
@@ -1,5 +1,6 @@
import { describe, expect, it } from "vitest";
import { z } from "zod";
import { SYSTEM_COMMAND_IDS } from "../../domains/system/commands/system-commands";
import { anyCapability, ok } from "../contracts";
import {
defineHousekeepingCommands,
@@ -7,6 +8,7 @@ import {
registerHousekeepingCommands,
} from "./bootstrap";
import {
getHousekeepingCommand,
type HousekeepingCommand,
registerHousekeepingCommand,
} from "./registry";
@@ -55,6 +57,9 @@ describe("housekeeping command bootstrap", () => {
});
it("registers the complete current list and seals during module initialization", () => {
expect(housekeepingCommandRegistryReady).toBe(true);
expect(
SYSTEM_COMMAND_IDS.map((id) => getHousekeepingCommand(id)?.id),
).toEqual(SYSTEM_COMMAND_IDS);
expect(() =>
registerHousekeepingCommand({
id: "system.bootstrap.too-late",
@@ -1,5 +1,6 @@
import "server-only";
import { SYSTEM_COMMANDS } from "../../domains/system/commands/system-commands";
import type { HousekeepingCommand } from "./registry";
import {
registerHousekeepingCommand,
@@ -34,7 +35,9 @@ export function registerHousekeepingCommands<
}
}
const currentHousekeepingCommands = defineHousekeepingCommands();
const currentHousekeepingCommands = defineHousekeepingCommands(
...SYSTEM_COMMANDS,
);
registerHousekeepingCommands(currentHousekeepingCommands);
sealHousekeepingCommandRegistry();
@@ -4,6 +4,7 @@ import { join, posix } from "node:path";
import { createElement, type ReactElement } from "react";
import { renderToStaticMarkup } from "react-dom/server";
import { describe, expect, it } from "vitest";
import { SYSTEM_ROUTE_IDS } from "../domains/system/routes";
import { HOUSEKEEPING_MANIFESTS } from "../manifests";
import { discoverLegacyPages } from "../migration/discover-legacy-pages";
import { HOUSEKEEPING_MIGRATION_MATRIX } from "../migration/matrix";
@@ -17,6 +18,81 @@ const SERVER_CAPABILITY_CONTEXT =
"src/features/housekeeping/foundation/server-capability-context.ts";
const PERMISSIONS_ADAPTER = "src/lib/permissions";
const DOMAIN_MODULE_ROOT = "src/features/housekeeping/domains";
const approvedSystemRuntimeImports = new Map<string, ReadonlySet<string>>([
[
"src/features/housekeeping/domains/system/commands/system-commands.ts",
new Set(["src/features/housekeeping/domains/system/services/mutations"]),
],
[
"src/features/housekeeping/domains/system/pages/access.tsx",
new Set(["src/features/housekeeping/domains/system/queries/access"]),
],
[
"src/features/housekeeping/domains/system/pages/configuration.tsx",
new Set(["src/features/housekeeping/domains/system/queries/configuration"]),
],
[
"src/features/housekeeping/domains/system/pages/observability.tsx",
new Set(["src/features/housekeeping/domains/system/queries/observability"]),
],
[
"src/features/housekeeping/domains/system/pages/operations.tsx",
new Set(["src/features/housekeeping/domains/system/queries/operations"]),
],
[
"src/features/housekeeping/domains/system/queries/access.ts",
new Set(["src/lib/db", "drizzle-orm"]),
],
[
"src/features/housekeeping/domains/system/queries/configuration.ts",
new Set(["src/lib/db", "drizzle-orm"]),
],
[
"src/features/housekeeping/domains/system/queries/observability.ts",
new Set(["src/lib/db", "drizzle-orm"]),
],
[
"src/features/housekeeping/domains/system/queries/operations.ts",
new Set([
"src/features/housekeeping/domains/system/queries/observability",
"src/lib/db",
"drizzle-orm",
]),
],
[
"src/features/housekeeping/domains/system/services/mutations.ts",
new Set([
"src/app/admin/settings/cms-settings-config",
"src/lib/db",
"drizzle-orm",
"mysql2",
]),
],
[
"src/features/housekeeping/domains/system/manifest.ts",
new Set(["src/features/housekeeping/domains/system/routes"]),
],
[
"src/features/housekeeping/domains/system/route-handlers.ts",
new Set([
"src/features/housekeeping/domains/system/pages/access",
"src/features/housekeeping/domains/system/pages/configuration",
"src/features/housekeeping/domains/system/pages/observability",
"src/features/housekeeping/domains/system/pages/operations",
"src/features/housekeeping/domains/system/routes",
]),
],
[
"src/features/housekeeping/foundation/commands/bootstrap.ts",
new Set([
"src/features/housekeeping/domains/system/commands/system-commands",
]),
],
[
"src/features/housekeeping/route-handlers.ts",
new Set(["src/features/housekeeping/domains/system/route-handlers"]),
],
]);
const forbiddenModuleRoots = [
"src/lib/db",
"src/lib/db-pool",
@@ -358,6 +434,17 @@ function isAllowedPermissionSetTypeImport(
);
}
function isApprovedSystemRuntimeImport(
canonical: CanonicalModuleSpecifier,
sourceFile: string,
): boolean {
const allowed = approvedSystemRuntimeImports.get(sourceFile);
return (
allowed !== undefined &&
canonical.candidates.some((candidate) => allowed.has(candidate))
);
}
function findHousekeepingImportBoundaryViolations(
source: string,
sourceFile: string,
@@ -370,6 +457,7 @@ function findHousekeepingImportBoundaryViolations(
if (canonical.violation) violations.push(canonical.violation);
if (isAllowedPermissionSetTypeImport(access, canonical, sourceFile))
continue;
if (isApprovedSystemRuntimeImport(canonical, sourceFile)) continue;
const forbiddenPath = canonical.candidates.find((candidate) =>
isForbiddenModulePath(candidate, sourceFile),
);
@@ -398,6 +486,33 @@ describe("housekeeping runtime import boundary", () => {
}
});
it("allows only the approved System vertical runtime edges", () => {
expect(
findHousekeepingImportBoundaryViolations(
'import { systemMutationService } from "../services/mutations";',
"src/features/housekeeping/domains/system/commands/system-commands.ts",
),
).toEqual([]);
expect(
findHousekeepingImportBoundaryViolations(
'import { db } from "@/lib/db";',
"src/features/housekeeping/domains/system/queries/access.ts",
),
).toEqual([]);
expect(
findHousekeepingImportBoundaryViolations(
'import { db } from "@/lib/db";',
"src/features/housekeeping/domains/system/commands/system-commands.ts",
),
).toEqual(["src/lib/db"]);
expect(
findHousekeepingImportBoundaryViolations(
'import service from "../system/services/mutations";',
"src/features/housekeeping/domains/people/manifest.ts",
),
).toEqual(["src/features/housekeeping/domains/system/services/mutations"]);
});
it.each([
[
"aliased database import",
@@ -561,7 +676,7 @@ describe("housekeeping foundation completion contracts", () => {
}
});
it("creates the real six-domain registry in locked order without workflows", () => {
it("creates the real six-domain registry with only the System routes enabled", () => {
const registry = createHousekeepingRegistry(HOUSEKEEPING_MANIFESTS);
expect(registry.domains.map((domain) => domain.id)).toEqual([
@@ -572,9 +687,16 @@ describe("housekeeping foundation completion contracts", () => {
"hotel",
"system",
]);
expect(registry.domains.every((domain) => domain.routes.length === 0)).toBe(
true,
);
expect(
registry.domains
.filter((domain) => domain.id !== "system")
.every((domain) => domain.routes.length === 0),
).toBe(true);
expect(
registry.domains
.find((domain) => domain.id === "system")
?.routes.map((route) => route.id),
).toEqual(SYSTEM_ROUTE_IDS);
});
it("keeps production preview disabled even when the flag is true", () => {
@@ -1,5 +1,6 @@
import { describe, expect, it } from "vitest";
import { PERMS } from "@/lib/permission-slugs";
import { SYSTEM_ROUTES } from "../domains/system/routes";
import { HOUSEKEEPING_MANIFESTS } from "../manifests";
import { HOUSEKEEPING_MIGRATION_MATRIX } from "../migration/matrix";
import {
@@ -353,7 +354,9 @@ describe("housekeeping registry", () => {
labelKey: expected.labelKey,
descriptionKey: expected.descriptionKey,
});
expect(actual.routes).toEqual([]);
expect(actual.routes).toEqual(
expected.id === "system" ? SYSTEM_ROUTES : [],
);
expect(actual.searchProviders).toEqual([]);
expect(actual.inboxSources).toEqual([]);
expect(actual.widgets).toEqual([]);
@@ -1,4 +1,5 @@
import { describe, expect, it } from "vitest";
import { SYSTEM_ROUTE_IDS } from "./domains/system/routes";
import { createHousekeepingRegistry } from "./foundation/registry";
import { HOUSEKEEPING_MANIFESTS } from "./manifests";
import { HOUSEKEEPING_ROUTE_HANDLERS } from "./route-handlers";
@@ -15,6 +16,6 @@ describe("housekeeping route handlers", () => {
expect(new Set(handlerIds).size).toBe(handlerIds.length);
expect([...handlerIds].sort()).toEqual([...routeIds].sort());
expect(handlerIds).toEqual([]);
expect(handlerIds).toEqual(SYSTEM_ROUTE_IDS);
});
});
+2 -1
View File
@@ -1,4 +1,5 @@
import type { ReactNode } from "react";
import { SYSTEM_ROUTE_HANDLERS } from "./domains/system/route-handlers";
import type { HousekeepingCapabilityContext } from "./foundation/contracts";
import type { HousekeepingRouteMatch } from "./foundation/routing/match-route";
@@ -13,4 +14,4 @@ export interface HousekeepingRouteHandler {
}
export const HOUSEKEEPING_ROUTE_HANDLERS: readonly HousekeepingRouteHandler[] =
Object.freeze([]);
Object.freeze([...SYSTEM_ROUTE_HANDLERS]);