diff --git a/app/(admin)/settings/_components/local-ai-sections/model3d-sections.tsx b/app/(admin)/settings/_components/local-ai-sections/model3d-sections.tsx index 754910fa9..9d4ed9987 100644 --- a/app/(admin)/settings/_components/local-ai-sections/model3d-sections.tsx +++ b/app/(admin)/settings/_components/local-ai-sections/model3d-sections.tsx @@ -372,9 +372,10 @@ export function LocalAiModel3dSections({ - setWorldlabsKeyPoolConfig({ + setWorldlabsKeyPoolConfig((prev) => ({ + ...prev, importToken: e.target.value, - }) + })) } placeholder="供 /api/worldlabs/key-pool/import 调用的 Bearer Token" type="password" diff --git a/app/(admin)/settings/_lib/types.ts b/app/(admin)/settings/_lib/types.ts index a4ee95841..20e8cebab 100644 --- a/app/(admin)/settings/_lib/types.ts +++ b/app/(admin)/settings/_lib/types.ts @@ -167,6 +167,9 @@ export type AiProviderSettingsConfig = { export type WorldLabsKeyPoolConfig = { importToken: string; + providerName: string; + baseUrl: string; + isEnabled: boolean; }; export type LocalAiModel3dSectionsProps = LocalAiSectionControlProps & { diff --git a/app/(admin)/settings/admin-settings-client.tsx b/app/(admin)/settings/admin-settings-client.tsx index cb4b45db9..38cca481d 100644 --- a/app/(admin)/settings/admin-settings-client.tsx +++ b/app/(admin)/settings/admin-settings-client.tsx @@ -3120,15 +3120,19 @@ export function AdminSettingsClient() { }, [providersData?.providers]); useEffect(() => { + const savedWorldlabsConfig = normalizeWorldLabsKeyPoolConfig( + worldlabsKeyPoolConfigData?.setting?.value + ); const worldlabsProvider = (providersData?.providers || []).find( (provider) => provider.type === "worldlabs-3d" ); if (worldlabsProvider) { setWorldlabs3dConfig({ providerId: worldlabsProvider.id, - name: worldlabsProvider.name || "World Labs", + name: worldlabsProvider.name || savedWorldlabsConfig.providerName, baseUrl: worldlabsProvider.baseUrl || + savedWorldlabsConfig.baseUrl || getProviderDefaultBaseUrl("worldlabs-3d"), apiKey: "", isEnabled: worldlabsProvider.isEnabled, @@ -3136,14 +3140,15 @@ export function AdminSettingsClient() { return; } - setWorldlabs3dConfig({ + setWorldlabs3dConfig((prev) => ({ providerId: "", - name: "World Labs", - baseUrl: getProviderDefaultBaseUrl("worldlabs-3d"), - apiKey: "", - isEnabled: true, - }); - }, [providersData?.providers]); + name: savedWorldlabsConfig.providerName || "World Labs", + baseUrl: + savedWorldlabsConfig.baseUrl || getProviderDefaultBaseUrl("worldlabs-3d"), + apiKey: prev.providerId ? "" : prev.apiKey, + isEnabled: savedWorldlabsConfig.isEnabled, + })); + }, [providersData?.providers, worldlabsKeyPoolConfigData?.setting?.value]); useEffect(() => { setWorldlabsKeyPoolConfig( @@ -6407,9 +6412,37 @@ export function AdminSettingsClient() { worldlabs3dConfig.baseUrl.trim() || getProviderDefaultBaseUrl("worldlabs-3d"); const hasProvider = Boolean(worldlabs3dConfig.providerId); + const normalizedKeyPoolConfig = normalizeWorldLabsKeyPoolConfig({ + ...worldlabsKeyPoolConfig, + providerName: trimmedName, + baseUrl: trimmedBaseUrl, + isEnabled: worldlabs3dConfig.isEnabled, + }); if (!hasProvider && !worldlabs3dConfig.apiKey.trim()) { - toast.error("请填写 World Labs API Key"); + const keyPoolResponse = await fetch("/api/admin/settings", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + key: WORLDLABS_KEY_POOL_CONFIG_KEY, + value: normalizedKeyPoolConfig, + }), + }); + + if (!keyPoolResponse.ok) { + const payload = await keyPoolResponse.json().catch(() => ({})); + throw new Error( + String( + payload?.message || + payload?.error || + "保存 World Labs 基础配置失败" + ) + ); + } + + setWorldlabsKeyPoolConfig(normalizedKeyPoolConfig); + mutateWorldlabsKeyPoolConfig(); + toast.success("World Labs 基础配置已保存,添加 API Key 后即可生成"); return; } @@ -6448,7 +6481,7 @@ export function AdminSettingsClient() { headers: { "Content-Type": "application/json" }, body: JSON.stringify({ key: WORLDLABS_KEY_POOL_CONFIG_KEY, - value: normalizeWorldLabsKeyPoolConfig(worldlabsKeyPoolConfig), + value: normalizedKeyPoolConfig, }), }); @@ -6465,6 +6498,7 @@ export function AdminSettingsClient() { toast.success("World Labs 配置保存成功"); setWorldlabs3dConfig((prev) => ({ ...prev, apiKey: "" })); + setWorldlabsKeyPoolConfig(normalizedKeyPoolConfig); mutateProviders(); mutateWorldlabsKeyPoolConfig(); } catch (error) { diff --git a/app/api/worldlabs/key-pool/import/route.ts b/app/api/worldlabs/key-pool/import/route.ts index 8e8835032..11c74a603 100644 --- a/app/api/worldlabs/key-pool/import/route.ts +++ b/app/api/worldlabs/key-pool/import/route.ts @@ -9,13 +9,14 @@ import { sanitizeWorldLabsApiKey, sanitizeWorldLabsBaseUrl, } from "@/lib/worldlabs/server"; -import { getWorldLabsKeyPoolConfig } from "@/lib/worldlabs/key-pool-config"; +import { + DEFAULT_WORLDLABS_BASE_URL, + getWorldLabsKeyPoolConfig, +} from "@/lib/worldlabs/key-pool-config"; export const runtime = "nodejs"; export const maxDuration = 60; -const DEFAULT_WORLDLABS_BASE_URL = "https://api.worldlabs.ai"; - const importPayloadSchema = z.object({ keys: z .array( @@ -161,7 +162,7 @@ export async function POST(request: Request) { ); const defaultBaseUrl = - sanitizeWorldLabsBaseUrl(payload.baseUrl || "") || + sanitizeWorldLabsBaseUrl(payload.baseUrl || dbConfig.baseUrl || "") || DEFAULT_WORLDLABS_BASE_URL; const defaultIsEnabled = payload.isEnabled !== false; const namePrefix = String(payload.namePrefix || "WorldLabsKey").trim(); diff --git a/lib/worldlabs/key-pool-config-shared.ts b/lib/worldlabs/key-pool-config-shared.ts index f80a4da3c..494072ef2 100644 --- a/lib/worldlabs/key-pool-config-shared.ts +++ b/lib/worldlabs/key-pool-config-shared.ts @@ -1,11 +1,18 @@ export const WORLDLABS_KEY_POOL_CONFIG_KEY = "worldlabs_key_pool_config"; +export const DEFAULT_WORLDLABS_BASE_URL = "https://api.worldlabs.ai"; export type WorldLabsKeyPoolConfig = { importToken: string; + providerName: string; + baseUrl: string; + isEnabled: boolean; }; export const defaultWorldLabsKeyPoolConfig: WorldLabsKeyPoolConfig = { importToken: "", + providerName: "World Labs", + baseUrl: DEFAULT_WORLDLABS_BASE_URL, + isEnabled: true, }; function normalizeImportToken(value: unknown) { @@ -25,5 +32,11 @@ export function normalizeWorldLabsKeyPoolConfig( const raw = value as Partial; return { importToken: normalizeImportToken(raw.importToken), + providerName: + String(raw.providerName || "World Labs").trim() || "World Labs", + baseUrl: + String(raw.baseUrl || DEFAULT_WORLDLABS_BASE_URL).trim() || + DEFAULT_WORLDLABS_BASE_URL, + isEnabled: raw.isEnabled !== false, }; } diff --git a/lib/worldlabs/key-pool-config.ts b/lib/worldlabs/key-pool-config.ts index d0c1ef46b..5bca9c4a1 100644 --- a/lib/worldlabs/key-pool-config.ts +++ b/lib/worldlabs/key-pool-config.ts @@ -2,6 +2,7 @@ import "server-only"; import { getSystemSetting } from "@/lib/db/queries"; import { + DEFAULT_WORLDLABS_BASE_URL, defaultWorldLabsKeyPoolConfig, normalizeWorldLabsKeyPoolConfig, WORLDLABS_KEY_POOL_CONFIG_KEY, @@ -9,6 +10,7 @@ import { } from "@/lib/worldlabs/key-pool-config-shared"; export { + DEFAULT_WORLDLABS_BASE_URL, defaultWorldLabsKeyPoolConfig, normalizeWorldLabsKeyPoolConfig, WORLDLABS_KEY_POOL_CONFIG_KEY,