diff --git a/routes/admin/config.py b/routes/admin/config.py index d0739a5..5a8471a 100644 --- a/routes/admin/config.py +++ b/routes/admin/config.py @@ -122,6 +122,49 @@ async def delete_key(key: str, _: bool = Depends(verify_admin)): # ---------------------------------------------------------------------- # 账号管理 # ---------------------------------------------------------------------- +@router.get("/accounts") +async def list_accounts( + page: int = 1, + page_size: int = 10, + _: bool = Depends(verify_admin) +): + """获取账号列表(分页,倒序,密码脱敏)""" + accounts = CONFIG.get("accounts", []) + total = len(accounts) + + # 倒序排列 + accounts = list(reversed(accounts)) + + # 计算分页 + page = max(1, page) + page_size = max(1, min(100, page_size)) # 限制每页最多 100 条 + total_pages = (total + page_size - 1) // page_size if total > 0 else 1 + + start = (page - 1) * page_size + end = start + page_size + page_accounts = accounts[start:end] + + # 脱敏处理 + safe_accounts = [] + for acc in page_accounts: + safe_acc = { + "email": acc.get("email", ""), + "mobile": acc.get("mobile", ""), + "has_password": bool(acc.get("password")), + "has_token": bool(acc.get("token")), + "token_preview": acc.get("token", "")[:20] + "..." if acc.get("token") else "", + } + safe_accounts.append(safe_acc) + + return JSONResponse(content={ + "items": safe_accounts, + "total": total, + "page": page, + "page_size": page_size, + "total_pages": total_pages, + }) + + @router.post("/accounts") async def add_account(request: Request, _: bool = Depends(verify_admin)): """添加账号""" diff --git a/webui/src/components/AccountManager.jsx b/webui/src/components/AccountManager.jsx index 25aa8c1..773b84e 100644 --- a/webui/src/components/AccountManager.jsx +++ b/webui/src/components/AccountManager.jsx @@ -8,7 +8,10 @@ import { Server, ShieldCheck, Copy, - Check + Check, + ChevronLeft, + ChevronRight, + ChevronDown } from 'lucide-react' import clsx from 'clsx' import { useI18n } from '../i18n' @@ -25,9 +28,36 @@ export default function AccountManager({ config, onRefresh, onMessage, authFetch const [testingAll, setTestingAll] = useState(false) const [batchProgress, setBatchProgress] = useState({ current: 0, total: 0, results: [] }) const [queueStatus, setQueueStatus] = useState(null) + const [keysExpanded, setKeysExpanded] = useState(false) + + // 分页状态 + const [accounts, setAccounts] = useState([]) + const [page, setPage] = useState(1) + const [pageSize] = useState(10) + const [totalPages, setTotalPages] = useState(1) + const [totalAccounts, setTotalAccounts] = useState(0) + const [loadingAccounts, setLoadingAccounts] = useState(false) const apiFetch = authFetch || fetch + const fetchAccounts = async (targetPage = page) => { + setLoadingAccounts(true) + try { + const res = await apiFetch(`/admin/accounts?page=${targetPage}&page_size=${pageSize}`) + if (res.ok) { + const data = await res.json() + setAccounts(data.items || []) + setTotalPages(data.total_pages || 1) + setTotalAccounts(data.total || 0) + setPage(data.page || 1) + } + } catch (e) { + console.error('Failed to fetch accounts:', e) + } finally { + setLoadingAccounts(false) + } + } + const fetchQueueStatus = async () => { try { const res = await apiFetch('/admin/queue/status') @@ -41,6 +71,7 @@ export default function AccountManager({ config, onRefresh, onMessage, authFetch } useEffect(() => { + fetchAccounts() fetchQueueStatus() const interval = setInterval(fetchQueueStatus, 5000) return () => clearInterval(interval) @@ -102,6 +133,7 @@ export default function AccountManager({ config, onRefresh, onMessage, authFetch onMessage('success', t('accountManager.addAccountSuccess')) setNewAccount({ email: '', mobile: '', password: '' }) setShowAddAccount(false) + fetchAccounts(1) // 添加后回到第一页 onRefresh() } else { const data = await res.json() @@ -120,6 +152,7 @@ export default function AccountManager({ config, onRefresh, onMessage, authFetch const res = await apiFetch(`/admin/accounts/${encodeURIComponent(id)}`, { method: 'DELETE' }) if (res.ok) { onMessage('success', t('messages.deleted')) + fetchAccounts() // 刷新当前页 onRefresh() } else { onMessage('error', t('messages.deleteFailed')) @@ -142,6 +175,7 @@ export default function AccountManager({ config, onRefresh, onMessage, authFetch ? t('apiTester.testSuccess', { account: identifier, time: data.response_time }) : `${identifier}: ${data.message}` onMessage(data.success ? 'success' : 'error', statusMessage) + fetchAccounts() // 刷新当前页 onRefresh() } catch (e) { onMessage('error', t('accountManager.testFailed', { error: e.message })) @@ -152,17 +186,17 @@ export default function AccountManager({ config, onRefresh, onMessage, authFetch const testAllAccounts = async () => { if (!confirm(t('accountManager.testAllConfirm'))) return - const accounts = config.accounts || [] - if (accounts.length === 0) return + const allAccounts = config.accounts || [] + if (allAccounts.length === 0) return setTestingAll(true) - setBatchProgress({ current: 0, total: accounts.length, results: [] }) + setBatchProgress({ current: 0, total: allAccounts.length, results: [] }) let successCount = 0 const results = [] - for (let i = 0; i < accounts.length; i++) { - const acc = accounts[i] + for (let i = 0; i < allAccounts.length; i++) { + const acc = allAccounts[i] const id = acc.email || acc.mobile try { @@ -178,10 +212,11 @@ export default function AccountManager({ config, onRefresh, onMessage, authFetch results.push({ id, success: false, message: e.message }) } - setBatchProgress({ current: i + 1, total: accounts.length, results: [...results] }) + setBatchProgress({ current: i + 1, total: allAccounts.length, results: [...results] }) } - onMessage('success', t('accountManager.testAllCompleted', { success: successCount, total: accounts.length })) + onMessage('success', t('accountManager.testAllCompleted', { success: successCount, total: allAccounts.length })) + fetchAccounts() // 刷新当前页 onRefresh() setTestingAll(false) } @@ -228,13 +263,22 @@ export default function AccountManager({ config, onRefresh, onMessage, authFetch {/* API Keys Section */}
{t('accountManager.apiKeysDesc')}
+{t('accountManager.apiKeysDesc')} ({config.keys?.length || 0})
+