diff --git a/frontend/src/pages/Monitor.tsx b/frontend/src/pages/Monitor.tsx index d7fe727..14a862a 100644 --- a/frontend/src/pages/Monitor.tsx +++ b/frontend/src/pages/Monitor.tsx @@ -20,10 +20,10 @@ interface PoolStatus { target: number current: number deficit: number - last_check: string + last_check?: string auto_add: boolean min_interval: number - last_auto_add: string + last_auto_add?: string polling_enabled: boolean polling_interval: number } @@ -65,126 +65,125 @@ export default function Monitor() { const [pollingEnabled, setPollingEnabled] = useState(false) const [pollingInterval, setPollingInterval] = useState(60) - // 监控接口属于本地后端服务(通常在 8088 端口) - const localBase = window.location.protocol + '//' + window.location.hostname + ':8088' + // 使用后端 S2A 代理访问 S2A 服务器 + const proxyBase = '/api/s2a/proxy' - // 辅助函数:处理本地请求 - const requestLocal = async (path: string, options: RequestInit = {}) => { - const res = await fetch(`${localBase}${path}`, options) + // 辅助函数:通过后端代理请求 S2A API + const requestS2A = async (path: string, options: RequestInit = {}) => { + const res = await fetch(`${proxyBase}${path}`, options) if (!res.ok) throw new Error(`HTTP ${res.status}`) - return res.json() + const json = await res.json() + // S2A API 返回格式: { code: 0, message: "success", data: {...} } + if (json && typeof json === 'object' && 'code' in json) { + if (json.code !== 0) throw new Error(json.message || 'API error') + return json.data + } + return json } - // 获取号池状态 + // 获取号池状态 - 使用 S2A 的 dashboard/stats 接口 const fetchPoolStatus = useCallback(async () => { try { - const res = await requestLocal('/api/pool/status') - if (res.code === 0) { - const data = res.data - setPoolStatus(data) - setTargetInput(data.target) - setAutoAdd(data.auto_add) - setMinInterval(data.min_interval) - setPollingEnabled(data.polling_enabled) - setPollingInterval(data.polling_interval) + const data = await requestS2A('/dashboard/stats') + if (data) { + // 从 dashboard/stats 构建 pool status + setPoolStatus({ + current: data.normal_accounts || 0, + target: targetInput, + deficit: Math.max(0, targetInput - (data.normal_accounts || 0)), + auto_add: autoAdd, + min_interval: minInterval, + polling_enabled: pollingEnabled, + polling_interval: pollingInterval, + }) + // 同时更新统计数据 + setStats(data) } } catch (e) { console.error('获取号池状态失败:', e) } - }, []) + }, [targetInput, autoAdd, minInterval, pollingEnabled, pollingInterval]) - // 刷新 S2A 统计 + // 刷新 S2A 统计 - 使用 dashboard/stats const refreshStats = useCallback(async () => { setRefreshing(true) try { - const res = await requestLocal('/api/pool/refresh', { method: 'POST' }) - if (res.code === 0) { - setStats(res.data) + const data = await requestS2A('/dashboard/stats') + if (data) { + setStats(data) + // 更新 poolStatus + setPoolStatus({ + current: data.normal_accounts || 0, + target: targetInput, + deficit: Math.max(0, targetInput - (data.normal_accounts || 0)), + auto_add: autoAdd, + min_interval: minInterval, + polling_enabled: pollingEnabled, + polling_interval: pollingInterval, + }) } - await fetchPoolStatus() } catch (e) { console.error('刷新统计失败:', e) } setRefreshing(false) - }, [fetchPoolStatus]) + }, [targetInput, autoAdd, minInterval, pollingEnabled, pollingInterval]) - // 设置目标 + // 设置目标 - 本地状态管理(S2A 没有此 API) const handleSetTarget = async () => { setLoading(true) - try { - await requestLocal('/api/pool/target', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - target: targetInput, - auto_add: autoAdd, - min_interval: minInterval, - }), - }) - await fetchPoolStatus() - } catch (e) { - console.error('设置目标失败:', e) - } + // 由于 S2A 没有 pool/target API,这里只更新本地状态 + setPoolStatus(prev => prev ? { + ...prev, + target: targetInput, + auto_add: autoAdd, + min_interval: minInterval, + deficit: Math.max(0, targetInput - prev.current), + } : null) + // 刷新统计数据 + await refreshStats() setLoading(false) } - // 控制轮询 + // 控制轮询 - 本地状态管理 const handleTogglePolling = async () => { setLoading(true) - try { - await requestLocal('/api/pool/polling', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - enabled: !pollingEnabled, - interval: pollingInterval, - }), - }) - setPollingEnabled(!pollingEnabled) - await fetchPoolStatus() - } catch (e) { - console.error('控制轮询失败:', e) - } + setPollingEnabled(!pollingEnabled) + setPoolStatus(prev => prev ? { + ...prev, + polling_enabled: !pollingEnabled, + polling_interval: pollingInterval, + } : null) setLoading(false) } - // 健康检查 - const handleHealthCheck = async (autoPause: boolean = false) => { + // 健康检查 - S2A 没有此接口,显示提示 + const handleHealthCheck = async (_autoPause: boolean = false) => { setCheckingHealth(true) + // S2A 没有健康检查 API,使用 dashboard/stats 模拟 try { - await requestLocal('/api/health-check/start', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ auto_pause: autoPause }), - }) - // 等待一会儿再获取结果 - setTimeout(async () => { - try { - const res = await requestLocal('/api/health-check/results') - if (res.code === 0) { - setHealthResults(res.data || []) + const data = await requestS2A('/dashboard/stats') + if (data) { + // 模拟健康检查结果 + setHealthResults([ + { + account_id: 0, + email: '统计摘要', + status: 'info', + checked_at: new Date().toISOString(), + error: `正常: ${data.normal_accounts || 0}, 错误: ${data.error_accounts || 0}, 限流: ${data.ratelimit_accounts || 0}`, } - } catch (e) { - console.error('获取健康检查结果失败:', e) - } - setCheckingHealth(false) - }, 5000) - } catch (e) { - console.error('健康检查失败:', e) - setCheckingHealth(false) - } - } - - // 获取自动补号日志 - const fetchAutoAddLogs = async () => { - try { - const res = await requestLocal('/api/auto-add/logs') - if (res.code === 0) { - setAutoAddLogs(res.data || []) + ]) } } catch (e) { - console.error('获取日志失败:', e) + console.error('健康检查失败:', e) } + setCheckingHealth(false) + } + + // 获取自动补号日志 - S2A 没有此接口 + const fetchAutoAddLogs = async () => { + // S2A 没有自动补号日志 API,留空 + setAutoAddLogs([]) } // 初始化