feat: Implement DeepSeek account validation, testing, and queue status monitoring with corresponding admin API endpoints.

This commit is contained in:
CJACK
2026-02-01 03:10:26 +08:00
parent bc260899c1
commit 5ac472626e
9 changed files with 807 additions and 283 deletions

View File

@@ -1,4 +1,4 @@
import { useState } from 'react'
import { useState, useEffect } from 'react'
export default function AccountManager({ config, onRefresh, onMessage }) {
const [showAddKey, setShowAddKey] = useState(false)
@@ -6,6 +6,30 @@ export default function AccountManager({ config, onRefresh, onMessage }) {
const [newKey, setNewKey] = useState('')
const [newAccount, setNewAccount] = useState({ email: '', mobile: '', password: '' })
const [loading, setLoading] = useState(false)
const [validating, setValidating] = useState({}) // 单个账号验证状态
const [validatingAll, setValidatingAll] = useState(false)
const [testing, setTesting] = useState({}) // 单个账号测试状态
const [testingAll, setTestingAll] = useState(false)
const [queueStatus, setQueueStatus] = useState(null)
// 获取队列状态
const fetchQueueStatus = async () => {
try {
const res = await fetch('/admin/queue/status')
if (res.ok) {
const data = await res.json()
setQueueStatus(data)
}
} catch (e) {
console.error('获取队列状态失败:', e)
}
}
useEffect(() => {
fetchQueueStatus()
const interval = setInterval(fetchQueueStatus, 5000) // 每5秒刷新
return () => clearInterval(interval)
}, [])
const addKey = async () => {
if (!newKey.trim()) return
@@ -90,8 +114,115 @@ export default function AccountManager({ config, onRefresh, onMessage }) {
}
}
// 验证单个账号
const validateAccount = async (identifier) => {
setValidating(prev => ({ ...prev, [identifier]: true }))
try {
const res = await fetch('/admin/accounts/validate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ identifier }),
})
const data = await res.json()
if (data.valid) {
onMessage('success', `${identifier}: ${data.message}`)
} else {
onMessage('error', `${identifier}: ${data.message}`)
}
onRefresh()
} catch (e) {
onMessage('error', '验证失败: ' + e.message)
} finally {
setValidating(prev => ({ ...prev, [identifier]: false }))
}
}
// 批量验证所有账号
const validateAllAccounts = async () => {
if (!confirm('确定要验证所有账号?这可能需要一些时间。')) return
setValidatingAll(true)
try {
const res = await fetch('/admin/accounts/validate-all', { method: 'POST' })
const data = await res.json()
onMessage('success', `验证完成: ${data.valid}/${data.total} 个账号有效`)
onRefresh()
} catch (e) {
onMessage('error', '批量验证失败: ' + e.message)
} finally {
setValidatingAll(false)
}
}
// 测试单个账号 API
const testAccount = async (identifier) => {
setTesting(prev => ({ ...prev, [identifier]: true }))
try {
const res = await fetch('/admin/accounts/test', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ identifier }),
})
const data = await res.json()
if (data.success) {
onMessage('success', `${identifier}: API 测试成功 (${data.response_time}ms)`)
} else {
onMessage('error', `${identifier}: ${data.message}`)
}
onRefresh()
} catch (e) {
onMessage('error', 'API 测试失败: ' + e.message)
} finally {
setTesting(prev => ({ ...prev, [identifier]: false }))
}
}
// 批量测试所有账号 API
const testAllAccounts = async () => {
if (!confirm('确定要测试所有账号的 API这可能需要较长时间。')) return
setTestingAll(true)
try {
const res = await fetch('/admin/accounts/test-all', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({}),
})
const data = await res.json()
onMessage('success', `API 测试完成: ${data.success}/${data.total} 个账号可用`)
onRefresh()
} catch (e) {
onMessage('error', '批量 API 测试失败: ' + e.message)
} finally {
setTestingAll(false)
}
}
return (
<div className="section">
{/* 队列状态监控 */}
{queueStatus && (
<div className="card">
<div className="card-header">
<span className="card-title">📊 轮询队列状态</span>
<button className="btn btn-secondary" onClick={fetchQueueStatus}>刷新</button>
</div>
<div className="queue-status">
<div className="stat-row">
<span className="stat-label">可用账号:</span>
<span className="stat-value stat-success">{queueStatus.available}</span>
<span className="stat-label" style={{ marginLeft: '20px' }}>使用中:</span>
<span className="stat-value stat-warning">{queueStatus.in_use}</span>
<span className="stat-label" style={{ marginLeft: '20px' }}>总计:</span>
<span className="stat-value">{queueStatus.total}</span>
</div>
{queueStatus.in_use > 0 && (
<div className="stat-detail">
正在使用: {queueStatus.in_use_accounts.join(', ')}
</div>
)}
</div>
</div>
)}
{/* API Keys */}
<div className="card">
<div className="card-header">
@@ -117,22 +248,57 @@ export default function AccountManager({ config, onRefresh, onMessage }) {
<div className="card">
<div className="card-header">
<span className="card-title">👤 DeepSeek 账号</span>
<button className="btn btn-primary" onClick={() => setShowAddAccount(true)}>+ 添加</button>
<div className="btn-group-inline">
<button
className="btn btn-primary btn-sm"
onClick={testAllAccounts}
disabled={testingAll || validatingAll || !config.accounts?.length}
>
{testingAll ? <span className="loading"></span> : '🧪 批量测试'}
</button>
<button
className="btn btn-secondary btn-sm"
onClick={validateAllAccounts}
disabled={validatingAll || testingAll || !config.accounts?.length}
>
{validatingAll ? <span className="loading"></span> : '✅ 批量验证'}
</button>
<button className="btn btn-primary" onClick={() => setShowAddAccount(true)}>+ 添加</button>
</div>
</div>
{config.accounts?.length > 0 ? (
<div className="list">
{config.accounts.map((acc, i) => (
<div key={i} className="list-item">
<div className="list-item-info">
<span className="list-item-text">{acc.email || acc.mobile}</span>
<span className={`badge ${acc.has_token ? 'badge-success' : 'badge-warning'}`}>
{acc.has_token ? '已登录' : '未登录'}
</span>
{config.accounts.map((acc, i) => {
const id = acc.email || acc.mobile
return (
<div key={i} className="list-item">
<div className="list-item-info">
<span className="list-item-text">{id}</span>
<span className={`badge ${acc.has_token ? 'badge-success' : 'badge-warning'}`}>
{acc.has_token ? '已登录' : '未登录'}
</span>
</div>
<div className="btn-group-inline">
<button
className="btn btn-primary btn-sm"
onClick={() => testAccount(id)}
disabled={testing[id]}
>
{testing[id] ? <span className="loading"></span> : '测试'}
</button>
<button
className="btn btn-secondary btn-sm"
onClick={() => validateAccount(id)}
disabled={validating[id]}
>
{validating[id] ? <span className="loading"></span> : '验证'}
</button>
<button className="btn btn-danger btn-sm" onClick={() => deleteAccount(id)}>删除</button>
</div>
</div>
<button className="btn btn-danger" onClick={() => deleteAccount(acc.email || acc.mobile)}>删除</button>
</div>
))}
)
})}
</div>
) : (
<div className="empty-state">暂无账号</div>

View File

@@ -1,10 +1,10 @@
import { useState } from 'react'
const MODELS = [
{ id: 'deepseek-chat', name: 'DeepSeek V3 (Chat)' },
{ id: 'deepseek-reasoner', name: 'DeepSeek R1 (Reasoner)' },
{ id: 'deepseek-chat-search', name: 'DeepSeek V3 + 搜索' },
{ id: 'deepseek-reasoner-search', name: 'DeepSeek R1 + 搜索' },
{ id: 'deepseek-chat', name: 'deepseek-chat' },
{ id: 'deepseek-reasoner', name: 'deepseek-reasoner' },
{ id: 'deepseek-chat-search', name: 'deepseek-chat-search' },
{ id: 'deepseek-reasoner-search', name: 'deepseek-reasoner-search' },
]
export default function ApiTester({ config, onMessage }) {