Skip to content
Merged
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
55 changes: 55 additions & 0 deletions src/codebuddy_proxy/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,61 @@ async def health():
}


# ============================================================================
# 端点:/v1/checkin-status 与 /v1/checkin(每日签到透传)
# ============================================================================

async def _billing_post(path: str) -> dict[str, Any]:
"""用本地已登录会话调用官方 billing 端点并返回原始 JSON。

自动处理 401:先走 ensure_authenticated 刷新 token 再重试一次。
"""
state = get_state()
url = state.client.endpoint.rstrip("/") + path
async with httpx.AsyncClient(timeout=httpx.Timeout(10.0)) as client:
resp = await client.post(url, json={}, headers=state.client.auth_headers())
if resp.status_code == 401:

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: _billing_post 固定 client.post,导致 /v1/checkin-status 虽然对外是 GET,向上游却发送 POST。请支持 method 并保证 GET 不发送多余 JSON body。

# token 过期:触发一次刷新后重试
try:
await asyncio.to_thread(state.client.ensure_authenticated, False)
except Exception:
pass
resp = await client.post(url, json={}, headers=state.client.auth_headers())
try:
return resp.json()
except Exception:
return {"code": resp.status_code, "msg": f"non-JSON response (HTTP {resp.status_code})"}


@app.get("/v1/checkin-status")
async def checkin_status():
"""查询每日签到活动状态(透传官方 /v2/billing/meter/checkin-status)。"""
state = get_state()
try:
state.ensure_auth()
except Exception as e:
raise HTTPException(status_code=503, detail={"error": {"message": f"not authenticated: {e}", "type": "auth_error"}})
result = await _billing_post("/v2/billing/meter/checkin-status")
return {"status": "ok", "upstream": result}


@app.post("/v1/checkin")

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: 返回包装对象会改变上游 {code,msg,data} 结构,并默认返回 HTTP 200,无法透传上游 4xx/5xx。请按约定同时处理状态码与响应体。

async def checkin():
"""执行每日签到(透传官方 /v2/billing/meter/daily-checkin)。

业务结果原样返回:code=0 且 data 非空为领取成功;已签到/活动结束等
业务拒绝通过上游 data.status / msg 区分,这里不做二次解释。
"""
state = get_state()
try:
state.ensure_auth()
except Exception as e:
raise HTTPException(status_code=503, detail={"error": {"message": f"not authenticated: {e}", "type": "auth_error"}})
result = await _billing_post("/v2/billing/meter/daily-checkin")
diagnostic("checkin_claim_result", code=result.get("code"), msg=result.get("msg"))
return {"status": "ok", "upstream": result}


# ============================================================================
# 端点:/v1/models
# ============================================================================
Expand Down