feat: add monitoring feature with backend API and frontend UI for settings.

This commit is contained in:
2026-01-30 11:34:06 +08:00
parent 55a0e46487
commit 59441adc99
3 changed files with 185 additions and 7 deletions

View File

@@ -58,13 +58,16 @@ export default function Monitor() {
const [checkingHealth, setCheckingHealth] = useState(false)
const [autoPauseEnabled, setAutoPauseEnabled] = useState(false)
// 配置表单状态
// 配置表单状态 - 从后端 SQLite 加载
const [targetInput, setTargetInput] = useState(50)
const [autoAdd, setAutoAdd] = useState(false)
const [minInterval, setMinInterval] = useState(300)
const [pollingEnabled, setPollingEnabled] = useState(false)
const [pollingInterval, setPollingInterval] = useState(60)
// 倒计时状态
const [countdown, setCountdown] = useState(60)
// 使用后端 S2A 代理访问 S2A 服务器
const proxyBase = '/api/s2a/proxy'
@@ -128,10 +131,29 @@ export default function Monitor() {
setRefreshing(false)
}, [targetInput, autoAdd, minInterval, pollingEnabled, pollingInterval])
// 设置目标 - 本地状态管理S2A 没有此 API
// 设置目标 - 保存到后端 SQLite
const handleSetTarget = async () => {
setLoading(true)
// 由于 S2A 没有 pool/target API这里只更新本地状态
try {
// 保存到后端数据库
const res = await fetch('/api/monitor/settings/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
target: targetInput,
auto_add: autoAdd,
min_interval: minInterval,
polling_enabled: pollingEnabled,
polling_interval: pollingInterval,
}),
})
if (!res.ok) {
console.error('保存设置失败:', res.status)
}
} catch (e) {
console.error('保存设置失败:', e)
}
// 更新本地状态
setPoolStatus(prev => prev ? {
...prev,
target: targetInput,
@@ -144,13 +166,29 @@ export default function Monitor() {
setLoading(false)
}
// 控制轮询 - 本地状态管理
// 控制轮询 - 保存到后端 SQLite
const handleTogglePolling = async () => {
setLoading(true)
setPollingEnabled(!pollingEnabled)
const newPollingEnabled = !pollingEnabled
setPollingEnabled(newPollingEnabled)
try {
await fetch('/api/monitor/settings/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
target: targetInput,
auto_add: autoAdd,
min_interval: minInterval,
polling_enabled: newPollingEnabled,
polling_interval: pollingInterval,
}),
})
} catch (e) {
console.error('保存轮询设置失败:', e)
}
setPoolStatus(prev => prev ? {
...prev,
polling_enabled: !pollingEnabled,
polling_enabled: newPollingEnabled,
polling_interval: pollingInterval,
} : null)
setLoading(false)
@@ -186,13 +224,55 @@ export default function Monitor() {
setAutoAddLogs([])
}
// 从后端加载监控设置
const loadMonitorSettings = async () => {
try {
const res = await fetch('/api/monitor/settings')
if (res.ok) {
const json = await res.json()
if (json.code === 0 && json.data) {
const s = json.data
setTargetInput(s.target || 50)
setAutoAdd(s.auto_add || false)
setMinInterval(s.min_interval || 300)
setPollingEnabled(s.polling_enabled || false)
setPollingInterval(s.polling_interval || 60)
}
}
} catch (e) {
console.error('加载监控设置失败:', e)
}
}
// 初始化
useEffect(() => {
loadMonitorSettings()
fetchPoolStatus()
refreshStats()
fetchAutoAddLogs()
}, [fetchPoolStatus, refreshStats])
// 倒计时定时器 - 当启用轮询时
useEffect(() => {
// 初始化倒计时
setCountdown(pollingInterval)
if (!pollingEnabled) return
const timer = setInterval(() => {
setCountdown(prev => {
if (prev <= 1) {
// 倒计时结束,刷新数据并重置
refreshStats()
return pollingInterval
}
return prev - 1
})
}, 1000)
return () => clearInterval(timer)
}, [pollingEnabled, pollingInterval, refreshStats])
// 计算健康状态
const healthySummary = healthResults.reduce(
(acc, r) => {
@@ -309,7 +389,7 @@ export default function Monitor() {
{pollingEnabled && (
<p className="mt-2 text-xs text-slate-500 flex items-center gap-1">
<Clock className="h-3 w-3" />
{pollingInterval}
<span className="font-mono text-green-500">{countdown}s</span> ( {pollingInterval} )
</p>
)}
</CardContent>