| """用户相关的API接口""" |
|
|
| from datetime import datetime, timedelta |
| from typing import List |
|
|
| import jwt |
| from fastapi import APIRouter, Depends, HTTPException, Response, status |
| from sqlalchemy.orm import Session |
|
|
| from qa_annotate.api.auth import get_current_active_user, get_current_superuser |
| from qa_annotate.config import settings |
| from qa_annotate.database.base import get_db |
| from qa_annotate.database.crud import SystemConfigCRUD, UserCRUD |
| from qa_annotate.schema.user import ( |
| Token, |
| User, |
| UserCreate, |
| UserLogin, |
| UserRegister, |
| UserUpdate, |
| ) |
|
|
| router = APIRouter(prefix="/users", tags=["users"]) |
|
|
|
|
| @router.post("/register", response_model=User, status_code=status.HTTP_201_CREATED) |
| def register(user_register: UserRegister, db: Session = Depends(get_db)): |
| """用户注册(公开接口,无需认证) |
| |
| 注意: |
| - 生产环境:新注册的用户默认禁用(is_active=False),需要管理员启用后才能登录 |
| - 非生产环境:新注册的用户默认启用(is_active=True),可以直接登录 |
| - 注册功能可以通过系统配置进行开关控制 |
| """ |
| |
| allow_registration = SystemConfigCRUD.get_value( |
| db, key="allow_registration", default="true" |
| ) |
| if allow_registration and allow_registration.lower() not in ("true", "1", "yes"): |
| raise HTTPException( |
| status_code=status.HTTP_403_FORBIDDEN, |
| detail="注册功能已禁用,请联系管理员", |
| ) |
|
|
| |
| existing_user = UserCRUD.get_by_username(db, username=user_register.username) |
| if existing_user: |
| raise HTTPException( |
| status_code=status.HTTP_400_BAD_REQUEST, |
| detail=f"用户名 '{user_register.username}' 已存在", |
| ) |
|
|
| |
| |
| is_active = not settings.is_production |
|
|
| |
| user_create = UserCreate( |
| username=user_register.username, |
| password=user_register.password, |
| full_name=user_register.full_name, |
| organization=user_register.organization, |
| team=user_register.team, |
| species=user_register.species, |
| is_active=is_active, |
| is_superuser=False, |
| ) |
|
|
| return UserCRUD.create(db=db, user=user_create) |
|
|
|
|
| @router.post("/login", response_model=Token) |
| def login(user_login: UserLogin, response: Response, db: Session = Depends(get_db)): |
| """用户登录""" |
| |
| user = UserCRUD.authenticate_user( |
| db, user_login.username, user_login.password, user_login.timestamp |
| ) |
|
|
| if not user: |
| raise HTTPException( |
| status_code=status.HTTP_401_UNAUTHORIZED, |
| detail="用户名或密码错误", |
| headers={"WWW-Authenticate": "Bearer"}, |
| ) |
|
|
| |
| if not user.is_active: |
| raise HTTPException( |
| status_code=status.HTTP_403_FORBIDDEN, |
| detail="用户已被禁用,请联系管理员", |
| headers={"WWW-Authenticate": "Bearer"}, |
| ) |
|
|
| |
| token_expire_seconds = settings.TOKEN_EXPIRE_DAYS * 24 * 60 * 60 |
| expire_time = datetime.utcnow() + timedelta(days=settings.TOKEN_EXPIRE_DAYS) |
|
|
| |
| token_data = { |
| "user_id": user.id, |
| "username": user.username, |
| "exp": int(expire_time.timestamp()), |
| } |
|
|
| |
| access_token = jwt.encode( |
| token_data, |
| settings.SECRET_KEY, |
| algorithm=settings.ALGORITHM, |
| ) |
|
|
| |
| response.set_cookie( |
| key="access_token", |
| value=access_token, |
| max_age=token_expire_seconds, |
| expires=token_expire_seconds, |
| path="/", |
| httponly=True, |
| samesite="lax", |
| ) |
|
|
| return Token(access_token=access_token, token_type="bearer", user=user) |
|
|
|
|
| @router.post("/", response_model=User, status_code=status.HTTP_201_CREATED) |
| def create_user( |
| user: UserCreate, |
| db: Session = Depends(get_db), |
| _: User = Depends(get_current_superuser), |
| ): |
| """创建用户""" |
| |
| existing_user = UserCRUD.get_by_username(db, username=user.username) |
| if existing_user: |
| raise HTTPException( |
| status_code=status.HTTP_400_BAD_REQUEST, |
| detail=f"用户名 '{user.username}' 已存在", |
| ) |
|
|
| return UserCRUD.create(db=db, user=user) |
|
|
|
|
| @router.put("/{user_id}", response_model=User) |
| def update_user( |
| user_id: int, |
| user_update: UserUpdate, |
| db: Session = Depends(get_db), |
| current_user: User = Depends(get_current_active_user), |
| ): |
| """更新用户""" |
| |
| existing_user = UserCRUD.get_by_id(db, user_id=user_id) |
| if not existing_user: |
| raise HTTPException( |
| status_code=status.HTTP_404_NOT_FOUND, detail=f"用户 ID {user_id} 不存在" |
| ) |
|
|
| |
| if not current_user.is_superuser and current_user.id != user_id: |
| raise HTTPException( |
| status_code=status.HTTP_403_FORBIDDEN, detail="只能更新自己的信息" |
| ) |
|
|
| |
| if not current_user.is_superuser: |
| if user_update.is_active is not None or user_update.is_superuser is not None: |
| raise HTTPException( |
| status_code=status.HTTP_403_FORBIDDEN, detail="无权修改用户状态或权限" |
| ) |
|
|
| |
| if ( |
| user_update.username is not None |
| and user_update.username != existing_user.username |
| ): |
| username_user = UserCRUD.get_by_username(db, username=user_update.username) |
| if username_user: |
| raise HTTPException( |
| status_code=status.HTTP_400_BAD_REQUEST, |
| detail=f"用户名 '{user_update.username}' 已存在", |
| ) |
|
|
| updated_user = UserCRUD.update(db=db, user_id=user_id, user_update=user_update) |
| if not updated_user: |
| raise HTTPException( |
| status_code=status.HTTP_404_NOT_FOUND, detail=f"用户 ID {user_id} 不存在" |
| ) |
|
|
| return updated_user |
|
|
|
|
| @router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT) |
| def delete_user( |
| user_id: int, |
| db: Session = Depends(get_db), |
| current_user: User = Depends(get_current_superuser), |
| ): |
| """删除用户""" |
| |
| existing_user = UserCRUD.get_by_id(db, user_id=user_id) |
| if not existing_user: |
| raise HTTPException( |
| status_code=status.HTTP_404_NOT_FOUND, detail=f"用户 ID {user_id} 不存在" |
| ) |
|
|
| success = UserCRUD.delete(db=db, user_id=user_id) |
| if not success: |
| raise HTTPException( |
| status_code=status.HTTP_404_NOT_FOUND, detail=f"用户 ID {user_id} 不存在" |
| ) |
|
|
| return None |
|
|
|
|
| @router.get("/me", response_model=User) |
| def get_current_user_info( |
| current_user: User = Depends(get_current_active_user), |
| ): |
| """获取当前登录用户信息""" |
| return current_user |
|
|
|
|
| @router.get("/", response_model=List[User]) |
| def list_users( |
| skip: int = 0, |
| limit: int = 100, |
| is_active: bool = None, |
| db: Session = Depends(get_db), |
| current_user: User = Depends(get_current_active_user), |
| ): |
| """获取用户列表(支持分页和过滤)""" |
| |
| if not current_user.is_superuser: |
| return [current_user] |
|
|
| return UserCRUD.get_all(db=db, skip=skip, limit=limit, is_active=is_active) |
|
|
|
|
| @router.get("/{user_id}", response_model=User) |
| def get_user( |
| user_id: int, |
| db: Session = Depends(get_db), |
| current_user: User = Depends(get_current_active_user), |
| ): |
| """根据ID获取用户""" |
| |
| if not current_user.is_superuser and current_user.id != user_id: |
| raise HTTPException( |
| status_code=status.HTTP_403_FORBIDDEN, detail="只能查看自己的信息" |
| ) |
|
|
| user = UserCRUD.get_by_id(db, user_id=user_id) |
| if not user: |
| raise HTTPException( |
| status_code=status.HTTP_404_NOT_FOUND, detail=f"用户 ID {user_id} 不存在" |
| ) |
| return user |
|
|