import { NextRequest, NextResponse } from "next/server"; import { z } from "zod"; import { db } from "@/lib/db"; import { siteSettings, systemSettingHistory, systemSettings } from "@/lib/db/schema"; import { eq, inArray } from "drizzle-orm"; import { auth } from "@/app/(auth)/auth"; import { isAgentWorkflowAdminType, isSiteAdminType } from "@/lib/middleware/admin"; import { defaultMembershipConfig, defaultMembershipUserOverrides, normalizeMembershipConfig, normalizeMembershipUserOverrides, } from "@/lib/membership/config"; import { defaultPointsPackages, defaultPointsSystemConfig, normalizePointsPackages, normalizePointsSystemConfig, POINTS_PACKAGES_KEY, POINTS_SYSTEM_CONFIG_KEY, } from "@/lib/points/config"; import { defaultDulupayConfig, DULUPAY_CONFIG_KEY, normalizeDulupayConfig, normalizeDulupayForPublic, } from "@/lib/payment/dulupay-config"; import { AGENT_SECONDARY_TYPE_CONFIG_KEY, defaultAgentSecondaryTypeConfig, normalizeAgentSecondaryTypeConfig, } from "@/lib/agents/secondary-type-config"; import { AGENT_WORKFLOW_UI_CONFIG_KEY, AGENT_WORKFLOW_PROMPT_FRAGMENT_MAX_LENGTH, defaultAgentWorkflowUiConfig, normalizeAgentWorkflowStepOrdering, normalizeAgentWorkflowUiConfig, } from "@/lib/agents/workflow-ui-config"; import { AGENT_WORKFLOW_BRAND_MODEL_CONFIG_KEY, defaultAgentWorkflowBrandModelConfig, normalizeAgentWorkflowBrandModelConfig, } from "@/lib/agents/workflow-brand-model-config"; import { AGENT_WORKFLOW_MATERIAL_API_CONFIG_KEY, defaultAgentWorkflowMaterialApiConfig, normalizeAgentWorkflowMaterialApiConfig, normalizeAgentWorkflowMaterialApiConfigForPublic, } from "@/lib/agents/workflow-material-config"; import { AGENT_WORKFLOW_GOODS_INFO_API_CONFIG_KEY, defaultAgentWorkflowGoodsInfoApiConfig, normalizeAgentWorkflowGoodsInfoApiConfig, normalizeAgentWorkflowGoodsInfoApiConfigForPublic, } from "@/lib/agents/workflow-goods-info-config"; import { AGENT_WORKFLOW_SHOWCASE_CONFIG_KEY, defaultAgentWorkflowShowcaseConfig, normalizeAgentWorkflowShowcaseConfig, } from "@/lib/agents/workflow-showcase-config"; import { AGENT_PSD_TEXT_COLOR_CONFIG_KEY, defaultAgentPsdTextColorConfig, normalizeAgentPsdTextColorConfig, } from "@/lib/agents/psd-text-color-config"; import { AGENT_PSD_TEMPLATE_SECONDARY_CATEGORY_CONFIG_KEY, defaultAgentPsdTemplateSecondaryCategoryConfig, normalizeAgentPsdTemplateSecondaryCategoryConfig, } from "@/lib/agents/psd-template-secondary-category-config"; import { AGENT_PSD_TEMPLATE_CATEGORY_TAG_CONFIG_KEY, buildAgentPsdTemplateCategoryTagIndex, defaultAgentPsdTemplateCategoryTagConfig, defaultAgentPsdTemplateCategoryTagIndex, normalizeAgentPsdTemplateCategoryTagConfig, } from "@/lib/agents/psd-template-category-tag-config"; import { readAgentPsdTemplates } from "@/lib/agents/psd-template-settings"; import { AGENT_PRESET_LIBRARY_CONFIG_KEY, defaultAgentPresetLibraryConfig, normalizeAgentPresetLibraryConfig, } from "@/lib/agents/preset-library-config"; import { defaultOssImageMigrationConfig, normalizeOssImageMigrationConfig, OSS_MIGRATION_CONFIG_KEY, } from "@/lib/storage/migration-config"; import { getHomeChatAnimationHtmlBytes, HOME_CHAT_ANIMATION_CONFIG_KEY, HOME_CHAT_ANIMATION_MAX_BYTES, normalizeHomeChatAnimationConfig, } from "@/lib/home/home-animation-config"; import type { AgentCategoryTreeConfig } from "@/types/agent"; const HOME_VARIANT_KEY = "home_variant"; // Historically stored as "home_surprise_categories" but used for the featured gallery categories. const HOME_SURPRISE_CATEGORIES_KEY = "home_surprise_categories"; const AI_DETAIL_GENERATOR_ENABLED_KEY = "ai_detail_generator_enabled"; const LOVART_COMPOSER_PREVIEW_TEXTS_KEY = "lovart_composer_preview_texts"; const LOVART_COMPOSER_PREFIX_KEY = "lovart_composer_prefix"; const LOVART_PROMPT_TAG_VISIBILITY_KEY = "lovart_prompt_tag_visibility"; const CONTACT_CONFIG_KEY = "contact_config"; const MEMBERSHIP_CONFIG_KEY = "membership_config"; const MEMBERSHIP_USER_OVERRIDES_KEY = "membership_user_plan_overrides"; const STORAGE_FAILOVER_CONFIG_KEY = "storage_failover_config"; const REALTIME_CANVAS_CONFIG_KEY = "realtime_canvas_config"; const LAYER_SPLIT_CONFIG_KEY = "layer_split_config"; const AGENT_CATEGORY_TREE_CONFIG_KEY = "agent_category_tree_config"; const CANVAS_AGENT_MODE_ENABLED_KEY = "canvas_agent_mode_enabled"; const VIDEO_CANVAS_ENABLED_KEY = "video_canvas_enabled"; const VIDEO_CANVAS_SIDEBAR_ENABLED_KEY = "video_canvas_sidebar_enabled"; const AGENT_WORKFLOW_SETTINGS_CONFLICT_KEYS = [ AGENT_PRESET_LIBRARY_CONFIG_KEY, AGENT_WORKFLOW_UI_CONFIG_KEY, AGENT_WORKFLOW_MATERIAL_API_CONFIG_KEY, AGENT_WORKFLOW_GOODS_INFO_API_CONFIG_KEY, AGENT_WORKFLOW_BRAND_MODEL_CONFIG_KEY, AGENT_WORKFLOW_SHOWCASE_CONFIG_KEY, AGENT_PSD_TEXT_COLOR_CONFIG_KEY, AGENT_PSD_TEMPLATE_SECONDARY_CATEGORY_CONFIG_KEY, ] as const; const AGENT_WORKFLOW_SETTINGS_FIELD_KEY_MAP = { agentPresetLibraryConfig: AGENT_PRESET_LIBRARY_CONFIG_KEY, agentWorkflowUiConfig: AGENT_WORKFLOW_UI_CONFIG_KEY, agentWorkflowMaterialApiConfig: AGENT_WORKFLOW_MATERIAL_API_CONFIG_KEY, agentWorkflowGoodsInfoApiConfig: AGENT_WORKFLOW_GOODS_INFO_API_CONFIG_KEY, agentWorkflowBrandModelConfig: AGENT_WORKFLOW_BRAND_MODEL_CONFIG_KEY, agentWorkflowShowcaseConfig: AGENT_WORKFLOW_SHOWCASE_CONFIG_KEY, agentPsdTextColorConfig: AGENT_PSD_TEXT_COLOR_CONFIG_KEY, agentPsdTemplateSecondaryCategoryConfig: AGENT_PSD_TEMPLATE_SECONDARY_CATEGORY_CONFIG_KEY, } as const; type AgentWorkflowSettingsField = keyof typeof AGENT_WORKFLOW_SETTINGS_FIELD_KEY_MAP; function stableStringifySettingsValue(value: unknown): string { if (value === null || typeof value !== "object") { return JSON.stringify(value) ?? "null"; } if (Array.isArray(value)) { return `[${value.map((item) => stableStringifySettingsValue(item)).join(",")}]`; } const entries = Object.entries(value as Record) .filter(([, item]) => item !== undefined) .sort(([left], [right]) => left.localeCompare(right)); return `{${entries .map(([key, item]) => `${JSON.stringify(key)}:${stableStringifySettingsValue(item)}`) .join(",")}}`; } function createSettingsValueSignature(value: unknown): string { const serialized = stableStringifySettingsValue(value); let hashA = 0xdeadbeef ^ serialized.length; let hashB = 0x41c6ce57 ^ serialized.length; for (let index = 0; index < serialized.length; index += 1) { const code = serialized.charCodeAt(index); hashA = Math.imul(hashA ^ code, 2654435761); hashB = Math.imul(hashB ^ code, 1597334677); } hashA = Math.imul(hashA ^ (hashA >>> 16), 2246822507) ^ Math.imul(hashB ^ (hashB >>> 13), 3266489909); hashB = Math.imul(hashB ^ (hashB >>> 16), 2246822507) ^ Math.imul(hashA ^ (hashA >>> 13), 3266489909); return `${serialized.length}:${(hashB >>> 0).toString(16)}${(hashA >>> 0).toString(16)}`; } function createAgentWorkflowSettingsStateFromValues(valueByKey: Map) { const workflowUiConfig = normalizeAgentWorkflowUiConfig( valueByKey.get(AGENT_WORKFLOW_UI_CONFIG_KEY) ); return { agentPresetLibraryConfig: normalizeAgentPresetLibraryConfig( valueByKey.get(AGENT_PRESET_LIBRARY_CONFIG_KEY) ), agentWorkflowUiConfig: { ...workflowUiConfig, steps: normalizeAgentWorkflowStepOrdering(workflowUiConfig.steps), }, agentWorkflowMaterialApiConfig: normalizeAgentWorkflowMaterialApiConfig( valueByKey.get(AGENT_WORKFLOW_MATERIAL_API_CONFIG_KEY) ), agentWorkflowGoodsInfoApiConfig: normalizeAgentWorkflowGoodsInfoApiConfig( valueByKey.get(AGENT_WORKFLOW_GOODS_INFO_API_CONFIG_KEY) ), agentWorkflowBrandModelConfig: normalizeAgentWorkflowBrandModelConfig( valueByKey.get(AGENT_WORKFLOW_BRAND_MODEL_CONFIG_KEY) ), agentWorkflowShowcaseConfig: normalizeAgentWorkflowShowcaseConfig( valueByKey.get(AGENT_WORKFLOW_SHOWCASE_CONFIG_KEY) ), agentPsdTextColorConfig: normalizeAgentPsdTextColorConfig( valueByKey.get(AGENT_PSD_TEXT_COLOR_CONFIG_KEY) ), agentPsdTemplateSecondaryCategoryConfig: normalizeAgentPsdTemplateSecondaryCategoryConfig( valueByKey.get(AGENT_PSD_TEMPLATE_SECONDARY_CATEGORY_CONFIG_KEY) ), } satisfies Record; } function createAgentWorkflowSettingsSectionSignatures(valueByKey: Map) { const state = createAgentWorkflowSettingsStateFromValues(valueByKey); return Object.fromEntries( Object.entries(state).map(([field, value]) => [field, createSettingsValueSignature(value)]) ) as Record; } function createAgentWorkflowSettingsSignature(valueByKey: Map): string { return createSettingsValueSignature(createAgentWorkflowSettingsStateFromValues(valueByKey)); } function normalizeHomeVariant(value: unknown): "classic" | "lovart" { return value === "lovart" ? "lovart" : "classic"; } function normalizeSurpriseCategories(value: unknown): string[] { if (!Array.isArray(value)) { return ["全部", "精选"]; } const normalized = value .map((item) => String(item || "").trim()) .filter(Boolean) // legacy typo compatibility: treat "惊喜" as "精选" .map((item) => (item === "惊喜" ? "精选" : item)) .slice(0, 12); if (normalized.length === 0) { return ["全部", "精选"]; } const unique = Array.from(new Set(normalized)); if (!unique.includes("全部")) { unique.unshift("全部"); } return unique; } function normalizeLovartComposerPreviewTexts(value: unknown): string[] { const defaults = [ "打造引人注目的社交媒体视觉", "帮我把这张产品图做成高级电商海报", "基于参考图延展 3 套同风格方案", ]; if (!Array.isArray(value)) { return defaults; } const normalized = value .map((item) => String(item || "").trim()) .filter(Boolean) .slice(0, 20); return normalized.length > 0 ? normalized : defaults; } function normalizeLovartComposerPrefix(value: unknown): string { const normalized = String(value || "").trim(); if (!normalized) { return "让Buyi-ai"; } return normalized.slice(0, 40); } function normalizeLovartPromptTagVisibility(value: unknown): { nanoBananaPro: boolean; design: boolean; branding: boolean; illustration: boolean; ecommerce: boolean; video: boolean; } { const raw = value && typeof value === "object" && !Array.isArray(value) ? (value as Record) : {}; return { nanoBananaPro: raw.nanoBananaPro !== false, design: raw.design !== false, branding: raw.branding !== false, illustration: raw.illustration !== false, ecommerce: raw.ecommerce !== false, video: raw.video !== false, }; } function normalizeContactConfig(value: unknown): { phone: string; wechatQrUrl: string; } { const raw = value && typeof value === "object" && !Array.isArray(value) ? (value as Record) : {}; const phone = String(raw.phone || "").trim().slice(0, 64); const wechatQrUrl = String(raw.wechatQrUrl || "").trim().slice(0, 1000); return { phone, wechatQrUrl, }; } function normalizeStorageFailoverConfig(value: unknown): { enabled: boolean; backupStorageConfigId: string; } { const raw = value && typeof value === "object" && !Array.isArray(value) ? (value as Record) : {}; const backupStorageConfigId = String(raw.backupStorageConfigId || "").trim(); return { enabled: raw.enabled === true, backupStorageConfigId, }; } function normalizeCanvasAgentModeEnabled(value: unknown): boolean { return value !== false; } function normalizeVideoCanvasEnabled( value: unknown, fallbackValue: unknown = true ): boolean { if (value !== undefined && value !== null) { return value !== false; } return fallbackValue !== false; } function normalizeVideoCanvasSidebarEnabled(value: unknown): boolean { return value !== false; } function normalizeLayerSplitEnabled(value: unknown): boolean { if (!value || typeof value !== "object" || Array.isArray(value)) { return false; } return (value as { enabled?: unknown }).enabled === true; } function normalizeAgentCategoryTreeConfig(value: unknown): AgentCategoryTreeConfig { const raw = value && typeof value === "object" && !Array.isArray(value) ? (value as Record) : {}; const normalizeList = (input: unknown) => { if (!Array.isArray(input)) return [] as string[]; return Array.from( new Set( input .map((item) => String(item || "").trim()) .filter(Boolean) .slice(0, 200) ) ); }; return { productCategories: normalizeList(raw.productCategories), audienceTags: normalizeList(raw.audienceTags), usageScenarios: normalizeList(raw.usageScenarios), brands: normalizeList(raw.brands), }; } // GET /api/site-settings - 获取网站设置 (公开) export async function GET() { try { const session = await auth().catch(() => null); const isAdmin = isSiteAdminType(session?.user?.type); const canManageAgentWorkflow = isAgentWorkflowAdminType(session?.user?.type); const [ settings, notificationSetting, homeVariantSetting, surpriseCategoriesSetting, aiDetailGeneratorEnabledSetting, lovartComposerPreviewTextsSetting, lovartComposerPrefixSetting, lovartPromptTagVisibilitySetting, contactConfigSetting, membershipConfigSetting, membershipOverridesSetting, pointsSystemConfigSetting, pointsPackagesSetting, dulupayConfigSetting, realtimeCanvasConfigSetting, layerSplitConfigSetting, canvasAgentModeEnabledSetting, videoCanvasEnabledSetting, videoCanvasSidebarEnabledSetting, storageFailoverConfigSetting, ossImageMigrationConfigSetting, agentPresetLibraryConfigSetting, agentCategoryTreeConfigSetting, agentSecondaryTypeConfigSetting, agentWorkflowUiConfigSetting, agentWorkflowMaterialApiConfigSetting, agentWorkflowGoodsInfoApiConfigSetting, agentWorkflowBrandModelConfigSetting, agentWorkflowShowcaseConfigSetting, agentPsdTextColorConfigSetting, agentPsdTemplateSecondaryCategoryConfigSetting, agentPsdTemplateCategoryTagConfigSetting, agentPsdTemplates, ] = await Promise.all([ db.select().from(siteSettings).limit(1).then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, "login_notification")) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, HOME_VARIANT_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, HOME_SURPRISE_CATEGORIES_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, AI_DETAIL_GENERATOR_ENABLED_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, LOVART_COMPOSER_PREVIEW_TEXTS_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, LOVART_COMPOSER_PREFIX_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, LOVART_PROMPT_TAG_VISIBILITY_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, CONTACT_CONFIG_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, MEMBERSHIP_CONFIG_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, MEMBERSHIP_USER_OVERRIDES_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, POINTS_SYSTEM_CONFIG_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, POINTS_PACKAGES_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, DULUPAY_CONFIG_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, REALTIME_CANVAS_CONFIG_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, LAYER_SPLIT_CONFIG_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, CANVAS_AGENT_MODE_ENABLED_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, VIDEO_CANVAS_ENABLED_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, VIDEO_CANVAS_SIDEBAR_ENABLED_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, STORAGE_FAILOVER_CONFIG_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, OSS_MIGRATION_CONFIG_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, AGENT_PRESET_LIBRARY_CONFIG_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, AGENT_CATEGORY_TREE_CONFIG_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, AGENT_SECONDARY_TYPE_CONFIG_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, AGENT_WORKFLOW_UI_CONFIG_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, AGENT_WORKFLOW_MATERIAL_API_CONFIG_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, AGENT_WORKFLOW_GOODS_INFO_API_CONFIG_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, AGENT_WORKFLOW_BRAND_MODEL_CONFIG_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, AGENT_WORKFLOW_SHOWCASE_CONFIG_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, AGENT_PSD_TEXT_COLOR_CONFIG_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, AGENT_PSD_TEMPLATE_SECONDARY_CATEGORY_CONFIG_KEY)) .limit(1) .then(rows => rows[0]), db .select() .from(systemSettings) .where(eq(systemSettings.key, AGENT_PSD_TEMPLATE_CATEGORY_TAG_CONFIG_KEY)) .limit(1) .then(rows => rows[0]), readAgentPsdTemplates().catch(() => []), ]); const notification = notificationSetting?.value || { enabled: false, title: "", content: "" }; const homeVariant = normalizeHomeVariant(homeVariantSetting?.value); const surpriseCategories = normalizeSurpriseCategories(surpriseCategoriesSetting?.value); const aiDetailGenerationEnabled = Boolean(aiDetailGeneratorEnabledSetting?.value); const lovartComposerPreviewTexts = normalizeLovartComposerPreviewTexts( lovartComposerPreviewTextsSetting?.value ); const lovartComposerPrefix = normalizeLovartComposerPrefix( lovartComposerPrefixSetting?.value ); const lovartPromptTagVisibility = normalizeLovartPromptTagVisibility( lovartPromptTagVisibilitySetting?.value ); const contactConfig = normalizeContactConfig(contactConfigSetting?.value); const membershipConfig = normalizeMembershipConfig(membershipConfigSetting?.value); const membershipUserOverrides = normalizeMembershipUserOverrides( membershipOverridesSetting?.value ); const pointsSystemConfig = normalizePointsSystemConfig( pointsSystemConfigSetting?.value ); const pointsPackages = normalizePointsPackages(pointsPackagesSetting?.value); const dulupayConfig = isAdmin ? normalizeDulupayConfig(dulupayConfigSetting?.value) : normalizeDulupayForPublic(dulupayConfigSetting?.value); const realtimeCanvasServiceEnabled = (realtimeCanvasConfigSetting?.value as { enabled?: boolean } | undefined)?.enabled !== false; const layerSplitEnabled = normalizeLayerSplitEnabled( layerSplitConfigSetting?.value ); const canvasAgentModeEnabled = normalizeCanvasAgentModeEnabled( canvasAgentModeEnabledSetting?.value ); const videoCanvasSidebarEnabled = normalizeVideoCanvasSidebarEnabled( videoCanvasSidebarEnabledSetting?.value ); const videoCanvasEnabled = normalizeVideoCanvasEnabled( videoCanvasEnabledSetting?.value, videoCanvasSidebarEnabledSetting?.value ); const storageFailoverConfig = normalizeStorageFailoverConfig( storageFailoverConfigSetting?.value ); const ossImageMigrationConfig = normalizeOssImageMigrationConfig( ossImageMigrationConfigSetting?.value ); const agentPresetLibraryConfig = normalizeAgentPresetLibraryConfig( agentPresetLibraryConfigSetting?.value ); const agentCategoryTreeConfig = normalizeAgentCategoryTreeConfig( agentCategoryTreeConfigSetting?.value ); const agentSecondaryTypeConfig = normalizeAgentSecondaryTypeConfig( agentSecondaryTypeConfigSetting?.value ); const agentWorkflowUiConfig = normalizeAgentWorkflowUiConfig( agentWorkflowUiConfigSetting?.value ); const agentWorkflowMaterialApiConfig = canManageAgentWorkflow ? normalizeAgentWorkflowMaterialApiConfig(agentWorkflowMaterialApiConfigSetting?.value) : normalizeAgentWorkflowMaterialApiConfigForPublic( agentWorkflowMaterialApiConfigSetting?.value ); const agentWorkflowGoodsInfoApiConfig = canManageAgentWorkflow ? normalizeAgentWorkflowGoodsInfoApiConfig(agentWorkflowGoodsInfoApiConfigSetting?.value) : normalizeAgentWorkflowGoodsInfoApiConfigForPublic( agentWorkflowGoodsInfoApiConfigSetting?.value ); const agentWorkflowBrandModelConfig = normalizeAgentWorkflowBrandModelConfig( agentWorkflowBrandModelConfigSetting?.value ); const agentWorkflowShowcaseConfig = normalizeAgentWorkflowShowcaseConfig( agentWorkflowShowcaseConfigSetting?.value ); const agentPsdTextColorConfig = normalizeAgentPsdTextColorConfig( agentPsdTextColorConfigSetting?.value ); const agentPsdTemplateSecondaryCategoryConfig = normalizeAgentPsdTemplateSecondaryCategoryConfig( agentPsdTemplateSecondaryCategoryConfigSetting?.value ); const agentPsdTemplateCategoryTagConfig = normalizeAgentPsdTemplateCategoryTagConfig( agentPsdTemplateCategoryTagConfigSetting?.value ); const agentPsdTemplateCategoryTagIndex = buildAgentPsdTemplateCategoryTagIndex({ templates: agentPsdTemplates, config: agentPsdTemplateCategoryTagConfig, }); // 如果没有设置记录,返回默认值 const baseSettings = settings || { siteName: "AI Chatbot", siteLogo: null, siteLogoDark: null, favicon: null, showAutoModel: true, defaultTheme: "system", usageTimeEnabled: false, usageTimeStart: "08:00", usageTimeEnd: "20:00", usageTimeMessage: "由壹点累了也是需要休息的", bannerEnabled: true, bannerText: "升级会员, Nano Banana Pro 免费365天!", bannerLink: null, toolbox1Name: "工具箱", toolbox1Enabled: true, toolbox2Name: "工具箱2", toolbox2Enabled: false, tutorialVideoUrl: null, tutorialEnabled: false, rankingEnabled: true, productPlanningEnabled: true, canvasEnabled: true, agentEnabled: true, canvasAgentModeEnabled: true, videoCanvasEnabled: true, videoCanvasSidebarEnabled: true, agentPresetLibraryConfig: defaultAgentPresetLibraryConfig, agentCategoryTreeConfig: normalizeAgentCategoryTreeConfig(undefined), agentSecondaryTypeConfig: defaultAgentSecondaryTypeConfig, agentWorkflowUiConfig: defaultAgentWorkflowUiConfig, agentWorkflowMaterialApiConfig: defaultAgentWorkflowMaterialApiConfig, agentWorkflowGoodsInfoApiConfig: defaultAgentWorkflowGoodsInfoApiConfig, agentWorkflowBrandModelConfig: defaultAgentWorkflowBrandModelConfig, agentWorkflowShowcaseConfig: defaultAgentWorkflowShowcaseConfig, agentPsdTextColorConfig: defaultAgentPsdTextColorConfig, agentPsdTemplateSecondaryCategoryConfig: defaultAgentPsdTemplateSecondaryCategoryConfig, agentPsdTemplateCategoryTagConfig: defaultAgentPsdTemplateCategoryTagConfig, agentPsdTemplateCategoryTagIndex: defaultAgentPsdTemplateCategoryTagIndex, realtimeCanvasEnabled: true, realtimeCanvasServiceEnabled: true, layerSplitEnabled, storageFailoverConfig, ossImageMigrationConfig, canvasDefaultModelId: null, canvasAutoModeDefaultModelId: null, canvasDetailDefaultModelId: null, canvasDetailAgentModelId: null, lovartPromoText: "现在升级,立享 Kling 3.0 与 Nano Banana Pro,最高 5 折优惠", lovartPromoActionText: "立即升级", lovartHeroTitle: "由壹点AI 让设计更简单", lovartHeroSubtitle: "懂你的设计代理,帮你搞定一切", homeVariant, surpriseCategories, aiDetailGenerationEnabled, lovartComposerPrefix, lovartComposerPreviewTexts, lovartPromptTagVisibility, contactConfig, membershipConfig, membershipUserOverrides, pointsSystemConfig, pointsPackages, dulupayConfig, }; const response = NextResponse.json({ ...baseSettings, lovartPromoText: baseSettings.lovartPromoText || "现在升级,立享 Kling 3.0 与 Nano Banana Pro,最高 5 折优惠", lovartPromoActionText: baseSettings.lovartPromoActionText || "立即升级", lovartHeroTitle: baseSettings.lovartHeroTitle || "由壹点AI 让设计更简单", lovartHeroSubtitle: baseSettings.lovartHeroSubtitle || "懂你的设计代理,帮你搞定一切", notification, homeVariant, surpriseCategories, aiDetailGenerationEnabled, lovartComposerPrefix, lovartComposerPreviewTexts, lovartPromptTagVisibility, contactConfig, agentPresetLibraryConfig, agentCategoryTreeConfig, agentSecondaryTypeConfig, agentWorkflowUiConfig, agentWorkflowMaterialApiConfig, agentWorkflowGoodsInfoApiConfig, agentWorkflowBrandModelConfig, agentWorkflowShowcaseConfig, agentPsdTextColorConfig, agentPsdTemplateSecondaryCategoryConfig, agentPsdTemplateCategoryTagConfig, agentPsdTemplateCategoryTagIndex, membershipConfig, membershipUserOverrides, pointsSystemConfig, pointsPackages, dulupayConfig, realtimeCanvasServiceEnabled, layerSplitEnabled, canvasAgentModeEnabled, videoCanvasEnabled, videoCanvasSidebarEnabled, storageFailoverConfig, ossImageMigrationConfig, }); // 禁用缓存,确保总是获取最新设置 response.headers.set("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate"); response.headers.set("Pragma", "no-cache"); response.headers.set("Expires", "0"); return response; } catch (error) { console.error("[Site Settings API] Get error:", error); return NextResponse.json( { error: "Failed to fetch site settings" }, { status: 500 } ); } } const AGENT_WORKFLOW_INPUT_OPTIONS_MAX_ITEMS = 5000; const agentWorkflowOptionSchema: z.ZodTypeAny = z.lazy(() => z.object({ key: z.string().trim().min(1).max(64), buttonName: z.string().trim().min(1).max(64), description: z.string().trim().max(300).nullable().optional(), previewImageUrl: z.string().trim().max(1000).nullable().optional(), enabled: z.boolean().optional(), sortOrder: z.number().min(0).max(999).optional(), promptFragment: z .string() .trim() .max(AGENT_WORKFLOW_PROMPT_FRAGMENT_MAX_LENGTH) .nullable() .optional(), blockedOptionKeys: z.array(z.string().trim().min(1).max(64)).max(100).optional(), children: z.array(agentWorkflowOptionSchema).max(100).optional(), }) ); const agentWorkflowStepSchema: z.ZodTypeAny = z.lazy(() => z.object({ key: z.string().trim().min(1).max(64), title: z.string().trim().min(1).max(64), description: z.string().trim().max(300).nullable().optional(), enabled: z.boolean().optional(), sortOrder: z.number().min(0).max(999).optional(), options: z.array(agentWorkflowOptionSchema).max(100), children: z.array(agentWorkflowStepSchema).max(100).optional(), }) ); const updateSchema = z.object({ siteName: z.string().min(1).max(128).optional(), siteLogo: z.string().nullable().optional(), siteLogoDark: z.string().nullable().optional(), favicon: z.string().nullable().optional(), showAutoModel: z.boolean().optional(), defaultTheme: z.enum(["system", "light", "dark"]).optional(), usageTimeEnabled: z.boolean().optional(), usageTimeStart: z.string().length(5).optional(), usageTimeEnd: z.string().length(5).optional(), usageTimeMessage: z.string().optional(), bannerEnabled: z.boolean().optional(), bannerText: z.string().optional(), bannerLink: z.string().nullable().optional(), toolbox1Name: z.string().max(64).optional(), toolbox1Enabled: z.boolean().optional(), toolbox2Name: z.string().max(64).optional(), toolbox2Enabled: z.boolean().optional(), tutorialVideoUrl: z.string().nullable().optional(), tutorialEnabled: z.boolean().optional(), rankingEnabled: z.boolean().optional(), productPlanningEnabled: z.boolean().optional(), canvasEnabled: z.boolean().optional(), agentEnabled: z.boolean().optional(), canvasAgentModeEnabled: z.boolean().optional(), videoCanvasEnabled: z.boolean().optional(), videoCanvasSidebarEnabled: z.boolean().optional(), agentPresetLibraryConfig: z .object({ sections: z .array( z.object({ id: z.string().trim().min(1).max(64).optional(), label: z.string().trim().min(1).max(64), description: z.string().trim().max(200).nullable().optional(), enabled: z.boolean().optional(), sortOrder: z.number().min(0).max(999).optional(), categories: z .array( z.lazy(() => z.object({ id: z.string().trim().min(1).max(64).optional(), name: z.string().trim().min(1).max(64), enabled: z.boolean().optional(), sortOrder: z.number().min(0).max(999).optional(), children: z .array( z.lazy(() => z.object({ id: z.string().trim().min(1).max(64).optional(), name: z.string().trim().min(1).max(64), enabled: z.boolean().optional(), sortOrder: z.number().min(0).max(999).optional(), children: z .array( z.object({ id: z.string().trim().min(1).max(64).optional(), name: z.string().trim().min(1).max(64), enabled: z.boolean().optional(), sortOrder: z.number().min(0).max(999).optional(), children: z.array(z.any()).optional(), }) ) .max(100) .optional(), }) ) ) .max(100) .optional(), }) ) ) .max(100) .optional(), }) ) .max(30), }) .optional(), agentCategoryTreeConfig: z .object({ productCategories: z.array(z.string().trim().min(1).max(64)).max(200).optional(), audienceTags: z.array(z.string().trim().min(1).max(64)).max(200).optional(), usageScenarios: z.array(z.string().trim().min(1).max(64)).max(200).optional(), brands: z.array(z.string().trim().min(1).max(64)).max(200).optional(), }) .optional(), agentSecondaryTypeConfig: z .object({ image: z.array(z.string().trim().min(1).max(64)).max(200).optional(), video: z.array(z.string().trim().min(1).max(64)).max(200).optional(), text: z.array(z.string().trim().min(1).max(64)).max(200).optional(), }) .optional(), agentWorkflowSettingsBaseSignature: z.string().trim().min(1).max(128).optional(), agentWorkflowSettingsBaseSignatures: z .record(z.string().trim().min(1).max(128)) .optional(), agentWorkflowUiConfig: z .object({ inputOptions: z .object({ brands: z .array(z.string().trim().min(1).max(64)) .max(AGENT_WORKFLOW_INPUT_OPTIONS_MAX_ITEMS) .optional(), stores: z .array(z.string().trim().min(1).max(64)) .max(AGENT_WORKFLOW_INPUT_OPTIONS_MAX_ITEMS) .optional(), categoryLabels: z .object({ firstCategory: z.string().trim().min(1).max(24).optional(), secondCategory: z.string().trim().min(1).max(24).optional(), thirdCategory: z.string().trim().min(1).max(24).optional(), fourthCategory: z.string().trim().min(1).max(24).optional(), fifthCategory: z.string().trim().min(1).max(24).optional(), }) .optional(), thirdCategories: z .array(z.string().trim().min(1).max(64)) .max(AGENT_WORKFLOW_INPUT_OPTIONS_MAX_ITEMS) .optional(), fourthCategories: z .array(z.string().trim().min(1).max(64)) .max(AGENT_WORKFLOW_INPUT_OPTIONS_MAX_ITEMS) .optional(), fifthCategories: z .array(z.string().trim().min(1).max(64)) .max(AGENT_WORKFLOW_INPUT_OPTIONS_MAX_ITEMS) .optional(), priceBands: z .array(z.string().trim().min(1).max(64)) .max(AGENT_WORKFLOW_INPUT_OPTIONS_MAX_ITEMS) .optional(), brandStoreEntries: z .array( z.object({ brandName: z.string().trim().min(1).max(64), stores: z .array(z.string().trim().min(1).max(64)) .max(AGENT_WORKFLOW_INPUT_OPTIONS_MAX_ITEMS) .optional(), storeEntries: z .array( z.object({ storeName: z.string().trim().min(1).max(64), thirdCategories: z .array(z.string().trim().min(1).max(64)) .max(AGENT_WORKFLOW_INPUT_OPTIONS_MAX_ITEMS) .optional(), thirdCategoryEntries: z .array( z.object({ categoryName: z.string().trim().min(1).max(64), fourthCategories: z .array(z.string().trim().min(1).max(64)) .max(AGENT_WORKFLOW_INPUT_OPTIONS_MAX_ITEMS) .optional(), fourthCategoryEntries: z .array( z.object({ categoryName: z.string().trim().min(1).max(64), fifthCategories: z .array(z.string().trim().min(1).max(64)) .max(AGENT_WORKFLOW_INPUT_OPTIONS_MAX_ITEMS) .optional(), sortOrder: z.number().min(0).max(999).optional(), }) ) .max(AGENT_WORKFLOW_INPUT_OPTIONS_MAX_ITEMS) .optional(), sortOrder: z.number().min(0).max(999).optional(), }) ) .max(AGENT_WORKFLOW_INPUT_OPTIONS_MAX_ITEMS) .optional(), sortOrder: z.number().min(0).max(999).optional(), }) ) .max(AGENT_WORKFLOW_INPUT_OPTIONS_MAX_ITEMS) .optional(), sortOrder: z.number().min(0).max(999).optional(), }) ) .max(AGENT_WORKFLOW_INPUT_OPTIONS_MAX_ITEMS) .optional(), }) .optional(), constraintSettings: z .object({ crossLevelMutualExclusionEnabled: z.boolean().optional(), }) .optional(), pageFeatureSettings: z .object({ presetStepEnabled: z.boolean().optional(), materialStepEnabled: z.boolean().optional(), psdTemplateEnabled: z.boolean().optional(), }) .optional(), steps: z .array(agentWorkflowStepSchema) .max(20), }) .optional(), agentWorkflowMaterialApiConfig: z .object({ enabled: z.boolean().optional(), baseUrl: z.string().trim().max(255).optional(), apiKey: z.string().trim().max(500).optional(), }) .optional(), agentWorkflowGoodsInfoApiConfig: z .object({ enabled: z.boolean().optional(), baseUrl: z.string().trim().max(255).optional(), clientKey: z.string().trim().max(255).optional(), secret: z.string().trim().max(255).optional(), }) .optional(), agentWorkflowBrandModelConfig: z .object({ entries: z .array( z.object({ brandName: z.string().trim().min(1).max(64), images: z .array( z.object({ id: z.string().trim().min(1).max(128).optional(), assetId: z.string().trim().max(128).nullable().optional(), imageUrl: z.string().trim().min(1).max(2000), displayName: z.string().trim().max(255).nullable().optional(), mimeType: z.string().trim().max(128).nullable().optional(), sortOrder: z.number().min(0).max(999).optional(), }) ) .max(100), }) ) .max(300), }) .optional(), agentWorkflowShowcaseConfig: z .object({ enabled: z.boolean().optional(), title: z.string().max(200).optional(), subtitle: z.string().max(300).optional(), actionText: z.string().max(48).optional(), videoUrl: z.string().max(2000).optional(), posterUrl: z.string().max(2000).nullable().optional(), overlayText: z.string().max(200).optional(), footerText: z.string().max(64).optional(), }) .optional(), agentPsdTextColorConfig: z .object({ enabled: z.boolean().optional(), colors: z.array(z.string().trim().max(32)).length(2).optional(), brandConfigs: z .array( z.object({ brandName: z.string().trim().min(1).max(64), enabled: z.boolean().optional(), colors: z.array(z.string().trim().max(32)).length(2).optional(), }) ) .max(300) .optional(), }) .optional(), agentPsdTemplateSecondaryCategoryConfig: z .object({ enabled: z.boolean().optional(), }) .optional(), agentPsdTemplateCategoryTagConfig: z .object({ tags: z.array(z.string().trim().min(1).max(64)).max(200).optional(), }) .optional(), realtimeCanvasEnabled: z.boolean().optional(), aiDetailGenerationEnabled: z.boolean().optional(), canvasDefaultModelId: z.string().nullable().optional(), canvasAutoModeDefaultModelId: z.string().nullable().optional(), canvasDetailDefaultModelId: z.string().nullable().optional(), canvasDetailAgentModelId: z.string().nullable().optional(), lovartPromoText: z.string().max(300).optional(), lovartPromoActionText: z.string().max(32).optional(), lovartHeroTitle: z.string().max(300).optional(), lovartHeroSubtitle: z.string().max(300).optional(), homeVariant: z.enum(["classic", "lovart"]).optional(), surpriseCategories: z.array(z.string().trim().min(1).max(20)).max(12).optional(), lovartComposerPrefix: z.string().trim().min(1).max(40).optional(), lovartComposerPreviewTexts: z.array(z.string().trim().min(1).max(120)).max(20).optional(), lovartPromptTagVisibility: z .object({ nanoBananaPro: z.boolean().optional(), design: z.boolean().optional(), branding: z.boolean().optional(), illustration: z.boolean().optional(), ecommerce: z.boolean().optional(), video: z.boolean().optional(), }) .optional(), contactConfig: z .object({ phone: z.string().max(64).optional(), wechatQrUrl: z.string().max(1000).optional(), }) .optional(), membershipConfig: z.unknown().optional(), membershipUserOverrides: z.unknown().optional(), pointsSystemConfig: z.unknown().optional(), pointsPackages: z.unknown().optional(), dulupayConfig: z.unknown().optional(), storageFailoverConfig: z .object({ enabled: z.boolean().optional(), backupStorageConfigId: z.string().max(128).optional(), }) .optional(), ossImageMigrationConfig: z .object({ enabled: z.boolean().optional(), preferAliyunForUploads: z.boolean().optional(), sourceStorageConfigId: z.string().max(128).optional(), targetStorageConfigId: z.string().max(128).optional(), migrateAfterHours: z.number().min(1).max(720).optional(), cleanupAfterHours: z.number().min(1).max(1440).optional(), batchSize: z.number().min(1).max(100).optional(), }) .optional(), homeAnimationConfig: z .object({ enabled: z.boolean().optional(), fileName: z.string().trim().max(255).optional(), htmlContent: z.string().max(2_000_000).optional(), updatedAt: z.string().nullable().optional(), }) .optional(), }); // PUT /api/site-settings - 更新网站设置(管理员 / 风格化管理员) export async function PUT(request: NextRequest) { try { const session = await auth(); if (!session?.user) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } const isAdmin = isSiteAdminType(session.user.type); const canManageAgentWorkflow = isAgentWorkflowAdminType(session.user.type); if (!canManageAgentWorkflow) { return NextResponse.json({ error: "Forbidden" }, { status: 403 }); } const body = await request.json(); if (!isAdmin) { const allowedKeys = new Set([ "agentPresetLibraryConfig", "agentWorkflowSettingsBaseSignature", "agentWorkflowSettingsBaseSignatures", "agentWorkflowUiConfig", "agentWorkflowMaterialApiConfig", "agentWorkflowGoodsInfoApiConfig", "agentWorkflowBrandModelConfig", "agentWorkflowShowcaseConfig", "agentPsdTextColorConfig", "agentPsdTemplateSecondaryCategoryConfig", "agentPsdTemplateCategoryTagConfig", ]); const hasRestrictedKeys = Object.keys(body || {}).some((key) => !allowedKeys.has(key)); if (hasRestrictedKeys) { return NextResponse.json({ error: "Forbidden" }, { status: 403 }); } } const data = updateSchema.parse(body); const { homeVariant, surpriseCategories, aiDetailGenerationEnabled, lovartComposerPrefix, lovartComposerPreviewTexts, lovartPromptTagVisibility, contactConfig, agentPresetLibraryConfig, agentCategoryTreeConfig, agentSecondaryTypeConfig, agentWorkflowSettingsBaseSignature, agentWorkflowSettingsBaseSignatures, agentWorkflowUiConfig, agentWorkflowMaterialApiConfig, agentWorkflowGoodsInfoApiConfig, agentWorkflowBrandModelConfig, agentWorkflowShowcaseConfig, agentPsdTextColorConfig, agentPsdTemplateSecondaryCategoryConfig, agentPsdTemplateCategoryTagConfig, membershipConfig, membershipUserOverrides, pointsSystemConfig, pointsPackages, dulupayConfig, storageFailoverConfig, ossImageMigrationConfig, canvasAgentModeEnabled, videoCanvasEnabled, videoCanvasSidebarEnabled, homeAnimationConfig, ...siteSettingsPayload } = data; const updatedAgentWorkflowFields = ( [ ["agentPresetLibraryConfig", agentPresetLibraryConfig], ["agentWorkflowUiConfig", agentWorkflowUiConfig], ["agentWorkflowMaterialApiConfig", agentWorkflowMaterialApiConfig], ["agentWorkflowGoodsInfoApiConfig", agentWorkflowGoodsInfoApiConfig], ["agentWorkflowBrandModelConfig", agentWorkflowBrandModelConfig], ["agentWorkflowShowcaseConfig", agentWorkflowShowcaseConfig], ["agentPsdTextColorConfig", agentPsdTextColorConfig], ["agentPsdTemplateSecondaryCategoryConfig", agentPsdTemplateSecondaryCategoryConfig], ] as const ) .filter(([, value]) => value !== undefined) .map(([field]) => field); const isUpdatingAgentWorkflowSettings = updatedAgentWorkflowFields.length > 0; if (isUpdatingAgentWorkflowSettings) { if (!agentWorkflowSettingsBaseSignatures && !agentWorkflowSettingsBaseSignature) { return NextResponse.json( { error: "工作流配置已启用多人协作保护,请刷新页面后再保存,避免覆盖其他管理员的数据。", }, { status: 409 } ); } const currentWorkflowRows = await db .select({ key: systemSettings.key, value: systemSettings.value, }) .from(systemSettings) .where(inArray(systemSettings.key, [...AGENT_WORKFLOW_SETTINGS_CONFLICT_KEYS])); const currentWorkflowValues = new Map( currentWorkflowRows.map((row) => [row.key, row.value]) ); const currentSectionSignatures = createAgentWorkflowSettingsSectionSignatures(currentWorkflowValues); const currentWorkflowSignature = agentWorkflowSettingsBaseSignature && !agentWorkflowSettingsBaseSignatures ? createAgentWorkflowSettingsSignature(currentWorkflowValues) : null; const hasWorkflowConflict = agentWorkflowSettingsBaseSignatures ? updatedAgentWorkflowFields.some( (field) => currentSectionSignatures[field] !== agentWorkflowSettingsBaseSignatures[field] ) : currentWorkflowSignature !== agentWorkflowSettingsBaseSignature; if (hasWorkflowConflict) { return NextResponse.json( { error: "服务器上的同一块工作流配置已被其他管理员更新,本次保存已阻止。请刷新页面确认最新配置后再保存。", }, { status: 409 } ); } } const persistSystemSettingWithHistory = async ( key: string, value: unknown, reason: string ) => { const [existingSetting] = await db .select({ value: systemSettings.value }) .from(systemSettings) .where(eq(systemSettings.key, key)) .limit(1); await db.insert(systemSettingHistory).values({ key, previousValue: existingSetting?.value ?? null, nextValue: value, reason, changedAt: new Date(), changedBy: session.user.id, }); await db .insert(systemSettings) .values({ key, value, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value, updatedAt: new Date(), updatedBy: session.user.id, }, }); }; const persistHomeAnimationConfig = async () => { if (homeAnimationConfig === undefined) { return; } const normalized = normalizeHomeChatAnimationConfig(homeAnimationConfig); const htmlBytes = getHomeChatAnimationHtmlBytes(normalized.htmlContent); if (htmlBytes > HOME_CHAT_ANIMATION_MAX_BYTES) { throw new Error( `首页动画 HTML 超过限制,当前 ${(htmlBytes / 1024).toFixed(1)}KB,最大 ${( HOME_CHAT_ANIMATION_MAX_BYTES / 1024 ).toFixed(0)}KB`, ); } await db .insert(systemSettings) .values({ key: HOME_CHAT_ANIMATION_CONFIG_KEY, value: { ...normalized, updatedAt: new Date().toISOString(), }, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: { ...normalized, updatedAt: new Date().toISOString(), }, updatedAt: new Date(), updatedBy: session.user.id, }, }); }; // 查找现有设置 const [existing] = await db.select().from(siteSettings).limit(1); if (existing) { // 更新现有设置 const [updated] = await db .update(siteSettings) .set({ ...siteSettingsPayload, agentEnabled: siteSettingsPayload.agentEnabled ?? existing.agentEnabled ?? true, updatedAt: new Date(), updatedBy: session.user.id, }) .where(eq(siteSettings.id, existing.id)) .returning(); if (homeVariant !== undefined) { await db .insert(systemSettings) .values({ key: HOME_VARIANT_KEY, value: homeVariant, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: homeVariant, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (surpriseCategories !== undefined) { const normalized = normalizeSurpriseCategories(surpriseCategories); await db .insert(systemSettings) .values({ key: HOME_SURPRISE_CATEGORIES_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (aiDetailGenerationEnabled !== undefined) { await db .insert(systemSettings) .values({ key: AI_DETAIL_GENERATOR_ENABLED_KEY, value: Boolean(aiDetailGenerationEnabled), updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: Boolean(aiDetailGenerationEnabled), updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (lovartComposerPreviewTexts !== undefined) { const normalized = normalizeLovartComposerPreviewTexts(lovartComposerPreviewTexts); await db .insert(systemSettings) .values({ key: LOVART_COMPOSER_PREVIEW_TEXTS_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (lovartComposerPrefix !== undefined) { const normalized = normalizeLovartComposerPrefix(lovartComposerPrefix); await db .insert(systemSettings) .values({ key: LOVART_COMPOSER_PREFIX_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (lovartPromptTagVisibility !== undefined) { const normalized = normalizeLovartPromptTagVisibility(lovartPromptTagVisibility); await db .insert(systemSettings) .values({ key: LOVART_PROMPT_TAG_VISIBILITY_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (contactConfig !== undefined) { const normalized = normalizeContactConfig(contactConfig); await db .insert(systemSettings) .values({ key: CONTACT_CONFIG_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (canvasAgentModeEnabled !== undefined) { const normalized = normalizeCanvasAgentModeEnabled(canvasAgentModeEnabled); await db .insert(systemSettings) .values({ key: CANVAS_AGENT_MODE_ENABLED_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (videoCanvasEnabled !== undefined) { const normalized = normalizeVideoCanvasEnabled(videoCanvasEnabled); await db .insert(systemSettings) .values({ key: VIDEO_CANVAS_ENABLED_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (videoCanvasSidebarEnabled !== undefined) { const normalized = normalizeVideoCanvasSidebarEnabled(videoCanvasSidebarEnabled); await db .insert(systemSettings) .values({ key: VIDEO_CANVAS_SIDEBAR_ENABLED_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (agentPresetLibraryConfig !== undefined) { const normalized = normalizeAgentPresetLibraryConfig(agentPresetLibraryConfig); await persistSystemSettingWithHistory( AGENT_PRESET_LIBRARY_CONFIG_KEY, normalized, "agent-workflow" ); } if (agentCategoryTreeConfig !== undefined) { const normalized = normalizeAgentCategoryTreeConfig(agentCategoryTreeConfig); await db .insert(systemSettings) .values({ key: AGENT_CATEGORY_TREE_CONFIG_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (agentSecondaryTypeConfig !== undefined) { const normalized = normalizeAgentSecondaryTypeConfig(agentSecondaryTypeConfig); await db .insert(systemSettings) .values({ key: AGENT_SECONDARY_TYPE_CONFIG_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (agentWorkflowUiConfig !== undefined) { const normalized = normalizeAgentWorkflowUiConfig(agentWorkflowUiConfig); await persistSystemSettingWithHistory( AGENT_WORKFLOW_UI_CONFIG_KEY, normalized, "agent-workflow" ); } if (agentWorkflowMaterialApiConfig !== undefined) { const normalized = normalizeAgentWorkflowMaterialApiConfig( agentWorkflowMaterialApiConfig ); await persistSystemSettingWithHistory( AGENT_WORKFLOW_MATERIAL_API_CONFIG_KEY, normalized, "agent-workflow" ); } if (agentWorkflowGoodsInfoApiConfig !== undefined) { const normalized = normalizeAgentWorkflowGoodsInfoApiConfig( agentWorkflowGoodsInfoApiConfig ); await persistSystemSettingWithHistory( AGENT_WORKFLOW_GOODS_INFO_API_CONFIG_KEY, normalized, "agent-workflow" ); } if (agentWorkflowBrandModelConfig !== undefined) { const normalized = normalizeAgentWorkflowBrandModelConfig( agentWorkflowBrandModelConfig ); await persistSystemSettingWithHistory( AGENT_WORKFLOW_BRAND_MODEL_CONFIG_KEY, normalized, "agent-workflow" ); } if (agentWorkflowShowcaseConfig !== undefined) { const normalized = normalizeAgentWorkflowShowcaseConfig(agentWorkflowShowcaseConfig); await persistSystemSettingWithHistory( AGENT_WORKFLOW_SHOWCASE_CONFIG_KEY, normalized, "agent-workflow" ); } if (agentPsdTextColorConfig !== undefined) { const normalized = normalizeAgentPsdTextColorConfig(agentPsdTextColorConfig); await persistSystemSettingWithHistory( AGENT_PSD_TEXT_COLOR_CONFIG_KEY, normalized, "agent-workflow" ); } if (agentPsdTemplateSecondaryCategoryConfig !== undefined) { const normalized = normalizeAgentPsdTemplateSecondaryCategoryConfig( agentPsdTemplateSecondaryCategoryConfig ); await persistSystemSettingWithHistory( AGENT_PSD_TEMPLATE_SECONDARY_CATEGORY_CONFIG_KEY, normalized, "agent-workflow" ); } if (agentPsdTemplateCategoryTagConfig !== undefined) { const normalized = normalizeAgentPsdTemplateCategoryTagConfig( agentPsdTemplateCategoryTagConfig ); await db .insert(systemSettings) .values({ key: AGENT_PSD_TEMPLATE_CATEGORY_TAG_CONFIG_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (membershipConfig !== undefined) { const normalized = normalizeMembershipConfig(membershipConfig); await db .insert(systemSettings) .values({ key: MEMBERSHIP_CONFIG_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (membershipUserOverrides !== undefined) { const normalized = normalizeMembershipUserOverrides(membershipUserOverrides); await db .insert(systemSettings) .values({ key: MEMBERSHIP_USER_OVERRIDES_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (pointsSystemConfig !== undefined) { const normalized = normalizePointsSystemConfig(pointsSystemConfig); await db .insert(systemSettings) .values({ key: POINTS_SYSTEM_CONFIG_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (pointsPackages !== undefined) { const normalized = normalizePointsPackages(pointsPackages); await db .insert(systemSettings) .values({ key: POINTS_PACKAGES_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (dulupayConfig !== undefined) { const normalized = normalizeDulupayConfig(dulupayConfig); await db .insert(systemSettings) .values({ key: DULUPAY_CONFIG_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (storageFailoverConfig !== undefined) { const normalized = normalizeStorageFailoverConfig(storageFailoverConfig); await db .insert(systemSettings) .values({ key: STORAGE_FAILOVER_CONFIG_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (ossImageMigrationConfig !== undefined) { const normalized = normalizeOssImageMigrationConfig(ossImageMigrationConfig); await db .insert(systemSettings) .values({ key: OSS_MIGRATION_CONFIG_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } await persistHomeAnimationConfig(); return NextResponse.json({ ...updated, homeVariant: homeVariant ?? normalizeHomeVariant(undefined), surpriseCategories: surpriseCategories !== undefined ? normalizeSurpriseCategories(surpriseCategories) : normalizeSurpriseCategories(undefined), aiDetailGenerationEnabled: aiDetailGenerationEnabled ?? false, lovartComposerPrefix: lovartComposerPrefix !== undefined ? normalizeLovartComposerPrefix(lovartComposerPrefix) : normalizeLovartComposerPrefix(undefined), lovartComposerPreviewTexts: lovartComposerPreviewTexts !== undefined ? normalizeLovartComposerPreviewTexts(lovartComposerPreviewTexts) : normalizeLovartComposerPreviewTexts(undefined), lovartPromptTagVisibility: lovartPromptTagVisibility !== undefined ? normalizeLovartPromptTagVisibility(lovartPromptTagVisibility) : normalizeLovartPromptTagVisibility(undefined), contactConfig: contactConfig !== undefined ? normalizeContactConfig(contactConfig) : normalizeContactConfig(undefined), agentPresetLibraryConfig: agentPresetLibraryConfig !== undefined ? normalizeAgentPresetLibraryConfig(agentPresetLibraryConfig) : defaultAgentPresetLibraryConfig, agentCategoryTreeConfig: agentCategoryTreeConfig !== undefined ? normalizeAgentCategoryTreeConfig(agentCategoryTreeConfig) : normalizeAgentCategoryTreeConfig(undefined), agentSecondaryTypeConfig: agentSecondaryTypeConfig !== undefined ? normalizeAgentSecondaryTypeConfig(agentSecondaryTypeConfig) : defaultAgentSecondaryTypeConfig, agentWorkflowUiConfig: agentWorkflowUiConfig !== undefined ? normalizeAgentWorkflowUiConfig(agentWorkflowUiConfig) : defaultAgentWorkflowUiConfig, agentWorkflowMaterialApiConfig: agentWorkflowMaterialApiConfig !== undefined ? normalizeAgentWorkflowMaterialApiConfig(agentWorkflowMaterialApiConfig) : defaultAgentWorkflowMaterialApiConfig, agentWorkflowGoodsInfoApiConfig: agentWorkflowGoodsInfoApiConfig !== undefined ? normalizeAgentWorkflowGoodsInfoApiConfig(agentWorkflowGoodsInfoApiConfig) : defaultAgentWorkflowGoodsInfoApiConfig, agentWorkflowBrandModelConfig: agentWorkflowBrandModelConfig !== undefined ? normalizeAgentWorkflowBrandModelConfig(agentWorkflowBrandModelConfig) : defaultAgentWorkflowBrandModelConfig, agentWorkflowShowcaseConfig: agentWorkflowShowcaseConfig !== undefined ? normalizeAgentWorkflowShowcaseConfig(agentWorkflowShowcaseConfig) : defaultAgentWorkflowShowcaseConfig, agentPsdTextColorConfig: agentPsdTextColorConfig !== undefined ? normalizeAgentPsdTextColorConfig(agentPsdTextColorConfig) : defaultAgentPsdTextColorConfig, agentPsdTemplateSecondaryCategoryConfig: agentPsdTemplateSecondaryCategoryConfig !== undefined ? normalizeAgentPsdTemplateSecondaryCategoryConfig( agentPsdTemplateSecondaryCategoryConfig ) : defaultAgentPsdTemplateSecondaryCategoryConfig, agentPsdTemplateCategoryTagConfig: agentPsdTemplateCategoryTagConfig !== undefined ? normalizeAgentPsdTemplateCategoryTagConfig(agentPsdTemplateCategoryTagConfig) : defaultAgentPsdTemplateCategoryTagConfig, membershipConfig: membershipConfig !== undefined ? normalizeMembershipConfig(membershipConfig) : defaultMembershipConfig, membershipUserOverrides: membershipUserOverrides !== undefined ? normalizeMembershipUserOverrides(membershipUserOverrides) : defaultMembershipUserOverrides, pointsSystemConfig: pointsSystemConfig !== undefined ? normalizePointsSystemConfig(pointsSystemConfig) : defaultPointsSystemConfig, pointsPackages: pointsPackages !== undefined ? normalizePointsPackages(pointsPackages) : defaultPointsPackages, dulupayConfig: dulupayConfig !== undefined ? normalizeDulupayConfig(dulupayConfig) : defaultDulupayConfig, storageFailoverConfig: storageFailoverConfig !== undefined ? normalizeStorageFailoverConfig(storageFailoverConfig) : normalizeStorageFailoverConfig(undefined), ossImageMigrationConfig: ossImageMigrationConfig !== undefined ? normalizeOssImageMigrationConfig(ossImageMigrationConfig) : defaultOssImageMigrationConfig, canvasAgentModeEnabled: canvasAgentModeEnabled !== undefined ? normalizeCanvasAgentModeEnabled(canvasAgentModeEnabled) : normalizeCanvasAgentModeEnabled(undefined), videoCanvasEnabled: videoCanvasEnabled !== undefined ? normalizeVideoCanvasEnabled(videoCanvasEnabled) : normalizeVideoCanvasEnabled(undefined, videoCanvasSidebarEnabled), videoCanvasSidebarEnabled: videoCanvasSidebarEnabled !== undefined ? normalizeVideoCanvasSidebarEnabled(videoCanvasSidebarEnabled) : normalizeVideoCanvasSidebarEnabled(undefined), }); } else { // 创建新设置 const [created] = await db .insert(siteSettings) .values({ siteName: siteSettingsPayload.siteName || "AI Chatbot", siteLogo: siteSettingsPayload.siteLogo, siteLogoDark: siteSettingsPayload.siteLogoDark, favicon: siteSettingsPayload.favicon, showAutoModel: siteSettingsPayload.showAutoModel ?? true, defaultTheme: siteSettingsPayload.defaultTheme ?? "system", usageTimeEnabled: siteSettingsPayload.usageTimeEnabled ?? false, usageTimeStart: siteSettingsPayload.usageTimeStart ?? "08:00", usageTimeEnd: siteSettingsPayload.usageTimeEnd ?? "20:00", usageTimeMessage: siteSettingsPayload.usageTimeMessage ?? "由壹点累了也是需要休息的", bannerEnabled: siteSettingsPayload.bannerEnabled ?? true, bannerText: siteSettingsPayload.bannerText ?? "升级会员, Nano Banana Pro 免费365天!", bannerLink: siteSettingsPayload.bannerLink, toolbox1Name: siteSettingsPayload.toolbox1Name ?? "工具箱", toolbox1Enabled: siteSettingsPayload.toolbox1Enabled ?? true, toolbox2Name: siteSettingsPayload.toolbox2Name ?? "工具箱2", toolbox2Enabled: siteSettingsPayload.toolbox2Enabled ?? false, tutorialVideoUrl: siteSettingsPayload.tutorialVideoUrl, tutorialEnabled: siteSettingsPayload.tutorialEnabled ?? false, rankingEnabled: siteSettingsPayload.rankingEnabled ?? true, productPlanningEnabled: siteSettingsPayload.productPlanningEnabled ?? true, canvasEnabled: siteSettingsPayload.canvasEnabled ?? true, agentEnabled: siteSettingsPayload.agentEnabled ?? true, realtimeCanvasEnabled: siteSettingsPayload.realtimeCanvasEnabled ?? true, canvasDefaultModelId: siteSettingsPayload.canvasDefaultModelId, canvasAutoModeDefaultModelId: siteSettingsPayload.canvasAutoModeDefaultModelId, canvasDetailDefaultModelId: siteSettingsPayload.canvasDetailDefaultModelId, canvasDetailAgentModelId: siteSettingsPayload.canvasDetailAgentModelId, lovartPromoText: siteSettingsPayload.lovartPromoText ?? "现在升级,立享 Kling 3.0 与 Nano Banana Pro,最高 5 折优惠", lovartPromoActionText: siteSettingsPayload.lovartPromoActionText ?? "立即升级", lovartHeroTitle: siteSettingsPayload.lovartHeroTitle ?? "由壹点AI 让设计更简单", lovartHeroSubtitle: siteSettingsPayload.lovartHeroSubtitle ?? "懂你的设计代理,帮你搞定一切", updatedBy: session.user.id, }) .returning(); if (homeVariant !== undefined) { await db .insert(systemSettings) .values({ key: HOME_VARIANT_KEY, value: homeVariant, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: homeVariant, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (surpriseCategories !== undefined) { const normalized = normalizeSurpriseCategories(surpriseCategories); await db .insert(systemSettings) .values({ key: HOME_SURPRISE_CATEGORIES_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } await persistHomeAnimationConfig(); if (aiDetailGenerationEnabled !== undefined) { await db .insert(systemSettings) .values({ key: AI_DETAIL_GENERATOR_ENABLED_KEY, value: Boolean(aiDetailGenerationEnabled), updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: Boolean(aiDetailGenerationEnabled), updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (lovartComposerPreviewTexts !== undefined) { const normalized = normalizeLovartComposerPreviewTexts(lovartComposerPreviewTexts); await db .insert(systemSettings) .values({ key: LOVART_COMPOSER_PREVIEW_TEXTS_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (lovartComposerPrefix !== undefined) { const normalized = normalizeLovartComposerPrefix(lovartComposerPrefix); await db .insert(systemSettings) .values({ key: LOVART_COMPOSER_PREFIX_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (lovartPromptTagVisibility !== undefined) { const normalized = normalizeLovartPromptTagVisibility(lovartPromptTagVisibility); await db .insert(systemSettings) .values({ key: LOVART_PROMPT_TAG_VISIBILITY_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (contactConfig !== undefined) { const normalized = normalizeContactConfig(contactConfig); await db .insert(systemSettings) .values({ key: CONTACT_CONFIG_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (canvasAgentModeEnabled !== undefined) { const normalized = normalizeCanvasAgentModeEnabled(canvasAgentModeEnabled); await db .insert(systemSettings) .values({ key: CANVAS_AGENT_MODE_ENABLED_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (videoCanvasSidebarEnabled !== undefined) { const normalized = normalizeVideoCanvasSidebarEnabled(videoCanvasSidebarEnabled); await db .insert(systemSettings) .values({ key: VIDEO_CANVAS_SIDEBAR_ENABLED_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (agentPresetLibraryConfig !== undefined) { const normalized = normalizeAgentPresetLibraryConfig(agentPresetLibraryConfig); await persistSystemSettingWithHistory( AGENT_PRESET_LIBRARY_CONFIG_KEY, normalized, "agent-workflow" ); } if (agentCategoryTreeConfig !== undefined) { const normalized = normalizeAgentCategoryTreeConfig(agentCategoryTreeConfig); await db .insert(systemSettings) .values({ key: AGENT_CATEGORY_TREE_CONFIG_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (agentSecondaryTypeConfig !== undefined) { const normalized = normalizeAgentSecondaryTypeConfig(agentSecondaryTypeConfig); await db .insert(systemSettings) .values({ key: AGENT_SECONDARY_TYPE_CONFIG_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (agentWorkflowUiConfig !== undefined) { const normalized = normalizeAgentWorkflowUiConfig(agentWorkflowUiConfig); await persistSystemSettingWithHistory( AGENT_WORKFLOW_UI_CONFIG_KEY, normalized, "agent-workflow" ); } if (agentWorkflowMaterialApiConfig !== undefined) { const normalized = normalizeAgentWorkflowMaterialApiConfig( agentWorkflowMaterialApiConfig ); await persistSystemSettingWithHistory( AGENT_WORKFLOW_MATERIAL_API_CONFIG_KEY, normalized, "agent-workflow" ); } if (agentWorkflowBrandModelConfig !== undefined) { const normalized = normalizeAgentWorkflowBrandModelConfig( agentWorkflowBrandModelConfig ); await persistSystemSettingWithHistory( AGENT_WORKFLOW_BRAND_MODEL_CONFIG_KEY, normalized, "agent-workflow" ); } if (agentWorkflowShowcaseConfig !== undefined) { const normalized = normalizeAgentWorkflowShowcaseConfig(agentWorkflowShowcaseConfig); await persistSystemSettingWithHistory( AGENT_WORKFLOW_SHOWCASE_CONFIG_KEY, normalized, "agent-workflow" ); } if (agentPsdTextColorConfig !== undefined) { const normalized = normalizeAgentPsdTextColorConfig(agentPsdTextColorConfig); await persistSystemSettingWithHistory( AGENT_PSD_TEXT_COLOR_CONFIG_KEY, normalized, "agent-workflow" ); } if (agentPsdTemplateCategoryTagConfig !== undefined) { const normalized = normalizeAgentPsdTemplateCategoryTagConfig( agentPsdTemplateCategoryTagConfig ); await db .insert(systemSettings) .values({ key: AGENT_PSD_TEMPLATE_CATEGORY_TAG_CONFIG_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (membershipConfig !== undefined) { const normalized = normalizeMembershipConfig(membershipConfig); await db .insert(systemSettings) .values({ key: MEMBERSHIP_CONFIG_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (membershipUserOverrides !== undefined) { const normalized = normalizeMembershipUserOverrides(membershipUserOverrides); await db .insert(systemSettings) .values({ key: MEMBERSHIP_USER_OVERRIDES_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (pointsSystemConfig !== undefined) { const normalized = normalizePointsSystemConfig(pointsSystemConfig); await db .insert(systemSettings) .values({ key: POINTS_SYSTEM_CONFIG_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (pointsPackages !== undefined) { const normalized = normalizePointsPackages(pointsPackages); await db .insert(systemSettings) .values({ key: POINTS_PACKAGES_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (dulupayConfig !== undefined) { const normalized = normalizeDulupayConfig(dulupayConfig); await db .insert(systemSettings) .values({ key: DULUPAY_CONFIG_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (storageFailoverConfig !== undefined) { const normalized = normalizeStorageFailoverConfig(storageFailoverConfig); await db .insert(systemSettings) .values({ key: STORAGE_FAILOVER_CONFIG_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (ossImageMigrationConfig !== undefined) { const normalized = normalizeOssImageMigrationConfig(ossImageMigrationConfig); await db .insert(systemSettings) .values({ key: OSS_MIGRATION_CONFIG_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } if (videoCanvasEnabled !== undefined) { const normalized = normalizeVideoCanvasEnabled(videoCanvasEnabled); await db .insert(systemSettings) .values({ key: VIDEO_CANVAS_ENABLED_KEY, value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: normalized, updatedAt: new Date(), updatedBy: session.user.id, }, }); } return NextResponse.json({ ...created, homeVariant: homeVariant ?? normalizeHomeVariant(undefined), surpriseCategories: surpriseCategories !== undefined ? normalizeSurpriseCategories(surpriseCategories) : normalizeSurpriseCategories(undefined), aiDetailGenerationEnabled: aiDetailGenerationEnabled ?? false, lovartComposerPrefix: lovartComposerPrefix !== undefined ? normalizeLovartComposerPrefix(lovartComposerPrefix) : normalizeLovartComposerPrefix(undefined), lovartComposerPreviewTexts: lovartComposerPreviewTexts !== undefined ? normalizeLovartComposerPreviewTexts(lovartComposerPreviewTexts) : normalizeLovartComposerPreviewTexts(undefined), lovartPromptTagVisibility: lovartPromptTagVisibility !== undefined ? normalizeLovartPromptTagVisibility(lovartPromptTagVisibility) : normalizeLovartPromptTagVisibility(undefined), contactConfig: contactConfig !== undefined ? normalizeContactConfig(contactConfig) : normalizeContactConfig(undefined), agentPresetLibraryConfig: agentPresetLibraryConfig !== undefined ? normalizeAgentPresetLibraryConfig(agentPresetLibraryConfig) : defaultAgentPresetLibraryConfig, agentCategoryTreeConfig: agentCategoryTreeConfig !== undefined ? normalizeAgentCategoryTreeConfig(agentCategoryTreeConfig) : normalizeAgentCategoryTreeConfig(undefined), agentSecondaryTypeConfig: agentSecondaryTypeConfig !== undefined ? normalizeAgentSecondaryTypeConfig(agentSecondaryTypeConfig) : defaultAgentSecondaryTypeConfig, agentWorkflowUiConfig: agentWorkflowUiConfig !== undefined ? normalizeAgentWorkflowUiConfig(agentWorkflowUiConfig) : defaultAgentWorkflowUiConfig, agentWorkflowMaterialApiConfig: agentWorkflowMaterialApiConfig !== undefined ? normalizeAgentWorkflowMaterialApiConfig(agentWorkflowMaterialApiConfig) : defaultAgentWorkflowMaterialApiConfig, agentWorkflowGoodsInfoApiConfig: agentWorkflowGoodsInfoApiConfig !== undefined ? normalizeAgentWorkflowGoodsInfoApiConfig(agentWorkflowGoodsInfoApiConfig) : defaultAgentWorkflowGoodsInfoApiConfig, agentWorkflowBrandModelConfig: agentWorkflowBrandModelConfig !== undefined ? normalizeAgentWorkflowBrandModelConfig(agentWorkflowBrandModelConfig) : defaultAgentWorkflowBrandModelConfig, agentWorkflowShowcaseConfig: agentWorkflowShowcaseConfig !== undefined ? normalizeAgentWorkflowShowcaseConfig(agentWorkflowShowcaseConfig) : defaultAgentWorkflowShowcaseConfig, agentPsdTextColorConfig: agentPsdTextColorConfig !== undefined ? normalizeAgentPsdTextColorConfig(agentPsdTextColorConfig) : defaultAgentPsdTextColorConfig, agentPsdTemplateSecondaryCategoryConfig: agentPsdTemplateSecondaryCategoryConfig !== undefined ? normalizeAgentPsdTemplateSecondaryCategoryConfig( agentPsdTemplateSecondaryCategoryConfig ) : defaultAgentPsdTemplateSecondaryCategoryConfig, agentPsdTemplateCategoryTagConfig: agentPsdTemplateCategoryTagConfig !== undefined ? normalizeAgentPsdTemplateCategoryTagConfig(agentPsdTemplateCategoryTagConfig) : defaultAgentPsdTemplateCategoryTagConfig, membershipConfig: membershipConfig !== undefined ? normalizeMembershipConfig(membershipConfig) : defaultMembershipConfig, membershipUserOverrides: membershipUserOverrides !== undefined ? normalizeMembershipUserOverrides(membershipUserOverrides) : defaultMembershipUserOverrides, pointsSystemConfig: pointsSystemConfig !== undefined ? normalizePointsSystemConfig(pointsSystemConfig) : defaultPointsSystemConfig, pointsPackages: pointsPackages !== undefined ? normalizePointsPackages(pointsPackages) : defaultPointsPackages, dulupayConfig: dulupayConfig !== undefined ? normalizeDulupayConfig(dulupayConfig) : defaultDulupayConfig, storageFailoverConfig: storageFailoverConfig !== undefined ? normalizeStorageFailoverConfig(storageFailoverConfig) : normalizeStorageFailoverConfig(undefined), ossImageMigrationConfig: ossImageMigrationConfig !== undefined ? normalizeOssImageMigrationConfig(ossImageMigrationConfig) : defaultOssImageMigrationConfig, canvasAgentModeEnabled: canvasAgentModeEnabled !== undefined ? normalizeCanvasAgentModeEnabled(canvasAgentModeEnabled) : normalizeCanvasAgentModeEnabled(undefined), videoCanvasEnabled: videoCanvasEnabled !== undefined ? normalizeVideoCanvasEnabled(videoCanvasEnabled) : normalizeVideoCanvasEnabled(undefined, videoCanvasSidebarEnabled), videoCanvasSidebarEnabled: videoCanvasSidebarEnabled !== undefined ? normalizeVideoCanvasSidebarEnabled(videoCanvasSidebarEnabled) : normalizeVideoCanvasSidebarEnabled(undefined), }); } } catch (error) { console.error("[Site Settings API] Update error:", error); if (error instanceof z.ZodError) { return NextResponse.json( { error: "Invalid request data", details: error.errors }, { status: 400 } ); } if (error instanceof Error) { return NextResponse.json( { error: error.message || "Failed to update site settings" }, { status: 500 } ); } return NextResponse.json( { error: "Failed to update site settings" }, { status: 500 } ); } }