Files
ai_agent/backend/app/api/routes/v1/admin_users.py
T
furyhawk 8351e73d39 feat: add Zustand stores for conversation, file preview, sidebar, theme, and knowledge base selection
- Implemented `conversation-store` for managing conversations and messages.
- Created `file-preview-store` to handle file preview state.
- Added `sidebar-store` for sidebar visibility management.
- Developed `theme-store` for theme persistence and management.
- Introduced `kb-selection-store` for managing active knowledge base selections with persistence.

chore: define API and chat types

- Added types for API responses, authentication, chat messages, conversations, and projects.
- Defined interfaces for various entities including users, sessions, and message ratings.

build: configure TypeScript and testing setup

- Set up `tsconfig.json` for TypeScript configuration.
- Created `vitest.config.ts` for testing configuration with Vitest.
- Added `vitest.setup.ts` for global test setup including mocks for Next.js router and media queries.
- Configured Vercel deployment settings in `vercel.json`.
2026-06-11 16:54:43 +08:00

94 lines
2.6 KiB
Python

"""Admin user management routes.
All endpoints require the admin role (CurrentAdmin).
Endpoints:
GET /admin/users — List all users (paginated + search)
GET /admin/users/{user_id} — Get a single user
PATCH /admin/users/{user_id} — Update user (role, is_active, is_app_admin)
DELETE /admin/users/{user_id} — Hard-delete a user
POST /admin/users/{user_id}/impersonate — Issue short-lived token to act as user
"""
from datetime import timedelta
from typing import Any
from uuid import UUID
from fastapi import APIRouter, Query, Request, status
from app.api.deps import CurrentAdmin, DBSession, UserSvc
from app.core.security import create_access_token
from app.schemas.conversation_share import AdminUserList
from app.schemas.user import UserRead, UserUpdate
router = APIRouter()
@router.get("", response_model=AdminUserList)
async def list_users(
_: CurrentAdmin,
service: UserSvc,
skip: int = Query(0, ge=0),
limit: int = Query(50, ge=1, le=500),
search: str | None = Query(None),
) -> Any:
result = await service.admin_list_with_counts(skip=skip, limit=limit, search=search)
return result
@router.get("/{user_id}", response_model=UserRead)
async def get_user(
user_id: UUID,
_: CurrentAdmin,
service: UserSvc,
) -> Any:
return await service.get_by_id(user_id)
@router.patch("/{user_id}", response_model=UserRead)
async def update_user(
request: Request,
user_id: UUID,
user_in: UserUpdate,
admin: CurrentAdmin,
db: DBSession,
service: UserSvc,
) -> Any:
user = await service.update(user_id, user_in)
return user
@router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT, response_model=None)
async def delete_user(
request: Request,
user_id: UUID,
admin: CurrentAdmin,
db: DBSession,
service: UserSvc,
) -> None:
await service.get_by_id(user_id) # raises 404 if not found
await service.delete(user_id)
@router.post("/{user_id}/impersonate", response_model=dict)
async def impersonate_user(
request: Request,
user_id: UUID,
admin: CurrentAdmin,
db: DBSession,
service: UserSvc,
) -> Any:
"""Issue a short-lived (1h) access token to act as the target user."""
target = await service.get_by_id(user_id)
token = create_access_token(
subject=str(target.id),
expires_delta=timedelta(hours=1),
)
return {
"access_token": token,
"token_type": "bearer",
"impersonated_user_id": str(target.id),
"impersonated_by": str(admin.id),
"expires_in": 3600,
}