diff --git a/src/app/(dashboard)/dashboard/providers/[id]/page.tsx b/src/app/(dashboard)/dashboard/providers/[id]/page.tsx index 58651ada52..587cfb1b93 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/page.tsx +++ b/src/app/(dashboard)/dashboard/providers/[id]/page.tsx @@ -31,7 +31,9 @@ import { isSelfHostedChatProvider, providerAllowsOptionalApiKey, supportsApiKeyOnFreeProvider, + supportsBulkApiKey, } from "@/shared/constants/providers"; +import { parseBulkApiKeys } from "@/shared/utils/bulkApiKeyParser"; import { getModelsByProviderId } from "@/shared/constants/models"; import { compatibleProviderSupportsModelImport, @@ -6294,6 +6296,18 @@ function AddApiKeyModal({ const [saveError, setSaveError] = useState(null); const [showAdvanced, setShowAdvanced] = useState(false); const [copiedCommandCodeField, setCopiedCommandCodeField] = useState(null); + + const bulkSupported = supportsBulkApiKey(provider); + const [mode, setMode] = useState<"single" | "bulk">("single"); + const [bulkText, setBulkText] = useState(""); + const [bulkValidateKeys, setBulkValidateKeys] = useState(false); + const [bulkResult, setBulkResult] = useState<{ + success: number; + failed: number; + total: number; + errors: Array<{ index: number; name: string; message: string }>; + } | null>(null); + const [bulkWarnings, setBulkWarnings] = useState([]); const apiCredentialLabel = isQoder ? t("personalAccessTokenLabel") : isWebSessionProvider @@ -6486,6 +6500,45 @@ function AddApiKeyModal({ } }; + const handleBulkSubmit = async () => { + if (!provider) return; + const parsed = parseBulkApiKeys(bulkText); + setBulkWarnings(parsed.warnings); + if (parsed.entries.length === 0) return; + + setSaving(true); + setBulkResult(null); + setSaveError(null); + + try { + const res = await fetch("/api/providers/bulk", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + provider, + entries: parsed.entries.map((e) => ({ name: e.name, apiKey: e.apiKey })), + priority: formData.priority || 1, + validateKeys: bulkValidateKeys, + }), + }); + const data = await res.json(); + if (!res.ok) { + setSaveError(typeof data?.error === "string" ? data.error : t("failedSaveConnection")); + return; + } + setBulkResult({ + success: data.success || 0, + failed: data.failed || 0, + total: data.total || 0, + errors: Array.isArray(data.errors) ? data.errors : [], + }); + } catch (err) { + setSaveError(err instanceof Error ? err.message : t("failedSaveConnection")); + } finally { + setSaving(false); + } + }; + if (!provider) return null; return ( @@ -6495,301 +6548,425 @@ function AddApiKeyModal({ onClose={onClose} >
- {isCcCompatible && ( -
-
- - warning - -

{t("ccCompatibleValidationHint")}

-
+ {bulkSupported && ( +
+ +
)} - {isCommandCode && onStartCommandCodeAuth && ( -
-
- - open_in_new - -
-

Browser/manual connect

-

- Open Command Code Studio, then paste the returned key/JSON/URL into the API key - field below. -

- {commandCodeAuthState?.message && ( -

- {commandCodeAuthPhaseLabel}: {commandCodeAuthState.message} -

+ + {bulkSupported && mode === "bulk" && ( +
+

{t("bulkAddFormatHint")}

+