Compare commits
4 Commits
feat/maint
...
feat/user-
| Author | SHA1 | Date | |
|---|---|---|---|
| 6400f7f612 | |||
| 5b59806e38 | |||
| 23632aa073 | |||
| 7017d3483e |
@@ -9,7 +9,7 @@ from sqlalchemy.orm import Session
|
||||
from typing import List
|
||||
|
||||
from app.core.config import get_db
|
||||
from app.api.deps import get_current_user
|
||||
from app.api.deps import get_current_user_or_apikey
|
||||
from app.models.models import User
|
||||
from app.models.agent import Agent
|
||||
from app.models.schedule_type import ScheduleType
|
||||
@@ -68,7 +68,7 @@ def _require_schedule_manage(db: Session, user: User) -> User:
|
||||
)
|
||||
def list_schedule_types(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
current_user: User = Depends(get_current_user_or_apikey),
|
||||
):
|
||||
_require_schedule_read(db, current_user)
|
||||
return db.query(ScheduleType).all()
|
||||
@@ -82,7 +82,7 @@ def list_schedule_types(
|
||||
def create_schedule_type(
|
||||
payload: ScheduleTypeCreate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
current_user: User = Depends(get_current_user_or_apikey),
|
||||
):
|
||||
_require_schedule_manage(db, current_user)
|
||||
|
||||
@@ -112,7 +112,7 @@ def update_schedule_type(
|
||||
schedule_type_id: int,
|
||||
payload: ScheduleTypeUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
current_user: User = Depends(get_current_user_or_apikey),
|
||||
):
|
||||
_require_schedule_manage(db, current_user)
|
||||
|
||||
@@ -135,7 +135,7 @@ def update_schedule_type(
|
||||
def delete_schedule_type(
|
||||
schedule_type_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
current_user: User = Depends(get_current_user_or_apikey),
|
||||
):
|
||||
_require_schedule_manage(db, current_user)
|
||||
|
||||
@@ -192,7 +192,7 @@ def assign_schedule_type(
|
||||
agent_id: str,
|
||||
payload: AgentScheduleTypeAssign,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
current_user: User = Depends(get_current_user_or_apikey),
|
||||
):
|
||||
_require_schedule_manage(db, current_user)
|
||||
|
||||
|
||||
@@ -221,6 +221,71 @@ def update_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}
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from pydantic import BaseModel
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional, List
|
||||
from datetime import datetime, time
|
||||
from enum import Enum
|
||||
@@ -186,6 +186,19 @@ class UserUpdate(BaseModel):
|
||||
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):
|
||||
id: int
|
||||
is_active: bool
|
||||
|
||||
Reference in New Issue
Block a user