feat: add monitoring feature with backend API and frontend UI for settings.
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user