feat: Add a new monitoring dashboard for S2A account pool management with configurable settings and status display.

This commit is contained in:
2026-01-30 12:14:33 +08:00
parent b9c5b42f4e
commit 5987a425ec
2 changed files with 71 additions and 23 deletions

View File

@@ -6,6 +6,7 @@ import (
"strconv"
"codex-pool/internal/database"
"codex-pool/internal/logger"
)
// MonitorSettings 监控设置
@@ -87,6 +88,11 @@ func HandleSaveMonitorSettings(w http.ResponseWriter, r *http.Request) {
database.Instance.SetConfig("monitor_polling_enabled", strconv.FormatBool(settings.PollingEnabled))
database.Instance.SetConfig("monitor_polling_interval", strconv.Itoa(settings.PollingInterval))
// 输出日志
logger.Info("监控设置已保存: target="+strconv.Itoa(settings.Target)+
", polling="+strconv.FormatBool(settings.PollingEnabled)+
", interval="+strconv.Itoa(settings.PollingInterval)+"s", "", "monitor")
Success(w, map[string]interface{}{
"message": "设置已保存",
"settings": settings,

View File

@@ -12,6 +12,7 @@ import {
AlertTriangle,
CheckCircle,
Clock,
Save,
} from 'lucide-react'
import { Card, CardHeader, CardTitle, CardContent, Button, Input } from '../components/common'
import type { DashboardStats } from '../types'
@@ -194,6 +195,29 @@ export default function Monitor() {
setLoading(false)
}
// 保存轮询设置(不切换状态)
const handleSavePollingSettings = async () => {
setLoading(true)
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: pollingEnabled,
polling_interval: pollingInterval,
}),
})
// 重置倒计时
setCountdown(pollingInterval)
} catch (e) {
console.error('保存轮询设置失败:', e)
}
setLoading(false)
}
// 健康检查 - S2A 没有此接口,显示提示
const handleHealthCheck = async (_autoPause: boolean = false) => {
setCheckingHealth(true)
@@ -471,20 +495,27 @@ export default function Monitor() {
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<Input
label="轮询间隔 (秒)"
<div className="w-full">
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1">
()
</label>
<input
type="number"
min={10}
max={300}
max={600}
value={pollingInterval}
onChange={(e) => {
const val = parseInt(e.target.value, 10)
if (!isNaN(val)) {
setPollingInterval(val)
}
}}
hint="自动刷新号池状态的间隔时间 (10-300秒)"
onChange={(e) => setPollingInterval(Number(e.target.value) || 60)}
className="w-full px-3 py-2 text-sm rounded-lg border transition-colors
bg-white dark:bg-slate-800
text-slate-900 dark:text-slate-100
border-slate-300 dark:border-slate-600
focus:border-blue-500 focus:ring-blue-500
focus:outline-none focus:ring-2"
/>
<p className="mt-1 text-sm text-slate-500 dark:text-slate-400">
(10-600)
</p>
</div>
<div className="p-4 rounded-lg bg-slate-50 dark:bg-slate-800/50">
<div className="flex items-center justify-between">
<div>
@@ -496,15 +527,26 @@ export default function Monitor() {
<div className={`status-dot ${pollingEnabled ? 'online' : 'offline'}`} />
</div>
</div>
<div className="flex gap-3">
<Button
onClick={handleSavePollingSettings}
loading={loading}
variant="outline"
className="flex-1"
icon={<Save className="h-4 w-4" />}
>
</Button>
<Button
onClick={handleTogglePolling}
loading={loading}
variant={pollingEnabled ? 'outline' : 'primary'}
className="w-full"
className="flex-1"
icon={pollingEnabled ? <Pause className="h-4 w-4" /> : <Play className="h-4 w-4" />}
>
{pollingEnabled ? '停止监控' : '启动监控'}
</Button>
</div>
</CardContent>
</Card>
</div>