feat(compute): 算力引擎对接改造 + 用户生命周期自动同步

- config: 置顶 load_dotenv(.env),修复 .env 未生效;COMPUTE_ADMIN_TOKEN 改读 PINEAGENTS_INTERNAL_TOKEN(回退旧名),新增独立 COMPUTE_RELAY_TOKEN
- compute_client: 空令牌不下发 Bearer(避免 httpx 非法头);proxy 空令牌返 503 中文诊断;新增 sync_user_enabled/sync_delete_user
- relay: /v1 转发改走 COMPUTE_RELAY_TOKEN(真实消费令牌),空令牌不下发
- rbac_admin: 平台用户创建→自动建引擎账号+签PAT;启用/禁用→同步引擎状态;删除→同步删引擎账号(最佳努力,失败静默)
This commit is contained in:
Pine
2026-08-25 12:08:14 +08:00
parent f067c02ec2
commit 1ca2e38559
4 changed files with 65 additions and 5 deletions
+22 -1
View File
@@ -29,7 +29,11 @@ def _base() -> str:
def _admin_headers() -> dict[str, str]:
return {"Authorization": f"Bearer {config.COMPUTE_ADMIN_TOKEN}"}
"""引擎管理员请求头。令牌未配置时不下发 Authorization,避免 `Bearer ` 空令牌被 httpx 判定非法。"""
token = config.COMPUTE_ADMIN_TOKEN
if not token:
return {}
return {"Authorization": f"Bearer {token}"}
async def _request(*, method: str, path: str, headers: dict[str, str] | None = None,
@@ -106,6 +110,21 @@ async def get_logs(user_pat: str) -> dict:
return await _request(method="GET", path="/api/log/self", headers=headers)
async def sync_user_enabled(username: str, enabled: bool) -> dict:
"""按 username 同步引擎用户启用/禁用(平台用户生命周期自动同步)。"""
return await _request(
method="PATCH", path="/api/user/status", headers=_admin_headers(),
json={"username": username, "status": 1 if enabled else 0},
)
async def sync_delete_user(username: str) -> dict:
"""按 username 删除引擎用户(平台用户删除时同步)。"""
return await _request(
method="DELETE", path=f"/api/user/by_username/{username}", headers=_admin_headers(),
)
async def proxy(method: str, path: str, *, json_body: dict | None = None, params: dict | None = None) -> dict:
"""管理 API 透传:转发到引擎 ``/api<path>``,返回 ``{status, body}``。
@@ -113,6 +132,8 @@ async def proxy(method: str, path: str, *, json_body: dict | None = None, params
models/channels/groups/tokens/users/logs/redemption/ratio 等),不改写每个端点契约。
保留引擎原始状态码与响应体(网关层不吞错误)。
"""
if not config.COMPUTE_ADMIN_TOKEN:
return {"status": 503, "body": '{"success": false, "message": "算力引擎内部令牌未配置(PINEAGENTS_INTERNAL_TOKEN"}'}
url = f"{_base()}/api{path}"
try:
async with httpx.AsyncClient(timeout=config.COMPUTE_TIMEOUT) as client: