feat: Add token preview and batch progress to admin account management, and expand API tests for admin endpoints.

This commit is contained in:
CJACK
2026-02-01 03:31:01 +08:00
parent a6d89876ba
commit 23bd4970d9
5 changed files with 239 additions and 99 deletions

View File

@@ -10,6 +10,7 @@ export default function AccountManager({ config, onRefresh, onMessage }) {
const [validatingAll, setValidatingAll] = useState(false)
const [testing, setTesting] = useState({}) // 单个账号测试状态
const [testingAll, setTestingAll] = useState(false)
const [batchProgress, setBatchProgress] = useState({ current: 0, total: 0, results: [] })
const [queueStatus, setQueueStatus] = useState(null)
// 获取队列状态
@@ -137,20 +138,41 @@ export default function AccountManager({ config, onRefresh, onMessage }) {
}
}
// 批量验证所有账号
// 批量验证所有账号(带进度)
const validateAllAccounts = async () => {
if (!confirm('确定要验证所有账号?这可能需要一些时间。')) return
if (!confirm('确定要验证所有账号?')) return
const accounts = config.accounts || []
if (accounts.length === 0) 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)
setBatchProgress({ current: 0, total: accounts.length, results: [] })
let validCount = 0
const results = []
for (let i = 0; i < accounts.length; i++) {
const acc = accounts[i]
const id = acc.email || acc.mobile
try {
const res = await fetch('/admin/accounts/validate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ identifier: id }),
})
const data = await res.json()
results.push({ id, success: data.valid, message: data.message })
if (data.valid) validCount++
} catch (e) {
results.push({ id, success: false, message: e.message })
}
setBatchProgress({ current: i + 1, total: accounts.length, results: [...results] })
}
onMessage('success', `验证完成: ${validCount}/${accounts.length} 个账号有效`)
onRefresh()
setValidatingAll(false)
}
// 测试单个账号 API
@@ -176,24 +198,41 @@ export default function AccountManager({ config, onRefresh, onMessage }) {
}
}
// 批量测试所有账号 API
// 批量测试所有账号 API(带进度)
const testAllAccounts = async () => {
if (!confirm('确定要测试所有账号的 API这可能需要较长时间。')) return
if (!confirm('确定要测试所有账号的 API')) return
const accounts = config.accounts || []
if (accounts.length === 0) 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)
setBatchProgress({ current: 0, total: accounts.length, results: [] })
let successCount = 0
const results = []
for (let i = 0; i < accounts.length; i++) {
const acc = accounts[i]
const id = acc.email || acc.mobile
try {
const res = await fetch('/admin/accounts/test', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ identifier: id }),
})
const data = await res.json()
results.push({ id, success: data.success, message: data.message, time: data.response_time })
if (data.success) successCount++
} catch (e) {
results.push({ id, success: false, message: e.message })
}
setBatchProgress({ current: i + 1, total: accounts.length, results: [...results] })
}
onMessage('success', `API 测试完成: ${successCount}/${accounts.length} 个账号可用`)
onRefresh()
setTestingAll(false)
}
return (
@@ -267,6 +306,31 @@ export default function AccountManager({ config, onRefresh, onMessage }) {
</div>
</div>
{/* 批量操作进度条 */}
{(testingAll || validatingAll) && batchProgress.total > 0 && (
<div className="batch-progress">
<div className="progress-header">
<span>{testingAll ? '🧪 批量测试中...' : '✅ 批量验证中...'}</span>
<span>{batchProgress.current}/{batchProgress.total}</span>
</div>
<div className="progress-bar">
<div
className="progress-fill"
style={{ width: `${(batchProgress.current / batchProgress.total) * 100}%` }}
/>
</div>
{batchProgress.results.length > 0 && (
<div className="progress-results">
{batchProgress.results.map((r, i) => (
<div key={i} className={`progress-result ${r.success ? 'success' : 'failed'}`}>
{r.success ? '✓' : '✗'} {r.id} {r.time ? `(${r.time}ms)` : ''}
</div>
))}
</div>
)}
</div>
)}
{config.accounts?.length > 0 ? (
<div className="list">
{config.accounts.map((acc, i) => {
@@ -278,6 +342,11 @@ export default function AccountManager({ config, onRefresh, onMessage }) {
<span className={`badge ${acc.has_token ? 'badge-success' : 'badge-warning'}`}>
{acc.has_token ? '已登录' : '未登录'}
</span>
{acc.token_preview && (
<span className="token-preview" title="Token 预览">
🔑 {acc.token_preview}
</span>
)}
</div>
<div className="btn-group-inline">
<button

View File

@@ -446,6 +446,65 @@ textarea.form-input {
font-size: 0.8rem;
}
/* Token Preview */
.token-preview {
font-size: 0.75rem;
color: var(--text-secondary);
font-family: 'Monaco', 'Menlo', monospace;
background: var(--bg-tertiary);
padding: 0.2rem 0.5rem;
border-radius: var(--radius);
margin-left: 0.5rem;
}
/* Batch Progress */
.batch-progress {
margin: 1rem 0;
padding: 1rem;
background: var(--bg-tertiary);
border-radius: var(--radius);
}
.progress-header {
display: flex;
justify-content: space-between;
margin-bottom: 0.5rem;
font-size: 0.9rem;
}
.progress-bar {
height: 8px;
background: var(--bg-secondary);
border-radius: 4px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--primary), var(--primary-hover));
transition: width 0.3s ease;
}
.progress-results {
margin-top: 0.75rem;
max-height: 150px;
overflow-y: auto;
font-size: 0.85rem;
}
.progress-result {
padding: 0.25rem 0;
font-family: 'Monaco', 'Menlo', monospace;
}
.progress-result.success {
color: var(--success);
}
.progress-result.failed {
color: var(--error);
}
@media (max-width: 640px) {
.app {
padding: 1rem;