feat: Implement account pool monitoring and management dashboard with S2A integration and add a new upload page.

This commit is contained in:
2026-02-06 21:54:37 +08:00
parent 1458b8b3e2
commit f97a9beb4b
5 changed files with 72 additions and 36 deletions

View File

@@ -54,8 +54,8 @@ export default function Monitor() {
const [checkInterval, setCheckInterval] = useState(60)
const [pollingEnabled, setPollingEnabled] = useState(false)
const [pollingInterval, setPollingInterval] = useState(60)
const [replenishUseProxy, setReplenishUseProxy] = useState(false) // 补号时使用代理
const [globalProxy, setGlobalProxy] = useState('') // 全局代理地址(只读显示)
const [replenishUseProxy, setReplenishUseProxy] = useState(false) // 补号时使用代理
const [proxyPoolStats, setProxyPoolStats] = useState<{ total: number; enabled: number } | null>(null) // 代理池统计
const [authMethod, setAuthMethod] = useState<'api' | 'browser'>('browser') // 授权方式
// 自动补号配置
@@ -238,12 +238,16 @@ export default function Monitor() {
// 从后端加载监控设置
const loadMonitorSettings = async () => {
try {
// 加载全局代理配置(始终执行)
const configRes = await fetch('/api/config')
// 加载代理池统计和全局配置(并行)
const [configRes, proxyStatsRes, settingsRes] = await Promise.all([
fetch('/api/config'),
fetch('/api/codex-proxy/stats'),
fetch('/api/monitor/settings'),
])
if (configRes.ok) {
const configJson = await configRes.json()
if (configJson.code === 0 && configJson.data) {
setGlobalProxy(configJson.data.default_proxy || '')
// 加载授权方式配置
if (configJson.data.auth_method) {
setAuthMethod(configJson.data.auth_method === 'api' ? 'api' : 'browser')
@@ -251,10 +255,16 @@ export default function Monitor() {
}
}
if (proxyStatsRes.ok) {
const proxyJson = await proxyStatsRes.json()
if (proxyJson.code === 0 && proxyJson.data) {
setProxyPoolStats(proxyJson.data)
}
}
// 加载监控设置
const res = await fetch('/api/monitor/settings')
if (res.ok) {
const json = await res.json()
if (settingsRes.ok) {
const json = await settingsRes.json()
if (json.code === 0 && json.data) {
const s = json.data
const target = s.target || 50
@@ -525,12 +535,12 @@ export default function Monitor() {
<Switch
checked={replenishUseProxy}
onChange={setReplenishUseProxy}
disabled={!autoAdd || !globalProxy}
label="补号时使用代理"
disabled={!autoAdd || !proxyPoolStats || proxyPoolStats.enabled === 0}
label="补号时使用代理"
description={
globalProxy
? `当前代理: ${globalProxy}`
: '请先在系统配置中设置代理地址(支持 http://host:port、http://user:pass@host:port、host:port:user:pass'
proxyPoolStats && proxyPoolStats.enabled > 0
? `代理: ${proxyPoolStats.enabled} 个可用 / ${proxyPoolStats.total} 个总计`
: '代理池为空,请先在"代理池配置"页面添加代理'
}
/>
</div>
@@ -757,10 +767,10 @@ export default function Monitor() {
type="checkbox"
checked={autoRegUseProxy}
onChange={(e) => setAutoRegUseProxy(e.target.checked)}
disabled={!globalProxy}
disabled={!proxyPoolStats || proxyPoolStats.enabled === 0}
className="h-4 w-4 rounded border-slate-300 text-purple-600 focus:ring-purple-500 disabled:opacity-50"
/>
<span>使</span>
<span>使{proxyPoolStats && proxyPoolStats.enabled > 0 ? ` (${proxyPoolStats.enabled})` : ''}</span>
</label>
</div>
</div>

View File

@@ -72,28 +72,33 @@ export default function Upload() {
const [membersPerTeam, setMembersPerTeam] = useState(4)
const [concurrentTeams, setConcurrentTeams] = useState(2)
const [concurrentS2A, setConcurrentS2A] = useState(2) // 入库并发数
const [useProxy, setUseProxy] = useState(false) // 是否使用全局代理
const [useProxy, setUseProxy] = useState(false) // 是否使用代理
const [includeOwner, setIncludeOwner] = useState(false) // 母号也入库
const [processCount, setProcessCount] = useState(0) // 处理数量0表示全部
const [authMethod, setAuthMethod] = useState<'api' | 'browser'>('browser') // 授权方式
const [proxyPoolStats, setProxyPoolStats] = useState<{ total: number; enabled: number } | null>(null)
// 获取全局代理地址
const globalProxy = config.proxy?.default || ''
// 加载全局配置中的授权方式
// 加载全局配置中的授权方式和代理池统计
useEffect(() => {
const fetchAuthMethod = async () => {
const fetchConfig = async () => {
try {
const res = await fetch('/api/config')
const data = await res.json()
if (data.code === 0 && data.data?.auth_method) {
setAuthMethod(data.data.auth_method === 'api' ? 'api' : 'browser')
const [configRes, proxyRes] = await Promise.all([
fetch('/api/config'),
fetch('/api/codex-proxy/stats'),
])
const configData = await configRes.json()
if (configData.code === 0 && configData.data?.auth_method) {
setAuthMethod(configData.data.auth_method === 'api' ? 'api' : 'browser')
}
const proxyData = await proxyRes.json()
if (proxyData.code === 0 && proxyData.data) {
setProxyPoolStats(proxyData.data)
}
} catch (error) {
console.error('Failed to fetch auth method:', error)
console.error('Failed to fetch config:', error)
}
}
fetchAuthMethod()
fetchConfig()
}, [])
const hasConfig = config.s2a.apiBase && config.s2a.adminKey
@@ -214,7 +219,7 @@ export default function Upload() {
concurrent_s2a: concurrentS2A, // 入库并发数
browser_type: 'chromedp', // 固定使用 chromedp
headless: true, // 始终使用无头模式
proxy: useProxy ? globalProxy : '',
proxy: useProxy ? 'pool:random' : '',
include_owner: includeOwner, // 母号也入库
process_count: processCount, // 处理数量0表示全部
}),
@@ -232,7 +237,7 @@ export default function Upload() {
alert('启动失败')
}
setLoading(false)
}, [stats, membersPerTeam, concurrentTeams, concurrentS2A, useProxy, globalProxy, includeOwner, processCount, fetchStatus])
}, [stats, membersPerTeam, concurrentTeams, concurrentS2A, useProxy, includeOwner, processCount, fetchStatus])
// 停止处理
const handleStop = useCallback(async () => {
@@ -508,12 +513,12 @@ export default function Upload() {
<Switch
checked={useProxy}
onChange={setUseProxy}
disabled={isRunning || !globalProxy}
label="使用全局代理"
disabled={isRunning || !proxyPoolStats || proxyPoolStats.enabled === 0}
label="使用代理"
description={
globalProxy
? `当前代理: ${globalProxy}`
: '请先在系统配置中设置代理地址(支持 http://host:port、http://user:pass@host:port、host:port:user:pass'
proxyPoolStats && proxyPoolStats.enabled > 0
? `代理: ${proxyPoolStats.enabled} 个可用 / ${proxyPoolStats.total} 个总计`
: '代理池为空,请先在"代理池配置"页面添加代理'
}
/>
</div>