Fix provider API key validation trimming (#810)
This commit is contained in:
committed by
GitHub
Unverified
parent
66b2ddb2dc
commit
49518300dc
@@ -34,6 +34,7 @@ import {
|
||||
getProviderDocsUrl,
|
||||
type ProviderType,
|
||||
getProviderIconUrl,
|
||||
normalizeProviderApiKeyInput,
|
||||
resolveProviderApiKeyForSave,
|
||||
resolveProviderModelForSave,
|
||||
shouldShowProviderModelId,
|
||||
@@ -424,10 +425,11 @@ function ProviderCard({
|
||||
try {
|
||||
const payload: { newApiKey?: string; updates?: Partial<ProviderConfig> } = {};
|
||||
const normalizedFallbackModels = normalizeFallbackModels(fallbackModelsText.split('\n'));
|
||||
const normalizedNewKey = normalizeProviderApiKeyInput(newKey);
|
||||
|
||||
if (newKey.trim()) {
|
||||
if (normalizedNewKey) {
|
||||
setValidating(true);
|
||||
const result = await onValidateKey(newKey, {
|
||||
const result = await onValidateKey(normalizedNewKey, {
|
||||
baseUrl: baseUrl.trim() || undefined,
|
||||
apiProtocol: (account.vendorId === 'custom' || account.vendorId === 'ollama') ? apiProtocol : undefined,
|
||||
});
|
||||
@@ -437,7 +439,7 @@ function ProviderCard({
|
||||
setSaving(false);
|
||||
return;
|
||||
}
|
||||
payload.newApiKey = newKey.trim();
|
||||
payload.newApiKey = normalizedNewKey;
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1164,13 +1166,14 @@ function AddProviderDialog({
|
||||
try {
|
||||
// Validate key first if the provider requires one and a key was entered
|
||||
const requiresKey = typeInfo?.requiresApiKey ?? false;
|
||||
if (requiresKey && !apiKey.trim()) {
|
||||
const normalizedApiKey = normalizeProviderApiKeyInput(apiKey);
|
||||
if (requiresKey && !normalizedApiKey) {
|
||||
setValidationError(t('aiProviders.toast.invalidKey')); // reusing invalid key msg or should add 'required' msg? null checks
|
||||
setSaving(false);
|
||||
return;
|
||||
}
|
||||
if (requiresKey && apiKey) {
|
||||
const result = await onValidateKey(selectedType, apiKey, {
|
||||
if (requiresKey && normalizedApiKey) {
|
||||
const result = await onValidateKey(selectedType, normalizedApiKey, {
|
||||
baseUrl: baseUrl.trim() || undefined,
|
||||
apiProtocol: (selectedType === 'custom' || selectedType === 'ollama') ? apiProtocol : undefined,
|
||||
});
|
||||
@@ -1191,7 +1194,7 @@ function AddProviderDialog({
|
||||
await onAdd(
|
||||
selectedType,
|
||||
name || (typeInfo?.id === 'custom' ? t('aiProviders.custom') : typeInfo?.name) || selectedType,
|
||||
apiKey.trim(),
|
||||
normalizedApiKey,
|
||||
{
|
||||
baseUrl: baseUrl.trim() || undefined,
|
||||
apiProtocol: (selectedType === 'custom' || selectedType === 'ollama') ? apiProtocol : undefined,
|
||||
@@ -1655,6 +1658,7 @@ function AddProviderDialog({
|
||||
|
||||
<div className="flex justify-end gap-3">
|
||||
<Button
|
||||
data-testid="add-provider-submit-button"
|
||||
onClick={handleAdd}
|
||||
className={cn("rounded-full px-8 h-[42px] text-[13px] font-semibold bg-[#0a84ff] hover:bg-[#007aff] text-white shadow-sm", useOAuthFlow && "hidden")}
|
||||
disabled={!selectedType || saving || (showModelIdField && modelId.trim().length === 0)}
|
||||
|
||||
@@ -246,9 +246,13 @@ export function resolveProviderModelForSave(
|
||||
return trimmedModelId || provider?.defaultModelId || undefined;
|
||||
}
|
||||
|
||||
export function normalizeProviderApiKeyInput(apiKey: string): string {
|
||||
return apiKey.trim();
|
||||
}
|
||||
|
||||
/** Normalize provider API key before saving; Ollama uses a local placeholder when blank. */
|
||||
export function resolveProviderApiKeyForSave(type: ProviderType | string, apiKey: string): string | undefined {
|
||||
const trimmed = apiKey.trim();
|
||||
const trimmed = normalizeProviderApiKeyInput(apiKey);
|
||||
if (type === 'ollama') {
|
||||
return trimmed || OLLAMA_PLACEHOLDER_API_KEY;
|
||||
}
|
||||
|
||||
@@ -99,6 +99,7 @@ import {
|
||||
type ProviderTypeInfo,
|
||||
getProviderDocsUrl,
|
||||
getProviderIconUrl,
|
||||
normalizeProviderApiKeyInput,
|
||||
resolveProviderApiKeyForSave,
|
||||
resolveProviderModelForSave,
|
||||
shouldInvertInDark,
|
||||
@@ -1009,6 +1010,7 @@ function ProviderContent({
|
||||
const isOAuth = selectedProviderData?.isOAuth ?? false;
|
||||
const supportsApiKey = selectedProviderData?.supportsApiKey ?? false;
|
||||
const useOAuthFlow = isOAuth && (!supportsApiKey || authMode === 'oauth');
|
||||
const normalizedApiKey = normalizeProviderApiKeyInput(apiKey);
|
||||
|
||||
const handleValidateAndSave = async () => {
|
||||
if (!selectedProvider) return;
|
||||
@@ -1034,11 +1036,19 @@ function ProviderContent({
|
||||
try {
|
||||
// Validate key if the provider requires one and a key was entered
|
||||
const isApiKeyRequired = requiresKey || (supportsApiKey && authMode === 'apikey');
|
||||
if (isApiKeyRequired && apiKey) {
|
||||
if (isApiKeyRequired && !normalizedApiKey) {
|
||||
setKeyValid(false);
|
||||
onConfiguredChange(false);
|
||||
toast.error(t('provider.invalid'));
|
||||
setValidating(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isApiKeyRequired) {
|
||||
const result = await invokeIpc(
|
||||
'provider:validateKey',
|
||||
selectedAccountId || selectedProvider,
|
||||
apiKey,
|
||||
normalizedApiKey,
|
||||
{
|
||||
baseUrl: baseUrl.trim() || undefined,
|
||||
apiProtocol: (selectedProvider === 'custom' || selectedProvider === 'ollama')
|
||||
@@ -1146,7 +1156,7 @@ function ProviderContent({
|
||||
const isApiKeyRequired = requiresKey || (supportsApiKey && authMode === 'apikey');
|
||||
const canSubmit =
|
||||
selectedProvider
|
||||
&& (isApiKeyRequired ? apiKey.length > 0 : true)
|
||||
&& (isApiKeyRequired ? normalizedApiKey.length > 0 : true)
|
||||
&& (showModelIdField ? modelId.trim().length > 0 : true)
|
||||
&& !useOAuthFlow;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import type {
|
||||
ProviderVendorInfo,
|
||||
ProviderWithKeyInfo,
|
||||
} from '@/lib/providers';
|
||||
import { normalizeProviderApiKeyInput } from '@/lib/providers';
|
||||
import { hostApiFetch } from '@/lib/host-api';
|
||||
import {
|
||||
fetchProviderSnapshot,
|
||||
@@ -326,9 +327,10 @@ export const useProviderStore = create<ProviderState>((set, get) => ({
|
||||
|
||||
validateAccountApiKey: async (providerId, apiKey, options) => {
|
||||
try {
|
||||
const normalizedApiKey = normalizeProviderApiKeyInput(apiKey);
|
||||
const result = await hostApiFetch<{ valid: boolean; error?: string }>('/api/providers/validate', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ providerId, apiKey, options }),
|
||||
body: JSON.stringify({ providerId, apiKey: normalizedApiKey, options }),
|
||||
});
|
||||
return result;
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user