From 9b4838b60430cf5cabaaf787a3762199c736af06 Mon Sep 17 00:00:00 2001 From: kyx236 Date: Tue, 3 Feb 2026 03:30:01 +0800 Subject: [PATCH] feat: add Codex proxy configuration page with full CRUD, toggle, and testing functionalities. --- frontend/src/pages/CodexProxyConfig.tsx | 86 +++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 4 deletions(-) diff --git a/frontend/src/pages/CodexProxyConfig.tsx b/frontend/src/pages/CodexProxyConfig.tsx index 99f2ecf..1379584 100644 --- a/frontend/src/pages/CodexProxyConfig.tsx +++ b/frontend/src/pages/CodexProxyConfig.tsx @@ -2,7 +2,7 @@ import { useState, useEffect, useCallback } from 'react' import { Globe, Plus, Trash2, ToggleLeft, ToggleRight, Loader2, Save, RefreshCcw, CheckCircle, XCircle, - AlertTriangle, Clock, MapPin, Play + AlertTriangle, Clock, MapPin, Play, PlayCircle } from 'lucide-react' import { Card, CardHeader, CardTitle, CardContent, Button, Input } from '../components/common' @@ -31,6 +31,7 @@ export default function CodexProxyConfig() { const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) const [testingIds, setTestingIds] = useState>(new Set()) + const [testingAll, setTestingAll] = useState(false) const [message, setMessage] = useState<{ type: 'success' | 'error', text: string } | null>(null) // 单个添加 @@ -177,6 +178,70 @@ export default function CodexProxyConfig() { } } + // 一键测试所有代理 + const handleTestAll = async () => { + if (testingAll || proxies.length === 0) return + setTestingAll(true) + setMessage(null) + + let successCount = 0 + let failCount = 0 + + // 并发测试所有代理(最多5个并发) + const concurrency = 5 + const chunks: CodexProxy[][] = [] + for (let i = 0; i < proxies.length; i += concurrency) { + chunks.push(proxies.slice(i, i + concurrency)) + } + + for (const chunk of chunks) { + // 标记当前批次为测试中 + const chunkIds = chunk.map(p => p.id) + setTestingIds(prev => { + const next = new Set(prev) + chunkIds.forEach(id => next.add(id)) + return next + }) + + // 并发执行当前批次 + const results = await Promise.allSettled( + chunk.map(async (proxy) => { + try { + const res = await fetch(`/api/codex-proxy/test?id=${proxy.id}`, { method: 'POST' }) + const data = await res.json() + return { id: proxy.id, success: data.code === 0, data } + } catch { + return { id: proxy.id, success: false } + } + }) + ) + + // 统计结果 + results.forEach(result => { + if (result.status === 'fulfilled' && result.value.success) { + successCount++ + } else { + failCount++ + } + }) + + // 移除当前批次的测试状态 + setTestingIds(prev => { + const next = new Set(prev) + chunkIds.forEach(id => next.delete(id)) + return next + }) + } + + // 刷新列表获取最新数据 + await fetchProxies() + setTestingAll(false) + setMessage({ + type: successCount > 0 ? 'success' : 'error', + text: `测试完成: ${successCount} 成功, ${failCount} 失败` + }) + } + // 清空所有 const handleClearAll = async () => { if (!confirm('确定要清空所有代理吗?此操作不可恢复!')) return @@ -376,9 +441,22 @@ export default function CodexProxyConfig() { 代理列表 - - 共 {proxies.length} 个代理 - +
+ {proxies.length > 0 && ( + + )} + + 共 {proxies.length} 个代理 + +
{proxies.length === 0 ? (