# Copyright 2026 zhaoxi826 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from fastapi import APIRouter, Request from fastapi import Depends from pydantic import BaseModel from kilostar.utils.access import Accessor, TokenData, RoleChecker from fastapi.concurrency import run_in_threadpool from kilostar.utils.ray_hook import ray_actor_hook from kilostar.core.postgres_database.model import UserAuthority from kilostar.utils.error import UserNotExistError from kilostar.utils.rate_limit import register_limiter, login_limiter auth_router = APIRouter(prefix="/api/v1/auth", tags=["auth"]) class UserRegister(BaseModel): """``POST /register`` 入参:用户名 + 明文密码。""" user_name: str password: str @auth_router.post("/register") async def create_user(user_register: UserRegister, request: Request): """注册新用户:异步线程池里做 argon2 哈希,再交由 PostgresDatabase Actor 落库。""" register_limiter.check(request) postgres_database = ray_actor_hook("postgres_database").postgres_database try: hashed_password = await run_in_threadpool( Accessor.hash_password, user_register.password ) except ValueError as e: from fastapi.responses import JSONResponse return JSONResponse( status_code=400, content={"code": "password_invalid", "message": str(e)}, ) user = await postgres_database.add_user.remote( user_register.user_name, hashed_password ) return {"message": "success", "user_id": user.user_id} class UserLogin(BaseModel): """``POST /login`` 入参:用户名 + 明文密码。""" user_name: str password: str @auth_router.post("/login") async def login_user(user_login: UserLogin, request: Request): """用户登录:查询用户后在线程池中校验口令,校验成功则签发 JWT。""" login_limiter.check(request) postgres_database = ray_actor_hook("postgres_database").postgres_database user = await postgres_database.login_user.remote(user_login.user_name) if not user: raise UserNotExistError() tokens = await run_in_threadpool( Accessor.login_hashed_password, user, user_login.password ) return { "message": "success", "token": tokens["access_token"], "access_token": tokens["access_token"], "refresh_token": tokens["refresh_token"], } class RefreshTokenRequest(BaseModel): """``POST /refresh`` 入参:refresh token。""" refresh_token: str @auth_router.post("/refresh") async def refresh_token(body: RefreshTokenRequest): """用 refresh token 换取新的 access token + refresh token 对。""" tokens = Accessor.refresh_access_token(body.refresh_token) return { "message": "success", "access_token": tokens["access_token"], "refresh_token": tokens["refresh_token"], } class ChangeAuthorityRequest(BaseModel): """``PUT /authority`` 入参:目标用户 ID 及新的权限枚举。""" user_id: str new_authority: UserAuthority @auth_router.put("/authority") async def change_authority( request: ChangeAuthorityRequest, _: TokenData = Depends( RoleChecker(allowed_roles=UserAuthority.SUPER_ADMINISTRATOR) ), ): """ Update a user's authority level. Only accessible by SUPER_ADMINISTRATOR. """ postgres_database = ray_actor_hook("postgres_database").postgres_database user = await postgres_database.change_user_authority.remote( user_id=request.user_id, new_authority=request.new_authority ) return { "message": "success", "user_id": user.user_id, "new_authority": user.user_authority, } @auth_router.get("/list") async def get_user_list( _: TokenData = Depends( RoleChecker(allowed_roles=UserAuthority.SUPER_ADMINISTRATOR) ), ): """ Get a list of all users. Only accessible by SUPER_ADMINISTRATOR. """ postgres_database = ray_actor_hook("postgres_database").postgres_database users = await postgres_database.get_all_users.remote() return { "users": [ {"user_id": u.user_id, "user_name": u.user_name, "role": u.user_authority} for u in users ] } @auth_router.delete("/{user_id}") async def delete_user( user_id: str, _: TokenData = Depends( RoleChecker(allowed_roles=UserAuthority.SUPER_ADMINISTRATOR) ), ): """ Delete a user. Only accessible by SUPER_ADMINISTRATOR. """ postgres_database = ray_actor_hook("postgres_database").postgres_database await postgres_database.delete_user_by_id.remote(user_id=user_id) return {"message": "success"}