Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -1234,9 +1234,18 @@ async def create_accounts_feed(request: BatchAccountCreate, _: bool = Depends(ve
}

@app.get("/v2/accounts")
async def list_accounts(_: bool = Depends(verify_admin_password)):
rows = await _db.fetchall("SELECT * FROM accounts ORDER BY created_at DESC")
return [_row_to_dict(r) for r in rows]
async def list_accounts(_: bool = Depends(verify_admin_password), enabled: Optional[bool] = None, sort_by: str = "created_at", sort_order: str = "desc"):
query = "SELECT * FROM accounts"
params = []
if enabled is not None:
query += " WHERE enabled=?"
params.append(1 if enabled else 0)
sort_field = "created_at" if sort_by not in ["created_at", "success_count"] else sort_by
order = "DESC" if sort_order.lower() == "desc" else "ASC"
query += f" ORDER BY {sort_field} {order}"
rows = await _db.fetchall(query, tuple(params) if params else ())
accounts = [_row_to_dict(r) for r in rows]
return {"accounts": accounts, "count": len(accounts)}

@app.get("/v2/accounts/{account_id}")
async def get_account_detail(account_id: str, _: bool = Depends(verify_admin_password)):
Expand Down
36 changes: 33 additions & 3 deletions frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,28 @@ <h1>Q2api 前端控制台</h1>

<div id="tab-accounts" class="tab-content active">
<div class="panel">
<h2>账号管理</h2>
<h2>账号管理 <span id="accountCount" style="font-size: 0.8em; color: #666;"></span></h2>
<div class="row">
<button class="btn-secondary" onclick="loadAccounts()">刷新列表</button>
<div style="margin-left: 20px;">
<label><input type="radio" name="accountFilter" value="all" checked onchange="loadAccounts()"> 全部</label>
<label style="margin-left: 10px;"><input type="radio" name="accountFilter" value="enabled" onchange="loadAccounts()"> 已启用</label>
<label style="margin-left: 10px;"><input type="radio" name="accountFilter" value="disabled" onchange="loadAccounts()"> 已禁用</label>
</div>
<div style="margin-left: 20px;">
<label>排序:
<select id="sortBy" onchange="loadAccounts()">
<option value="created_at">创建日期</option>
<option value="success_count">成功次数</option>
</select>
</label>
<label style="margin-left: 10px;">
<select id="sortOrder" onchange="loadAccounts()">
<option value="desc">降序</option>
<option value="asc">升序</option>
</select>
</label>
</div>
</div>
<div class="list" id="accounts"></div>
</div>
Expand Down Expand Up @@ -628,9 +647,20 @@ <h2>Chat 测试(OpenAI 兼容 /v1/chat/completions)</h2>

async function loadAccounts(){
try{
const r = await authFetch(api('/v2/accounts'));
const filter = document.querySelector('input[name="accountFilter"]:checked')?.value || 'all';
const sortBy = document.getElementById('sortBy')?.value || 'created_at';
const sortOrder = document.getElementById('sortOrder')?.value || 'desc';
let url = '/v2/accounts?';
const params = [];
if (filter === 'enabled') params.push('enabled=true');
else if (filter === 'disabled') params.push('enabled=false');
params.push(`sort_by=${sortBy}`);
params.push(`sort_order=${sortOrder}`);
url += params.join('&');
const r = await authFetch(api(url));
const j = await r.json();
renderAccounts(j);
document.getElementById('accountCount').textContent = `(${j.count})`;
renderAccounts(j.accounts);
} catch(e){
alert('加载账户失败:' + e);
}
Expand Down