import { auth } from "@/app/(auth)/auth"; import { getSystemSetting } from "@/lib/db/queries"; import { DEFAULT_BFL_BASE_URL, RealtimeCanvasConfig, resolveRealtimeProviders, } from "@/lib/realtime/config"; const defaultConfig = { session: { idleTimeoutMs: 120000, }, transport: { throttleInterval: 100, maxBuffering: 2, }, image: { maxInputResolution: 1024, jpegQuality: 0.55, }, model: { id: "flux-2-klein-4b", defaults: { steps: 3, cfgScale: 7, strength: 0.32, imageSize: "square" as const, }, limits: { steps: { min: 1, max: 6 }, cfgScale: { min: 1, max: 14 }, strength: { min: 0.05, max: 1.0 }, }, }, features: { history: true, abCompare: false, }, }; function clamp(value: number, min: number, max: number) { return Math.min(max, Math.max(min, value)); } export async function GET() { const session = await auth(); if (!session?.user?.id) { return Response.json({ code: "UNAUTHORIZED", message: "请先登录" }, { status: 401 }); } const setting = await getSystemSetting({ key: "realtime_canvas_config" }).catch(() => null); const config = (setting?.value || {}) as RealtimeCanvasConfig; if (config.enabled === false) { return Response.json( { code: "FEATURE_DISABLED", message: "实时画布功能未开启" }, { status: 403 } ); } const providers = resolveRealtimeProviders(config); const activeProvider = providers[0]; if (!activeProvider) { return Response.json( { code: "BFL_KEY_MISSING", message: "未配置可用的实时画布服务(请在服务器环境变量或设置中配置)", }, { status: 500 } ); } const throttleInterval = clamp( Number(config.throttleInterval || defaultConfig.transport.throttleInterval), 60, 260 ); const maxBuffering = clamp( Number(config.maxBuffering || defaultConfig.transport.maxBuffering), 1, 6 ); const maxInputResolution = clamp( Number(config.maxInputResolution || defaultConfig.image.maxInputResolution), 512, 1536 ); const jpegQuality = clamp( Number(config.jpegQuality || defaultConfig.image.jpegQuality), 0.35, 0.82 ); const steps = clamp( Number(config.defaults?.steps || defaultConfig.model.defaults.steps), defaultConfig.model.limits.steps.min, defaultConfig.model.limits.steps.max ); const legacyScheduleMu = Number(config.defaults?.scheduleMu); const cfgScaleBase = Number( config.defaults?.cfgScale ?? (Number.isFinite(legacyScheduleMu) ? legacyScheduleMu * 2.5 : defaultConfig.model.defaults.cfgScale) ); const cfgScale = clamp( cfgScaleBase, defaultConfig.model.limits.cfgScale.min, defaultConfig.model.limits.cfgScale.max ); const strength = clamp( Number(config.defaults?.strength ?? defaultConfig.model.defaults.strength), defaultConfig.model.limits.strength.min, defaultConfig.model.limits.strength.max ); const imageSize = config.defaults?.imageSize === "square_hd" ? "square_hd" : "square"; const modelId = activeProvider.model; return Response.json({ ...defaultConfig, provider: { type: activeProvider.type, baseUrl: activeProvider.baseUrl || DEFAULT_BFL_BASE_URL, }, failover: { enabled: providers.length > 1, fallbackProvider: providers.length > 1 ? { type: providers[1].type, baseUrl: providers[1].baseUrl, model: providers[1].model, } : null, }, transport: { throttleInterval, maxBuffering, }, image: { maxInputResolution, jpegQuality, }, model: { ...defaultConfig.model, id: modelId || defaultConfig.model.id, defaults: { steps, cfgScale, strength, imageSize, }, }, behavior: { strictSketch: config.strictSketch !== false, renderMode: config.renderMode === "strict-structure" || config.renderMode === "stylize" ? config.renderMode : "balanced", }, }); }