Files
chongming_lite/src/chongming/app/api/routers/auth.py
T

159 lines
5.1 KiB
Python
Raw Normal View History

2026-04-20 17:11:31 +08:00
from uuid import UUID
2026-03-06 17:57:44 +08:00
from fastapi import APIRouter, Depends, HTTPException, status, Request
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.ext.asyncio import AsyncSession
from ...core.database import get_session
from ...core.security import verify_password
from ...service.user import UserService
from ...service.auth import get_auth_service
2026-04-26 10:20:32 +08:00
from ...model.user import UserCreate, UserRead, UserLogin, Userlogout, UserSession
from ...model.http_error import HTTPError, Detail
2026-03-06 17:57:44 +08:00
from ..deps import get_current_user
2026-04-26 10:20:32 +08:00
from plugins.jwt.jwt_cache import TokenData, TokenResponse, RefreshTokenResponse
2026-03-06 17:57:44 +08:00
router = APIRouter(tags=["authentication"])
2026-04-13 15:55:12 +08:00
access_scheme = HTTPBearer(scheme_name="AccessToken")
refresh_scheme = HTTPBearer(scheme_name="RefreshToken")
2026-03-06 17:57:44 +08:00
2026-04-26 10:20:32 +08:00
@router.post(
"/register",
response_model=UserRead,
responses={400: {"model": HTTPError, "description": "邮箱已被注册"}},
)
2026-03-06 17:57:44 +08:00
async def register(
user_data: UserCreate,
session: AsyncSession = Depends(get_session),
):
existing = await UserService.get_user_by_email(user_data.email, session)
if existing:
2026-04-26 10:20:32 +08:00
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=Detail(
message="邮箱已被注册", code=status.HTTP_400_BAD_REQUEST
).model_dump(),
)
2026-03-06 17:57:44 +08:00
user = await UserService.create_user(
session=session,
email=user_data.email,
password=user_data.password,
username=user_data.username,
full_name=user_data.full_name,
)
return user
2026-04-26 10:20:32 +08:00
@router.post(
"/login",
response_model=TokenResponse,
responses={
400: {"model": HTTPError, "description": "邮箱不存在"},
401: {"model": HTTPError, "description": "密码错误"},
402: {"model": HTTPError, "description": "用户被禁用"},
},
)
2026-03-06 17:57:44 +08:00
async def login(
user_data: UserLogin,
request: Request,
session: AsyncSession = Depends(get_session),
):
user = await UserService.get_user_by_email(user_data.email, session)
if not user:
2026-04-26 10:20:32 +08:00
raise HTTPException(
status_code=400, detail=Detail(message="邮箱不存在", code=400).model_dump()
)
2026-03-06 17:57:44 +08:00
if not verify_password(user_data.password, user.hashed_password):
2026-04-26 10:20:32 +08:00
raise HTTPException(
status_code=400, detail=Detail(message="密码错误", code=401).model_dump()
)
2026-03-06 17:57:44 +08:00
if not user.is_active:
2026-04-26 10:20:32 +08:00
raise HTTPException(
status_code=400, detail=Detail(message="用户被禁用", code=402).model_dump()
)
2026-04-13 15:55:12 +08:00
auth_service = get_auth_service()
return await auth_service.create_tokens(user, request, device_id=None)
2026-03-06 17:57:44 +08:00
2026-04-26 10:20:32 +08:00
@router.post(
"/refresh",
response_model=RefreshTokenResponse,
responses={
401: {"model": HTTPError, "description": "无效的刷新令牌"},
},
summary="刷新访问令牌",
)
2026-03-06 17:57:44 +08:00
async def refresh_token(
request: Request,
2026-04-13 15:55:12 +08:00
credentials: HTTPAuthorizationCredentials = Depends(refresh_scheme),
2026-03-06 17:57:44 +08:00
session: AsyncSession = Depends(get_session),
):
auth_service = get_auth_service()
refresh_token = credentials.credentials
new_access_token = await auth_service.refresh_access_token(
refresh_token, session, request
)
if not new_access_token:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
2026-04-26 10:20:32 +08:00
detail=Detail(message="无效的刷新令牌", code=401).model_dump(),
2026-03-06 17:57:44 +08:00
headers={"WWW-Authenticate": "Bearer"},
)
return {"access_token": new_access_token, "token_type": "bearer"}
2026-04-26 10:20:32 +08:00
@router.post("/logout", response_model=Userlogout)
2026-03-06 17:57:44 +08:00
async def logout(
2026-04-13 15:55:12 +08:00
credentials: HTTPAuthorizationCredentials = Depends(access_scheme),
2026-03-06 17:57:44 +08:00
):
auth_service = get_auth_service()
token = credentials.credentials
await auth_service.logout(token)
2026-04-26 10:20:32 +08:00
return {"message": "登出成功", "code": 200}
2026-03-06 17:57:44 +08:00
2026-04-26 10:20:32 +08:00
@router.post("/logout-all", summary="登出所有设备", response_model=Userlogout)
2026-03-06 17:57:44 +08:00
async def logout_all(current_user: TokenData = Depends(get_current_user)):
auth_service = get_auth_service()
await auth_service.logout_all(current_user.user_id)
2026-04-26 10:20:32 +08:00
return {"message": "所有设备已登出", "code": 200}
2026-03-06 17:57:44 +08:00
@router.get("/sessions", summary="获取用户会话")
async def get_sessions(current_user: TokenData = Depends(get_current_user)):
"""获取用户的所有活跃会话"""
auth_service = get_auth_service()
sessions = await auth_service.get_user_sessions(current_user.user_id)
2026-04-26 10:20:32 +08:00
return UserSession(
user_id=current_user.user_id, sessions=sessions, count=len(sessions)
)
2026-03-06 17:57:44 +08:00
2026-04-26 10:20:32 +08:00
@router.get(
"/me",
response_model=UserRead,
summary="获取当前用户信息",
responses={404: {"model": HTTPError, "description": "用户不存在"}},
)
2026-03-06 17:57:44 +08:00
async def get_current_user_info(
session: AsyncSession = Depends(get_session),
current_user: TokenData = Depends(get_current_user),
):
"""获取当前登录用户的信息"""
2026-04-20 17:11:31 +08:00
user = await UserService.get_user_by_id(UUID(current_user.user_id), session)
2026-03-06 17:57:44 +08:00
if not user:
2026-04-26 10:20:32 +08:00
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=Detail(message="用户不存在", code=404).model_dump(),
)
2026-03-06 17:57:44 +08:00
return user