Merge pull request 'feat(users): PATCH /users/{id}/bind-agent to backfill agents row' (#20) from feat/user-bind-agent into main
This commit is contained in:
@@ -221,6 +221,71 @@ def update_user(
|
|||||||
return _user_response(user)
|
return _user_response(user)
|
||||||
|
|
||||||
|
|
||||||
|
@router.patch("/{identifier}/bind-agent", response_model=schemas.UserResponse)
|
||||||
|
def bind_agent(
|
||||||
|
identifier: str,
|
||||||
|
payload: schemas.UserBindAgentRequest,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
_: models.User = Depends(require_account_creator),
|
||||||
|
):
|
||||||
|
"""Bind an existing user to (agent_id, claw_identifier).
|
||||||
|
|
||||||
|
Backfill path for users that were created via `hf user create` before
|
||||||
|
the cli supported `--agent-id` / `--claw-identifier` flags. Creates
|
||||||
|
the `agents` row that should have been written at user-create time.
|
||||||
|
|
||||||
|
Idempotent: if the user is already bound to the same
|
||||||
|
(agent_id, claw_identifier), returns the user unchanged (200, no-op).
|
||||||
|
|
||||||
|
Rejects (409) if:
|
||||||
|
- the user is bound to a DIFFERENT (agent_id, claw_identifier)
|
||||||
|
- the requested agent_id is already in use by another user
|
||||||
|
|
||||||
|
Permission: account.create (admin auto-grants) — same gate as
|
||||||
|
POST /users so the surface stays symmetric.
|
||||||
|
"""
|
||||||
|
user = _find_user_by_id_or_username(db, identifier)
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(status_code=404, detail="User not found")
|
||||||
|
|
||||||
|
existing_agent_for_user = db.query(Agent).filter(Agent.user_id == user.id).first()
|
||||||
|
if existing_agent_for_user:
|
||||||
|
if (
|
||||||
|
existing_agent_for_user.agent_id == payload.agent_id
|
||||||
|
and existing_agent_for_user.claw_identifier == payload.claw_identifier
|
||||||
|
):
|
||||||
|
# idempotent re-bind
|
||||||
|
return _user_response(user)
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=409,
|
||||||
|
detail=(
|
||||||
|
f"User '{user.username}' is already bound to agent "
|
||||||
|
f"'{existing_agent_for_user.agent_id}' on claw "
|
||||||
|
f"'{existing_agent_for_user.claw_identifier}'"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
existing_for_agent_id = (
|
||||||
|
db.query(Agent).filter(Agent.agent_id == payload.agent_id).first()
|
||||||
|
)
|
||||||
|
if existing_for_agent_id:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=409,
|
||||||
|
detail=f"agent_id '{payload.agent_id}' already in use by another user",
|
||||||
|
)
|
||||||
|
|
||||||
|
db.add(
|
||||||
|
Agent(
|
||||||
|
user_id=user.id,
|
||||||
|
agent_id=payload.agent_id,
|
||||||
|
claw_identifier=payload.claw_identifier,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(user)
|
||||||
|
return _user_response(user)
|
||||||
|
|
||||||
|
|
||||||
_BUILTIN_USERNAMES = {"acc-mgr", DELETED_USER_USERNAME}
|
_BUILTIN_USERNAMES = {"acc-mgr", DELETED_USER_USERNAME}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel, Field
|
||||||
from typing import Optional, List
|
from typing import Optional, List
|
||||||
from datetime import datetime, time
|
from datetime import datetime, time
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
@@ -186,6 +186,19 @@ class UserUpdate(BaseModel):
|
|||||||
discord_user_id: Optional[str] = None
|
discord_user_id: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class UserBindAgentRequest(BaseModel):
|
||||||
|
"""Request body for PATCH /users/{identifier}/bind-agent.
|
||||||
|
|
||||||
|
Binds an existing user to (agent_id, claw_identifier) by inserting a
|
||||||
|
row in the `agents` table. Both fields required (mirrors the
|
||||||
|
create-time invariant in UserCreate). Idempotent: re-binding the same
|
||||||
|
user to the same (agent_id, claw_identifier) returns the existing
|
||||||
|
Agent row instead of 409.
|
||||||
|
"""
|
||||||
|
agent_id: str = Field(..., min_length=1, max_length=128)
|
||||||
|
claw_identifier: str = Field(..., min_length=1, max_length=128)
|
||||||
|
|
||||||
|
|
||||||
class UserResponse(UserBase):
|
class UserResponse(UserBase):
|
||||||
id: int
|
id: int
|
||||||
is_active: bool
|
is_active: bool
|
||||||
|
|||||||
Reference in New Issue
Block a user