feat: Implement a pool monitoring dashboard and file upload functionality.

This commit is contained in:
2026-01-30 10:08:18 +08:00
parent b3841e08ea
commit cefbb2f38f
2 changed files with 51 additions and 46 deletions

View File

@@ -14,7 +14,6 @@ import {
Clock,
} from 'lucide-react'
import { Card, CardHeader, CardTitle, CardContent, Button, Input } from '../components/common'
import { useConfig } from '../hooks/useConfig'
import type { DashboardStats } from '../types'
interface PoolStatus {
@@ -50,7 +49,6 @@ interface AutoAddLog {
}
export default function Monitor() {
const { config } = useConfig()
const [stats, setStats] = useState<DashboardStats | null>(null)
const [poolStatus, setPoolStatus] = useState<PoolStatus | null>(null)
const [healthResults, setHealthResults] = useState<HealthCheckResult[]>([])
@@ -67,51 +65,54 @@ export default function Monitor() {
const [pollingEnabled, setPollingEnabled] = useState(false)
const [pollingInterval, setPollingInterval] = useState(60)
const backendUrl = config.s2a.apiBase.replace(/\/api.*$/, '').replace(/:8080/, ':8088')
// 使用后端代理路径
const proxyBase = '/api/s2a/proxy'
// 辅助函数:解包 S2A 响应
const requestS2A = async (url: string, options: RequestInit = {}) => {
const res = await fetch(url, options)
if (!res.ok) throw new Error(`HTTP ${res.status}`)
const data = await res.json()
if (data && typeof data === 'object' && 'code' in data && 'data' in data) {
if (data.code !== 0) throw new Error(data.message || 'API error')
return data.data
}
return data
}
// 获取号池状态
const fetchPoolStatus = useCallback(async () => {
try {
const res = await fetch(`${backendUrl}/api/pool/status`)
if (res.ok) {
const data = await res.json()
if (data.code === 0) {
setPoolStatus(data.data)
setTargetInput(data.data.target)
setAutoAdd(data.data.auto_add)
setMinInterval(data.data.min_interval)
setPollingEnabled(data.data.polling_enabled)
setPollingInterval(data.data.polling_interval)
}
}
const data = await requestS2A(`${proxyBase}/api/pool/status`)
setPoolStatus(data)
setTargetInput(data.target)
setAutoAdd(data.auto_add)
setMinInterval(data.min_interval)
setPollingEnabled(data.polling_enabled)
setPollingInterval(data.polling_interval)
} catch (e) {
console.error('获取号池状态失败:', e)
}
}, [backendUrl])
}, [])
// 刷新 S2A 统计
const refreshStats = useCallback(async () => {
setRefreshing(true)
try {
const res = await fetch(`${backendUrl}/api/pool/refresh`, { method: 'POST' })
if (res.ok) {
const data = await res.json()
if (data.code === 0) {
setStats(data.data)
}
}
const data = await requestS2A(`${proxyBase}/api/pool/refresh`, { method: 'POST' })
setStats(data)
await fetchPoolStatus()
} catch (e) {
console.error('刷新统计失败:', e)
}
setRefreshing(false)
}, [backendUrl, fetchPoolStatus])
}, [fetchPoolStatus])
// 设置目标
const handleSetTarget = async () => {
setLoading(true)
try {
await fetch(`${backendUrl}/api/pool/target`, {
await requestS2A(`${proxyBase}/api/pool/target`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -131,7 +132,7 @@ export default function Monitor() {
const handleTogglePolling = async () => {
setLoading(true)
try {
await fetch(`${backendUrl}/api/pool/polling`, {
await requestS2A(`${proxyBase}/api/pool/polling`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -151,19 +152,18 @@ export default function Monitor() {
const handleHealthCheck = async (autoPause: boolean = false) => {
setCheckingHealth(true)
try {
await fetch(`${backendUrl}/api/health-check/start`, {
await requestS2A(`${proxyBase}/api/health-check/start`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ auto_pause: autoPause }),
})
// 等待一会儿再获取结果
setTimeout(async () => {
const res = await fetch(`${backendUrl}/api/health-check/results`)
if (res.ok) {
const data = await res.json()
if (data.code === 0) {
setHealthResults(data.data || [])
}
try {
const data = await requestS2A(`${proxyBase}/api/health-check/results`)
setHealthResults(data || [])
} catch (e) {
console.error('获取健康检查结果失败:', e)
}
setCheckingHealth(false)
}, 5000)
@@ -176,13 +176,8 @@ export default function Monitor() {
// 获取自动补号日志
const fetchAutoAddLogs = async () => {
try {
const res = await fetch(`${backendUrl}/api/auto-add/logs`)
if (res.ok) {
const data = await res.json()
if (data.code === 0) {
setAutoAddLogs(data.data || [])
}
}
const data = await requestS2A(`${proxyBase}/api/auto-add/logs`)
setAutoAddLogs(data || [])
} catch (e) {
console.error('获取日志失败:', e)
}
@@ -560,10 +555,10 @@ export default function Monitor() {
<div
key={idx}
className={`flex items-center justify-between p-3 rounded-lg text-sm ${log.action.includes('trigger') || log.action.includes('decrease')
? 'bg-orange-50 dark:bg-orange-900/20'
: log.action.includes('increase')
? 'bg-green-50 dark:bg-green-900/20'
: 'bg-slate-50 dark:bg-slate-800/50'
? 'bg-orange-50 dark:bg-orange-900/20'
: log.action.includes('increase')
? 'bg-green-50 dark:bg-green-900/20'
: 'bg-slate-50 dark:bg-slate-800/50'
}`}
>
<div className="flex items-center gap-3">