import { useState, useEffect, useCallback } from 'react' import { Globe, Plus, Trash2, ToggleLeft, ToggleRight, Loader2, Save, RefreshCcw, CheckCircle, XCircle, AlertTriangle, Clock } from 'lucide-react' import { Card, CardHeader, CardTitle, CardContent, Button, Input } from '../components/common' interface CodexProxy { id: number proxy_url: string description: string is_enabled: boolean last_used_at: string | null success_count: number fail_count: number created_at: string } interface ProxyStats { total: number enabled: number disabled: number } export default function CodexProxyConfig() { const [proxies, setProxies] = useState([]) const [stats, setStats] = useState({ total: 0, enabled: 0, disabled: 0 }) const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) const [message, setMessage] = useState<{ type: 'success' | 'error', text: string } | null>(null) // 单个添加 const [newProxyUrl, setNewProxyUrl] = useState('') const [newDescription, setNewDescription] = useState('') // 批量添加 const [batchMode, setBatchMode] = useState(false) const [batchInput, setBatchInput] = useState('') // 获取代理列表 const fetchProxies = useCallback(async () => { setLoading(true) try { const res = await fetch('/api/codex-proxy') const data = await res.json() if (data.code === 0 && data.data) { setProxies(data.data.proxies || []) setStats(data.data.stats || { total: 0, enabled: 0, disabled: 0 }) } } catch (error) { console.error('获取代理列表失败:', error) } finally { setLoading(false) } }, []) useEffect(() => { fetchProxies() }, [fetchProxies]) // 添加代理 const handleAddProxy = async () => { if (!newProxyUrl.trim()) return setSaving(true) setMessage(null) try { const res = await fetch('/api/codex-proxy', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ proxy_url: newProxyUrl.trim(), description: newDescription.trim(), }), }) const data = await res.json() if (data.code === 0) { setMessage({ type: 'success', text: '代理添加成功' }) setNewProxyUrl('') setNewDescription('') fetchProxies() } else { setMessage({ type: 'error', text: data.message || '添加失败' }) } } catch { setMessage({ type: 'error', text: '网络错误' }) } finally { setSaving(false) } } // 批量添加 const handleBatchAdd = async () => { const lines = batchInput.split('\n').filter(line => line.trim()) if (lines.length === 0) return setSaving(true) setMessage(null) try { const res = await fetch('/api/codex-proxy', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ proxies: lines }), }) const data = await res.json() if (data.code === 0) { setMessage({ type: 'success', text: `成功添加 ${data.data.added}/${data.data.total} 个代理` }) setBatchInput('') fetchProxies() } else { setMessage({ type: 'error', text: data.message || '添加失败' }) } } catch { setMessage({ type: 'error', text: '网络错误' }) } finally { setSaving(false) } } // 切换启用状态 const handleToggle = async (id: number) => { try { const res = await fetch(`/api/codex-proxy?id=${id}`, { method: 'PUT' }) const data = await res.json() if (data.code === 0) { fetchProxies() } } catch (error) { console.error('切换状态失败:', error) } } // 删除代理 const handleDelete = async (id: number) => { if (!confirm('确定要删除这个代理吗?')) return try { const res = await fetch(`/api/codex-proxy?id=${id}`, { method: 'DELETE' }) const data = await res.json() if (data.code === 0) { fetchProxies() } } catch (error) { console.error('删除失败:', error) } } // 清空所有 const handleClearAll = async () => { if (!confirm('确定要清空所有代理吗?此操作不可恢复!')) return try { const res = await fetch('/api/codex-proxy?all=true', { method: 'DELETE' }) const data = await res.json() if (data.code === 0) { setMessage({ type: 'success', text: '已清空所有代理' }) fetchProxies() } } catch (error) { console.error('清空失败:', error) } } // 格式化代理显示 const formatProxyDisplay = (url: string) => { if (url.includes('@')) { const parts = url.split('@') return parts[parts.length - 1] } return url.replace(/^https?:\/\//, '').replace(/^socks5:\/\//, '') } // 格式化时间 const formatTime = (timeStr: string | null) => { if (!timeStr) return '从未' const date = new Date(timeStr) return date.toLocaleString('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }) } // 计算成功率 const getSuccessRate = (proxy: CodexProxy) => { const total = proxy.success_count + proxy.fail_count if (total === 0) return null return Math.round((proxy.success_count / total) * 100) } if (loading) { return (
) } return (
{/* Header */}

CodexAuth 代理池

管理 CodexAuth API 授权使用的代理池,支持随机轮换

{proxies.length > 0 && ( )}
{/* Message */} {message && (
{message.text}
)} {/* Stats */}
{stats.total}
总数
{stats.enabled}
已启用
{stats.disabled}
已禁用
{/* Add Proxy */} 添加代理 {batchMode ? ( <>