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
@@ -10,10 +10,19 @@ from ..schemas.admin import SetUserRoleRequest, SetUserStatusRequest
from ..schemas.admin import UserCreateRequest, RolePermissionRequest, UpdateUserRequest
from ...rbac import require_permission, require_roles, write_audit
from ...infrastructure.repositories import Database
from ...services import compute_client
router = APIRouter(prefix="/admin", tags=["admin"])
async def _sync_compute(coro) -> None:
"""算力引擎用户同步为附加能力:失败不阻断平台用户操作(引擎未配置/不可达时静默)。"""
try:
await coro
except Exception: # noqa: BLE001
pass
@router.post("/users", summary="新增账号(按类型/角色)")
async def create_user(
req: UserCreateRequest,
@@ -24,6 +33,9 @@ async def create_user(
from ...services.user_admin_service import UserAdminService
user = await UserAdminService(db).create_user(req, actor)
# 平台用户创建 → 引擎同步建号 + 签发 PAT(算力引擎用户与平台一致)
await _sync_compute(compute_client.create_user(user["username"]))
await _sync_compute(compute_client.issue_pat(user["username"]))
await write_audit(db, action="user.create", resource="user", resource_id=user["id"],
detail=f"role={req.role}", user=actor, request=request)
return await db.users.to_profile(user)
@@ -78,9 +90,14 @@ async def set_user_status(
db: Database = Depends(get_db),
actor: dict = Depends(require_permission("action:user.disable")),
):
if await db.users.get_by_id(user_id) is None:
user = await db.users.get_by_id(user_id)
if user is None:
raise HTTPException(status_code=404, detail="User not found")
updated = await db.users.set_status(user_id, req.status)
# 平台用户启用/禁用 → 引擎镜像同步
await _sync_compute(compute_client.sync_user_enabled(
user.get("username"), req.status == "active",
))
await write_audit(
db, action="user.disable", resource="user", resource_id=user_id,
detail=f"{actor['username']} -> status={req.status}",
@@ -118,9 +135,13 @@ async def delete_user(
db: Database = Depends(get_db),
actor: dict = Depends(require_permission("action:user.manage")),
):
user = await db.users.get_by_id(user_id)
ok = await db.users.delete(user_id)
if not ok:
raise HTTPException(status_code=404, detail="User not found")
# 平台用户删除 → 引擎镜像同步删除
if user:
await _sync_compute(compute_client.sync_delete_user(user.get("username")))
await write_audit(db, action="user.delete", resource="user", resource_id=user_id,
detail="deleted", user=actor, request=request)
return {"ok": True, "user_id": user_id}
+6 -1
View File
@@ -30,7 +30,12 @@ _ENGINE_MODEL_IDS = [
def _auth_headers() -> dict[str, str]:
return {"Authorization": f"Bearer {config.COMPUTE_ADMIN_TOKEN}"}
# /v1 转发用引擎能识别的真实消费令牌(PINEAGENTS_COMPUTE_RELAY_TOKEN),
# 与管理的 COMPUTE_ADMIN_TOKEN(内部合成 root) 区分;空令牌不下发,避免 `Bearer ` 非法被 httpx 拒绝。
token = config.COMPUTE_RELAY_TOKEN or config.COMPUTE_ADMIN_TOKEN
if not token:
return {}
return {"Authorization": f"Bearer {token}"}
@router.post("/chat/completions")