Files
Epicnabbo-Catalogus-Updated…/src/features/housekeeping/domains/system/queries/configuration.ts
T

173 lines
4.5 KiB
TypeScript

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,
);