import { dbGlobal } from "drizzle-pkg/lib/db"; import { appConfigs, userConfigs } from "drizzle-pkg/lib/schema/config"; import { and, eq } from "drizzle-orm"; import { canUserOverride, deserializeConfigValue, getConfigDefaultValue, getConfigDefinition, getConfigScope, isKnownConfigKey, KnownConfigKey, KnownConfigValue, serializeConfigValue, validateConfigValue, } from "./registry"; export class ConfigKeyNotFoundError extends Error { code = "CONFIG_KEY_NOT_FOUND"; constructor(key: string) { super(`配置项不存在: ${key}`); this.name = "ConfigKeyNotFoundError"; } } export class ConfigScopeInvalidError extends Error { code = "CONFIG_SCOPE_INVALID"; constructor(message: string) { super(message); this.name = "ConfigScopeInvalidError"; } } export class ConfigValueInvalidError extends Error { code = "CONFIG_VALUE_INVALID"; constructor(message: string) { super(message); this.name = "ConfigValueInvalidError"; } } function assertKnownKey(key: string): asserts key is KnownConfigKey { if (!isKnownConfigKey(key)) { throw new ConfigKeyNotFoundError(key); } } export function ensureKnownConfigKey(key: string): KnownConfigKey { assertKnownKey(key); return key; } export async function getGlobalConfigValue(key: K): Promise> { const [row] = await dbGlobal .select({ value: appConfigs.value, }) .from(appConfigs) .where(eq(appConfigs.key, key)) .limit(1); if (!row) { return getConfigDefaultValue(key); } return deserializeConfigValue(key, row.value); } export async function getUserConfigValue( userId: number | undefined, key: K, ): Promise | undefined> { if (!userId) { return undefined; } const [row] = await dbGlobal .select({ value: userConfigs.value, }) .from(userConfigs) .where(and(eq(userConfigs.userId, userId), eq(userConfigs.key, key))) .limit(1); if (!row) { return undefined; } return deserializeConfigValue(key, row.value); } export async function getMergedConfigValue( userId: number | undefined, key: K, ): Promise> { const scope = getConfigScope(key); if (scope === "global") { return getGlobalConfigValue(key); } if (scope === "user") { return (await getUserConfigValue(userId, key)) ?? getConfigDefaultValue(key); } const userValue = await getUserConfigValue(userId, key); if (userValue !== undefined) { return userValue; } return getGlobalConfigValue(key); } export async function setGlobalConfigValue(key: K, value: unknown) { const scope = getConfigScope(key); if (scope === "user") { throw new ConfigScopeInvalidError(`配置项 ${key} 不支持全局写入`); } if (!validateConfigValue(key, value)) { throw new ConfigValueInvalidError(`配置项 ${key} 的值不合法`); } await dbGlobal .insert(appConfigs) .values({ key, value: serializeConfigValue(key, value), valueType: getConfigDefinition(key).valueType, }) .onConflictDoUpdate({ target: appConfigs.key, set: { value: serializeConfigValue(key, value), valueType: getConfigDefinition(key).valueType, }, }); } export async function setCurrentUserConfigValue(userId: number, key: K, value: unknown) { const scope = getConfigScope(key); if (scope === "global" || !canUserOverride(key)) { throw new ConfigScopeInvalidError(`配置项 ${key} 不允许用户覆盖`); } if (!validateConfigValue(key, value)) { throw new ConfigValueInvalidError(`配置项 ${key} 的值不合法`); } await dbGlobal .insert(userConfigs) .values({ userId, key, value: serializeConfigValue(key, value), valueType: getConfigDefinition(key).valueType, }) .onConflictDoUpdate({ target: [userConfigs.userId, userConfigs.key], set: { value: serializeConfigValue(key, value), valueType: getConfigDefinition(key).valueType, }, }); } export async function resetCurrentUserConfigValue(userId: number, key: KnownConfigKey) { const scope = getConfigScope(key); if (scope === "global" || !canUserOverride(key)) { throw new ConfigScopeInvalidError(`配置项 ${key} 不允许用户重置`); } await dbGlobal .delete(userConfigs) .where(and(eq(userConfigs.userId, userId), eq(userConfigs.key, key))); }