import { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { Key, Eye, EyeOff, TestTube, Save, Trash2, AlertCircle, CheckCircle } from 'lucide-react'; import { toast } from 'sonner'; import { apiKeysAPI } from '../lib/api'; const ApiKeysPage = () => { const [showApiKey, setShowApiKey] = useState(false); const [formData, setFormData] = useState({ apiKey: '', model: '', apiUrl: '', }); const [isEditing, setIsEditing] = useState(false); const [testResult, setTestResult] = useState<{ success: boolean; message: string } | null>(null); const [hasPassedTest, setHasPassedTest] = useState(false); const queryClient = useQueryClient(); const { data: apiKeyInfo, isLoading } = useQuery({ queryKey: ['api-keys'], queryFn: () => apiKeysAPI.getApiKeys().then((res) => res.data), }); const updateApiKeyMutation = useMutation({ mutationFn: (data: { apiKey: string; model?: string; apiUrl?: string }) => apiKeysAPI.updateApiKey(data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['api-keys'] }); setIsEditing(false); setFormData({ apiKey: '', model: '', apiUrl: '' }); toast.success('API key updated successfully!'); }, onError: () => { toast.error('Failed to update API key'); }, }); const deleteApiKeyMutation = useMutation({ mutationFn: () => apiKeysAPI.deleteApiKey(), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['api-keys'] }); setFormData({ apiKey: '', model: '', apiUrl: '' }); setIsEditing(false); toast.success('API key deleted successfully!'); }, onError: () => { toast.error('Failed to delete API key'); }, }); const testApiKeyMutation = useMutation({ mutationFn: (data: { apiKey: string; model?: string; apiUrl?: string }) => apiKeysAPI.testApiKey(data), onSuccess: () => { setTestResult({ success: true, message: 'API key is valid and working!' }); setHasPassedTest(true); toast.success('API key test successful!'); }, onError: (error: { response?: { data?: { error?: string } } }) => { const message = error.response?.data?.error || 'API key test failed'; setTestResult({ success: false, message }); setHasPassedTest(false); toast.error(message); }, }); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!formData.apiKey.trim()) { toast.error('API key is required'); return; } if (!hasPassedTest) { toast.error('Please test the API key before saving'); return; } updateApiKeyMutation.mutate({ apiKey: formData.apiKey, model: formData.model || undefined, apiUrl: formData.apiUrl || undefined, }); }; const handleTest = () => { if (!formData.apiKey.trim()) { toast.error('API key is required for testing'); return; } setTestResult(null); testApiKeyMutation.mutate({ apiKey: formData.apiKey, model: formData.model || undefined, apiUrl: formData.apiUrl || undefined, }); }; const handleEdit = () => { setIsEditing(true); setFormData({ apiKey: '', model: apiKeyInfo?.model || '', apiUrl: apiKeyInfo?.apiUrl || '', }); setTestResult(null); setHasPassedTest(false); }; const handleCancel = () => { setIsEditing(false); setFormData({ apiKey: '', model: '', apiUrl: '' }); setTestResult(null); setHasPassedTest(false); }; if (isLoading) { return (
); } return (

AI API Keys

Configure your custom AI API keys and endpoints for personalized AI interactions.

Current Configuration

{apiKeyInfo?.hasApiKey && !isEditing && (
)}
{apiKeyInfo?.hasApiKey && !isEditing ? (
Status Configured
{apiKeyInfo.model && (
Model {apiKeyInfo.model}
)} {apiKeyInfo.apiUrl && (
API Endpoint {apiKeyInfo.apiUrl}
)}
) : (

No API Key Configured

Set up your custom AI API key to use personalized models and endpoints.

)}
{isEditing && (

{apiKeyInfo?.hasApiKey ? 'Update' : 'Configure'} API Key

* You must test the API key before saving to ensure it works correctly.

{ setFormData({ ...formData, apiKey: e.target.value }); setHasPassedTest(false); setTestResult(null); }} placeholder="Enter your API key" className="input pr-10 dark:bg-gray-700 dark:border-gray-600 dark:text-gray-100 dark:placeholder-gray-400" required />
{ setFormData({ ...formData, model: e.target.value }); setHasPassedTest(false); setTestResult(null); }} placeholder="e.g., openai/gpt-4o-mini, anthropic/claude-4-sonnet" className="input dark:bg-gray-700 dark:border-gray-600 dark:text-gray-100 dark:placeholder-gray-400" />

Leave empty to use the default model

{ setFormData({ ...formData, apiUrl: e.target.value }); setHasPassedTest(false); setTestResult(null); }} placeholder="https://openrouter.ai/api/v1" className="input dark:bg-gray-700 dark:border-gray-600 dark:text-gray-100 dark:placeholder-gray-400" />

Enter the base API URL (e.g., https://openrouter.ai/api/v1, https://api.openai.com/v1)

{testResult && (
{testResult.success ? ( ) : ( )} {testResult.message}
)}
)}

Information

Your API key is encrypted and stored securely. It will only be used for AI interactions within the Discord bot.

Supported providers include OpenAI, OpenRouter, Anthropic, and any OpenAI-compatible API endpoints.

Use the test function to verify your API key works before saving.

); }; export default ApiKeysPage;