feat(accounts): 新增 Sub2Api 格式导出功能

This commit is contained in:
cnlimiter
2026-03-16 19:39:11 +08:00
parent 46f390a984
commit f0e1dd3ad0
4 changed files with 80 additions and 3 deletions

View File

@@ -393,6 +393,82 @@ async def export_accounts_csv(request: BatchExportRequest):
)
@router.post("/export/sub2api")
async def export_accounts_sub2api(request: BatchExportRequest):
"""导出账号为 Sub2Api 格式(每个账号单独一个 JSON 文件,多个打包为 ZIP"""
import io
import zipfile
def make_sub2api_json(acc) -> dict:
expires_at = int(acc.expires_at.timestamp()) if acc.expires_at else 0
return {
"proxies": [],
"accounts": [
{
"name": acc.email,
"platform": "openai",
"type": "oauth",
"credentials": {
"access_token": acc.access_token or "",
"chatgpt_account_id": acc.account_id or "",
"chatgpt_user_id": "",
"client_id": acc.client_id or "",
"expires_at": expires_at,
"expires_in": 863999,
"model_mapping": {
"gpt-5.1": "gpt-5.1",
"gpt-5.1-codex": "gpt-5.1-codex",
"gpt-5.1-codex-max": "gpt-5.1-codex-max",
"gpt-5.1-codex-mini": "gpt-5.1-codex-mini",
"gpt-5.2": "gpt-5.2",
"gpt-5.2-codex": "gpt-5.2-codex"
},
"organization_id": acc.workspace_id or "",
"refresh_token": acc.refresh_token or ""
},
"extra": {},
"concurrency": 10,
"priority": 1,
"rate_multiplier": 1,
"auto_pause_on_expired": True
}
]
}
with get_db() as db:
ids = resolve_account_ids(
db, request.ids, request.select_all,
request.status_filter, request.email_service_filter, request.search_filter
)
accounts = db.query(Account).filter(Account.id.in_(ids)).all()
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
if len(accounts) == 1:
acc = accounts[0]
content = json.dumps(make_sub2api_json(acc), ensure_ascii=False, indent=2)
filename = f"{acc.email}_sub2api.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(
zip_buffer,
media_type="application/zip",
headers={"Content-Disposition": f"attachment; filename={zip_filename}"}
)
@router.post("/export/cpa")
async def export_accounts_cpa(request: BatchExportRequest):
"""导出账号为 CPA Token JSON 格式(每个账号单独一个 JSON 文件,打包为 ZIP"""