// server/utils/userConfig.ts - 用户自定义配置 JSON value 的归一化与合并 import type { Prisma } from "~~/prisma/generated/client"; import type { IUserSettingsUpdateRequest } from "~~/shared/types/settings"; export interface UserConfigValue { useCustomConfig: boolean; customApiBase: string | null; customApiKey: string | null; customModel: string | null; customMaxTokens: number | null; customTemperature: number | null; } export const DEFAULT_USER_CONFIG_VALUE: UserConfigValue = { useCustomConfig: false, customApiBase: null, customApiKey: null, customModel: null, customMaxTokens: null, customTemperature: null }; const isRecord = (value: unknown): value is Record => typeof value === "object" && value !== null && !Array.isArray(value); const cleanString = (value: unknown): string | null => { if (typeof value !== "string") return null; const trimmed = value.trim(); return trimmed.length > 0 ? trimmed : null; }; const cleanApiKey = (value: unknown): string | null => { if (typeof value !== "string") return null; return value.length > 0 ? value : null; }; const cleanNumber = (value: unknown): number | null => typeof value === "number" && Number.isFinite(value) ? value : null; export const normalizeUserConfigValue = ( value: unknown ): UserConfigValue => { if (!isRecord(value)) { return { ...DEFAULT_USER_CONFIG_VALUE }; } return { useCustomConfig: value.useCustomConfig === true, customApiBase: cleanString(value.customApiBase), customApiKey: cleanApiKey(value.customApiKey), customModel: cleanString(value.customModel), customMaxTokens: cleanNumber(value.customMaxTokens), customTemperature: cleanNumber(value.customTemperature) }; }; export const mergeUserConfigUpdate = ( current: UserConfigValue, req: IUserSettingsUpdateRequest ): UserConfigValue => { const next: UserConfigValue = { ...current }; if (req.useCustomConfig !== undefined) { next.useCustomConfig = req.useCustomConfig; } if (req.customApiBase !== undefined) { next.customApiBase = cleanString(req.customApiBase); } // 空字符串表示不修改已保存的 key,避免用户保存表单时误清空密钥 if (req.customApiKey !== undefined && req.customApiKey !== "") { next.customApiKey = cleanApiKey(req.customApiKey); } if (req.customModel !== undefined) { next.customModel = cleanString(req.customModel); } if (req.customMaxTokens !== undefined) { next.customMaxTokens = cleanNumber(req.customMaxTokens); } if (req.customTemperature !== undefined) { next.customTemperature = cleanNumber(req.customTemperature); } return next; }; export const hasUserConfigMutation = (req: IUserSettingsUpdateRequest) => req.useCustomConfig !== undefined || req.customApiBase !== undefined || (req.customApiKey !== undefined && req.customApiKey !== "") || req.customModel !== undefined || req.customMaxTokens !== undefined || req.customTemperature !== undefined; export const toUserConfigJson = ( value: UserConfigValue ): Prisma.InputJsonObject => ({ useCustomConfig: value.useCustomConfig, customApiBase: value.customApiBase, customApiKey: value.customApiKey, customModel: value.customModel, customMaxTokens: value.customMaxTokens, customTemperature: value.customTemperature });