You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
165 lines
4.5 KiB
165 lines
4.5 KiB
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<K extends KnownConfigKey>(key: K): Promise<KnownConfigValue<K>> {
|
|
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<K extends KnownConfigKey>(
|
|
userId: number | undefined,
|
|
key: K,
|
|
): Promise<KnownConfigValue<K> | 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<K extends KnownConfigKey>(
|
|
userId: number | undefined,
|
|
key: K,
|
|
): Promise<KnownConfigValue<K>> {
|
|
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<K extends KnownConfigKey>(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<K extends KnownConfigKey>(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)));
|
|
}
|
|
|