mirror of
https://github.com/furyhawk/ai_agent.git
synced 2026-07-21 10:15:44 +00:00
- 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`.
134 lines
3.6 KiB
Python
134 lines
3.6 KiB
Python
# ruff: noqa: I001 - Imports structured for Jinja2 template conditionals
|
|
"""User management routes."""
|
|
|
|
from typing import Any
|
|
|
|
from uuid import UUID
|
|
|
|
from fastapi import APIRouter, File, HTTPException, UploadFile, status
|
|
from fastapi.responses import FileResponse
|
|
from fastapi_pagination import Page
|
|
|
|
from app.api.deps import (
|
|
CurrentAdmin,
|
|
CurrentUser,
|
|
UserSvc,
|
|
)
|
|
from app.db.models.user import UserRole
|
|
from app.schemas.user import UserRead, UserUpdate
|
|
from app.services.file_storage import get_file_storage
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/me", response_model=UserRead)
|
|
async def read_current_user(
|
|
current_user: CurrentUser,
|
|
) -> Any:
|
|
"""Get current user.
|
|
|
|
Returns the authenticated user's profile including their role.
|
|
"""
|
|
return current_user
|
|
|
|
|
|
@router.patch("/me", response_model=UserRead)
|
|
async def update_current_user(
|
|
user_in: UserUpdate,
|
|
current_user: CurrentUser,
|
|
user_service: UserSvc,
|
|
) -> Any:
|
|
"""Update current user.
|
|
|
|
Users can update their own profile (email, full_name).
|
|
Role changes require admin privileges.
|
|
"""
|
|
# Prevent non-admin users from changing their own role
|
|
if user_in.role is not None and not current_user.has_role(UserRole.ADMIN):
|
|
user_in.role = None
|
|
user = await user_service.update(current_user.id, user_in)
|
|
return user
|
|
|
|
|
|
@router.post("/me/avatar", response_model=UserRead)
|
|
async def upload_avatar(
|
|
user_service: UserSvc,
|
|
current_user: CurrentUser,
|
|
file: UploadFile = File(...),
|
|
) -> Any:
|
|
"""Upload or replace avatar image for the current user."""
|
|
data = await file.read()
|
|
try:
|
|
user = await user_service.update_avatar(
|
|
current_user.id, data, file.filename or "avatar.jpg", file.content_type or ""
|
|
)
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=400, detail=str(e)) from None
|
|
return user
|
|
|
|
|
|
@router.get("/avatar/{user_id}")
|
|
async def get_avatar(user_id: UUID, user_service: UserSvc) -> Any:
|
|
"""Get user avatar image."""
|
|
user = await user_service.get_by_id(user_id)
|
|
if not user.avatar_url:
|
|
raise HTTPException(status_code=404, detail="No avatar set")
|
|
storage = get_file_storage()
|
|
file_path = storage.get_full_path(user.avatar_url)
|
|
if not file_path:
|
|
raise HTTPException(status_code=404, detail="Avatar file not found")
|
|
return FileResponse(path=file_path, media_type="image/jpeg")
|
|
|
|
|
|
@router.get("", response_model=Page[UserRead])
|
|
async def read_users(
|
|
user_service: UserSvc,
|
|
_: CurrentAdmin,
|
|
) -> Any:
|
|
"""Get all users (admin only)."""
|
|
return await user_service.list_paginated()
|
|
|
|
|
|
@router.get("/{user_id}", response_model=UserRead)
|
|
async def read_user(
|
|
user_id: UUID,
|
|
user_service: UserSvc,
|
|
_: CurrentAdmin,
|
|
) -> Any:
|
|
"""Get user by ID (admin only).
|
|
|
|
Raises NotFoundError if user does not exist.
|
|
"""
|
|
user = await user_service.get_by_id(user_id)
|
|
return user
|
|
|
|
|
|
@router.patch("/{user_id}", response_model=UserRead)
|
|
async def update_user_by_id(
|
|
user_id: UUID,
|
|
user_in: UserUpdate,
|
|
user_service: UserSvc,
|
|
_: CurrentAdmin,
|
|
) -> Any:
|
|
"""Update user by ID (admin only).
|
|
|
|
Admins can update any user including their role.
|
|
|
|
Raises NotFoundError if user does not exist.
|
|
"""
|
|
user = await user_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_by_id(
|
|
user_id: UUID,
|
|
user_service: UserSvc,
|
|
_: CurrentAdmin,
|
|
) -> None:
|
|
"""Delete user by ID (admin only).
|
|
|
|
Raises NotFoundError if user does not exist.
|
|
"""
|
|
await user_service.delete(user_id)
|