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 (
Configure your custom AI API keys and endpoints for personalized AI interactions.
Set up your custom AI API key to use personalized models and endpoints.
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.