From 1cda4fa23e32fb99798f67565aa932a3fa1f032c Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 20 Apr 2026 15:45:07 +0800 Subject: [PATCH] fix: settle fallback model billing and worldlabs key pool --- .../settings/admin-settings-client.tsx | 138 +++++++++++++++++- app/(chat)/api/chat/route.ts | 38 ++++- app/api/worldlabs/key-pool/import/route.ts | 42 ++++-- lib/worldlabs/server.ts | 19 ++- 4 files changed, 211 insertions(+), 26 deletions(-) diff --git a/app/(admin)/settings/admin-settings-client.tsx b/app/(admin)/settings/admin-settings-client.tsx index 019004933..dcc5c12db 100644 --- a/app/(admin)/settings/admin-settings-client.tsx +++ b/app/(admin)/settings/admin-settings-client.tsx @@ -1790,6 +1790,7 @@ export function AdminSettingsClient() { const [worldlabsKeyPoolConfig, setWorldlabsKeyPoolConfig] = useState( defaultWorldLabsKeyPoolConfig ); + const [worldlabsApiKeysDraft, setWorldlabsApiKeysDraft] = useState(""); const [threeDModelAliasConfig, setThreeDModelAliasConfig] = useState( defaultThreeDModelAliasConfig() ); @@ -5091,6 +5092,102 @@ export function AdminSettingsClient() { } }; + const handleAddWorldlabs3dPoolKey = async () => { + try { + const trimmedName = worldlabs3dConfig.name.trim() || "World Labs Key"; + const trimmedBaseUrl = + worldlabs3dConfig.baseUrl.trim() || + getProviderDefaultBaseUrl("worldlabs-3d"); + const trimmedApiKey = worldlabs3dConfig.apiKey.trim(); + + if (!trimmedApiKey) { + toast.error("请填写要加入 Key 池的 World Labs API Key"); + return; + } + + const response = await fetch("/api/worldlabs/key-pool/import", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + keys: [ + { + apiKey: trimmedApiKey, + name: trimmedName, + baseUrl: trimmedBaseUrl, + isEnabled: worldlabs3dConfig.isEnabled, + }, + ], + }), + }); + + const payload = await response.json().catch(() => ({})); + if (!response.ok || payload?.success === false) { + throw new Error( + String(payload?.error || payload?.message || "添加 World Labs Key 失败") + ); + } + + const importedCount = Number(payload?.data?.importedCount || 0); + const skippedCount = Number(payload?.data?.skippedCount || 0); + toast.success( + importedCount > 0 + ? "World Labs Key 已加入池" + : skippedCount > 0 + ? "这个 World Labs Key 已存在,已跳过重复添加" + : "World Labs Key 处理完成" + ); + setWorldlabs3dConfig((prev) => ({ ...prev, apiKey: "" })); + mutateProviders(); + } catch (error) { + toast.error( + error instanceof Error ? error.message : "添加 World Labs Key 失败" + ); + } + }; + + const handleImportWorldlabs3dPoolKeys = async () => { + try { + const apiKeysText = worldlabsApiKeysDraft.trim(); + if (!apiKeysText) { + toast.error("请先粘贴要批量导入的 World Labs API Key"); + return; + } + + const response = await fetch("/api/worldlabs/key-pool/import", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + apiKeysText, + namePrefix: worldlabs3dConfig.name.trim() || "WorldLabsKey", + baseUrl: + worldlabs3dConfig.baseUrl.trim() || + getProviderDefaultBaseUrl("worldlabs-3d"), + isEnabled: worldlabs3dConfig.isEnabled, + }), + }); + + const payload = await response.json().catch(() => ({})); + if (!response.ok || payload?.success === false) { + throw new Error( + String(payload?.error || payload?.message || "批量导入 World Labs Key 失败") + ); + } + + const importedCount = Number(payload?.data?.importedCount || 0); + const skippedCount = Number(payload?.data?.skippedCount || 0); + toast.success( + `World Labs Key 导入完成:新增 ${importedCount} 个,跳过 ${skippedCount} 个` + ); + setWorldlabsApiKeysDraft(""); + setWorldlabs3dConfig((prev) => ({ ...prev, apiKey: "" })); + mutateProviders(); + } catch (error) { + toast.error( + error instanceof Error ? error.message : "批量导入 World Labs Key 失败" + ); + } + }; + const updateThreeDModelAliasEntries = ( key: "tripoModels" | "worldModels", updater: (entries: ThreeDModelAliasEntry[]) => ThreeDModelAliasEntry[] @@ -15071,9 +15168,37 @@ export function AdminSettingsClient() { /api/worldlabs/key-pool/import - 批量导入新 Key,保存到数据库系统设置。 + 批量导入新 Key;后台管理员在本页面导入时不需要额外填写令牌。 +
+
+
+ +
+ 每行一个 World Labs API Key,导入时会自动跳过已存在或重复的 + Key。 +
+
+ +
+