refactor(routes): #13 优化 Sub2API 导出格式为合并数组

This commit is contained in:
cnlimiter
2026-03-18 12:02:29 +08:00
parent e2e5c4a232
commit 7ce403ece3

View File

@@ -412,44 +412,40 @@ async def export_accounts_csv(request: BatchExportRequest):
@router.post("/export/sub2api") @router.post("/export/sub2api")
async def export_accounts_sub2api(request: BatchExportRequest): async def export_accounts_sub2api(request: BatchExportRequest):
"""导出账号为 Sub2Api 格式(每个账号单独一个 JSON 文件,多个打包为 ZIP""" """导出账号为 Sub2Api 格式(所有选中账号合并到一个 JSON 的 accounts 数组中"""
import io
import zipfile
def make_sub2api_json(acc) -> dict: def make_account_entry(acc) -> dict:
expires_at = int(acc.expires_at.timestamp()) if acc.expires_at else 0 expires_at = int(acc.expires_at.timestamp()) if acc.expires_at else 0
return { return {
"proxies": [], "name": acc.email,
"accounts": [ "platform": "openai",
{ "type": "oauth",
"name": acc.email, "credentials": {
"platform": "openai", "access_token": acc.access_token or "",
"type": "oauth", "chatgpt_account_id": acc.account_id or "",
"credentials": { "chatgpt_user_id": "",
"access_token": acc.access_token or "", "client_id": acc.client_id or "",
"chatgpt_account_id": acc.account_id or "", "expires_at": expires_at,
"chatgpt_user_id": "", "expires_in": 863999,
"client_id": acc.client_id or "", "model_mapping": {
"expires_at": expires_at, "gpt-5.1": "gpt-5.1",
"expires_in": 863999, "gpt-5.1-codex": "gpt-5.1-codex",
"model_mapping": { "gpt-5.1-codex-max": "gpt-5.1-codex-max",
"gpt-5.1": "gpt-5.1", "gpt-5.1-codex-mini": "gpt-5.1-codex-mini",
"gpt-5.1-codex": "gpt-5.1-codex", "gpt-5.2": "gpt-5.2",
"gpt-5.1-codex-max": "gpt-5.1-codex-max", "gpt-5.2-codex": "gpt-5.2-codex",
"gpt-5.1-codex-mini": "gpt-5.1-codex-mini", "gpt-5.3": "gpt-5.3",
"gpt-5.2": "gpt-5.2", "gpt-5.3-codex": "gpt-5.3-codex",
"gpt-5.2-codex": "gpt-5.2-codex" "gpt-5.4": "gpt-5.4"
}, },
"organization_id": acc.workspace_id or "", "organization_id": acc.workspace_id or "",
"refresh_token": acc.refresh_token or "" "refresh_token": acc.refresh_token or ""
}, },
"extra": {}, "extra": {},
"concurrency": 10, "concurrency": 10,
"priority": 1, "priority": 1,
"rate_multiplier": 1, "rate_multiplier": 1,
"auto_pause_on_expired": True "auto_pause_on_expired": True
}
]
} }
with get_db() as db: with get_db() as db:
@@ -460,29 +456,21 @@ async def export_accounts_sub2api(request: BatchExportRequest):
accounts = db.query(Account).filter(Account.id.in_(ids)).all() accounts = db.query(Account).filter(Account.id.in_(ids)).all()
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
payload = {
"proxies": [],
"accounts": [make_account_entry(acc) for acc in accounts]
}
content = json.dumps(payload, ensure_ascii=False, indent=2)
if len(accounts) == 1: if len(accounts) == 1:
acc = accounts[0] filename = f"{accounts[0].email}_sub2api.json"
content = json.dumps(make_sub2api_json(acc), ensure_ascii=False, indent=2) else:
filename = f"{acc.email}_sub2api.json" filename = f"sub2api_tokens_{timestamp}.json"
return StreamingResponse(
iter([content]),
media_type="application/json",
headers={"Content-Disposition": f"attachment; filename={filename}"}
)
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zf:
for acc in accounts:
content = json.dumps(make_sub2api_json(acc), ensure_ascii=False, indent=2)
zf.writestr(f"{acc.email}_sub2api.json", content)
zip_buffer.seek(0)
zip_filename = f"sub2api_tokens_{timestamp}.zip"
return StreamingResponse( return StreamingResponse(
zip_buffer, iter([content]),
media_type="application/zip", media_type="application/json",
headers={"Content-Disposition": f"attachment; filename={zip_filename}"} headers={"Content-Disposition": f"attachment; filename={filename}"}
) )