import React, { useState, useEffect, useCallback } from 'react'; import { IconButton } from '~/components/ui/IconButton'; import type { ProviderInfo } from '~/types/model'; import Cookies from 'js-cookie'; interface APIKeyManagerProps { provider: ProviderInfo; apiKey: string; setApiKey: (key: string) => void; getApiKeyLink?: string; labelForGetApiKey?: string; } // cache which stores whether the provider's API key is set via environment variable const providerEnvKeyStatusCache: Record = {}; const apiKeyMemoizeCache: { [k: string]: Record } = {}; export function getApiKeysFromCookies() { const storedApiKeys = Cookies.get('apiKeys'); let parsedKeys: Record = {}; if (storedApiKeys) { parsedKeys = apiKeyMemoizeCache[storedApiKeys]; if (!parsedKeys) { parsedKeys = apiKeyMemoizeCache[storedApiKeys] = JSON.parse(storedApiKeys); } } return parsedKeys; } // eslint-disable-next-line @typescript-eslint/naming-convention export const APIKeyManager: React.FC = ({ provider, apiKey, setApiKey }) => { const [isEditing, setIsEditing] = useState(false); const [tempKey, setTempKey] = useState(apiKey); const [isEnvKeySet, setIsEnvKeySet] = useState(false); // Reset states and load saved key when provider changes useEffect(() => { // Load saved API key from cookies for this provider const savedKeys = getApiKeysFromCookies(); const savedKey = savedKeys[provider.name] || ''; setTempKey(savedKey); setApiKey(savedKey); setIsEditing(false); }, [provider.name]); const checkEnvApiKey = useCallback(async () => { // Check cache first if (providerEnvKeyStatusCache[provider.name] !== undefined) { setIsEnvKeySet(providerEnvKeyStatusCache[provider.name]); return; } try { const response = await fetch(`/api/check-env-key?provider=${encodeURIComponent(provider.name)}`); const data = await response.json(); const isSet = (data as { isSet: boolean }).isSet; // Cache the result providerEnvKeyStatusCache[provider.name] = isSet; setIsEnvKeySet(isSet); } catch (error) { console.error('Failed to check environment API key:', error); setIsEnvKeySet(false); } }, [provider.name]); useEffect(() => { checkEnvApiKey(); }, [checkEnvApiKey]); const handleSave = () => { // Save to parent state setApiKey(tempKey); // Save to cookies const currentKeys = getApiKeysFromCookies(); const newKeys = { ...currentKeys, [provider.name]: tempKey }; Cookies.set('apiKeys', JSON.stringify(newKeys)); setIsEditing(false); }; return (
{provider?.name} API Key: {!isEditing && (
{apiKey ? ( <>
Set via UI ) : isEnvKeySet ? ( <>
Set via environment variable ) : ( <>
Not Set (Please set via UI or ENV_VAR) )}
)}
{isEditing ? (
setTempKey(e.target.value)} className="w-[300px] px-3 py-1.5 text-sm rounded border border-bolt-elements-borderColor bg-bolt-elements-prompt-background text-bolt-elements-textPrimary focus:outline-none focus:ring-2 focus:ring-bolt-elements-focus" />
setIsEditing(false)} title="Cancel" className="bg-red-500/10 hover:bg-red-500/20 text-red-500" >
) : ( <> { setIsEditing(true)} title="Edit API Key" className="bg-blue-500/10 hover:bg-blue-500/20 text-blue-500" >
} {provider?.getApiKeyLink && !apiKey && ( window.open(provider?.getApiKeyLink)} title="Get API Key" className="bg-purple-500/10 hover:bg-purple-500/20 text-purple-500 flex items-center gap-2" > {provider?.labelForGetApiKey || 'Get API Key'}
)} )}
); };