feat: Add a new monitoring dashboard for S2A account pool management with configurable settings and status display.
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"codex-pool/internal/database"
|
"codex-pool/internal/database"
|
||||||
|
"codex-pool/internal/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MonitorSettings 监控设置
|
// 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_enabled", strconv.FormatBool(settings.PollingEnabled))
|
||||||
database.Instance.SetConfig("monitor_polling_interval", strconv.Itoa(settings.PollingInterval))
|
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{}{
|
Success(w, map[string]interface{}{
|
||||||
"message": "设置已保存",
|
"message": "设置已保存",
|
||||||
"settings": settings,
|
"settings": settings,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
Clock,
|
Clock,
|
||||||
|
Save,
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
import { Card, CardHeader, CardTitle, CardContent, Button, Input } from '../components/common'
|
import { Card, CardHeader, CardTitle, CardContent, Button, Input } from '../components/common'
|
||||||
import type { DashboardStats } from '../types'
|
import type { DashboardStats } from '../types'
|
||||||
@@ -194,6 +195,29 @@ export default function Monitor() {
|
|||||||
setLoading(false)
|
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 没有此接口,显示提示
|
// 健康检查 - S2A 没有此接口,显示提示
|
||||||
const handleHealthCheck = async (_autoPause: boolean = false) => {
|
const handleHealthCheck = async (_autoPause: boolean = false) => {
|
||||||
setCheckingHealth(true)
|
setCheckingHealth(true)
|
||||||
@@ -471,20 +495,27 @@ export default function Monitor() {
|
|||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent className="space-y-4">
|
||||||
<Input
|
<div className="w-full">
|
||||||
label="轮询间隔 (秒)"
|
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1">
|
||||||
|
轮询间隔 (秒)
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
min={10}
|
min={10}
|
||||||
max={300}
|
max={600}
|
||||||
value={pollingInterval}
|
value={pollingInterval}
|
||||||
onChange={(e) => {
|
onChange={(e) => setPollingInterval(Number(e.target.value) || 60)}
|
||||||
const val = parseInt(e.target.value, 10)
|
className="w-full px-3 py-2 text-sm rounded-lg border transition-colors
|
||||||
if (!isNaN(val)) {
|
bg-white dark:bg-slate-800
|
||||||
setPollingInterval(val)
|
text-slate-900 dark:text-slate-100
|
||||||
}
|
border-slate-300 dark:border-slate-600
|
||||||
}}
|
focus:border-blue-500 focus:ring-blue-500
|
||||||
hint="自动刷新号池状态的间隔时间 (10-300秒)"
|
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="p-4 rounded-lg bg-slate-50 dark:bg-slate-800/50">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
@@ -496,15 +527,26 @@ export default function Monitor() {
|
|||||||
<div className={`status-dot ${pollingEnabled ? 'online' : 'offline'}`} />
|
<div className={`status-dot ${pollingEnabled ? 'online' : 'offline'}`} />
|
||||||
</div>
|
</div>
|
||||||
</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
|
<Button
|
||||||
onClick={handleTogglePolling}
|
onClick={handleTogglePolling}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
variant={pollingEnabled ? 'outline' : 'primary'}
|
variant={pollingEnabled ? 'outline' : 'primary'}
|
||||||
className="w-full"
|
className="flex-1"
|
||||||
icon={pollingEnabled ? <Pause className="h-4 w-4" /> : <Play className="h-4 w-4" />}
|
icon={pollingEnabled ? <Pause className="h-4 w-4" /> : <Play className="h-4 w-4" />}
|
||||||
>
|
>
|
||||||
{pollingEnabled ? '停止监控' : '启动监控'}
|
{pollingEnabled ? '停止监控' : '启动监控'}
|
||||||
</Button>
|
</Button>
|
||||||
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user