Compare commits
26 Commits
feat/user-
...
d870646e28
| Author | SHA1 | Date | |
|---|---|---|---|
| d870646e28 | |||
|
|
2cbf6445eb | ||
| 4675ab7201 | |||
| a9d075bc19 | |||
| 9429e37542 | |||
| 0229fbb54c | |||
| ffce4298c8 | |||
| a115e380cb | |||
| 94155614f5 | |||
| 90b494f097 | |||
| e7d3cbe07b | |||
| 51fb8ca073 | |||
| 1cb924451b | |||
| c011e334a0 | |||
| d52861fd9c | |||
| 3aa6dd2d6e | |||
| c3199d0cd0 | |||
| d3f72962c0 | |||
| 4643a73c60 | |||
| eae947d9b6 | |||
| a2f626557e | |||
| c5827db872 | |||
| 7326cadfec | |||
| 1b10c97099 | |||
| 8434a5d226 | |||
| a2ab541b73 |
@@ -9,7 +9,7 @@ from sqlalchemy.orm import Session
|
|||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from app.core.config import get_db
|
from app.core.config import get_db
|
||||||
from app.api.deps import get_current_user_or_apikey
|
from app.api.deps import get_current_user
|
||||||
from app.models.models import User
|
from app.models.models import User
|
||||||
from app.models.agent import Agent
|
from app.models.agent import Agent
|
||||||
from app.models.schedule_type import ScheduleType
|
from app.models.schedule_type import ScheduleType
|
||||||
@@ -68,7 +68,7 @@ def _require_schedule_manage(db: Session, user: User) -> User:
|
|||||||
)
|
)
|
||||||
def list_schedule_types(
|
def list_schedule_types(
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
current_user: User = Depends(get_current_user_or_apikey),
|
current_user: User = Depends(get_current_user),
|
||||||
):
|
):
|
||||||
_require_schedule_read(db, current_user)
|
_require_schedule_read(db, current_user)
|
||||||
return db.query(ScheduleType).all()
|
return db.query(ScheduleType).all()
|
||||||
@@ -82,7 +82,7 @@ def list_schedule_types(
|
|||||||
def create_schedule_type(
|
def create_schedule_type(
|
||||||
payload: ScheduleTypeCreate,
|
payload: ScheduleTypeCreate,
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
current_user: User = Depends(get_current_user_or_apikey),
|
current_user: User = Depends(get_current_user),
|
||||||
):
|
):
|
||||||
_require_schedule_manage(db, current_user)
|
_require_schedule_manage(db, current_user)
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ def update_schedule_type(
|
|||||||
schedule_type_id: int,
|
schedule_type_id: int,
|
||||||
payload: ScheduleTypeUpdate,
|
payload: ScheduleTypeUpdate,
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
current_user: User = Depends(get_current_user_or_apikey),
|
current_user: User = Depends(get_current_user),
|
||||||
):
|
):
|
||||||
_require_schedule_manage(db, current_user)
|
_require_schedule_manage(db, current_user)
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ def update_schedule_type(
|
|||||||
def delete_schedule_type(
|
def delete_schedule_type(
|
||||||
schedule_type_id: int,
|
schedule_type_id: int,
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
current_user: User = Depends(get_current_user_or_apikey),
|
current_user: User = Depends(get_current_user),
|
||||||
):
|
):
|
||||||
_require_schedule_manage(db, current_user)
|
_require_schedule_manage(db, current_user)
|
||||||
|
|
||||||
@@ -192,7 +192,7 @@ def assign_schedule_type(
|
|||||||
agent_id: str,
|
agent_id: str,
|
||||||
payload: AgentScheduleTypeAssign,
|
payload: AgentScheduleTypeAssign,
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
current_user: User = Depends(get_current_user_or_apikey),
|
current_user: User = Depends(get_current_user),
|
||||||
):
|
):
|
||||||
_require_schedule_manage(db, current_user)
|
_require_schedule_manage(db, current_user)
|
||||||
|
|
||||||
|
|||||||
@@ -221,71 +221,6 @@ 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, Field
|
from pydantic import BaseModel
|
||||||
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,19 +186,6 @@ 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