Files
AcgStyleBlog/backend/app/api/endpoints/auth.py
2026-03-28 22:18:43 +08:00

110 lines
3.1 KiB
Python

"""
认证 API 接口
"""
from fastapi import APIRouter, HTTPException, status
from app.schemas.auth import LoginRequest, RegisterRequest, TokenResponse, RefreshTokenRequest
from app.schemas.user import UserPublic
from app.crud.user import user_crud
from app.core.security import (
create_access_token,
create_refresh_token,
decode_token,
)
from app.core.logger import app_logger
router = APIRouter(prefix="/auth", tags=["认证"])
@router.post("/register", response_model=UserPublic, status_code=status.HTTP_201_CREATED)
async def register(request: RegisterRequest):
"""用户注册"""
# 检查用户名是否存在
if await user_crud.get_by_username(request.username):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="用户名已存在"
)
# 检查邮箱是否存在
if await user_crud.get_by_email(request.email):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="邮箱已被注册"
)
# 创建用户
user = await user_crud.create(
username=request.username,
email=request.email,
password=request.password
)
app_logger.info(f"New user registered: {user.username}")
return user
@router.post("/login", response_model=TokenResponse)
async def login(login_data: LoginRequest):
"""用户登录"""
username_or_email = login_data.username
password = login_data.password
# 验证用户
user = await user_crud.authenticate(username_or_email, password)
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="用户已被禁用"
)
# 生成令牌
token_data = {"sub": str(user.id), "username": user.username}
access_token = create_access_token(token_data)
refresh_token = create_refresh_token(token_data)
app_logger.info(f"User logged in: {user.username}")
return TokenResponse(
access_token=access_token,
refresh_token=refresh_token,
token_type="bearer"
)
@router.post("/refresh", response_model=TokenResponse)
async def refresh_token(request: RefreshTokenRequest):
"""刷新令牌"""
payload = decode_token(request.refresh_token)
if payload.get("type") != "refresh":
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="无效的刷新令牌"
)
user_id = payload.get("sub")
username = payload.get("username")
# 生成新令牌
token_data = {"sub": user_id, "username": username}
access_token = create_access_token(token_data)
refresh_token = create_refresh_token(token_data)
return TokenResponse(
access_token=access_token,
refresh_token=refresh_token,
token_type="bearer"
)
@router.post("/logout")
async def logout():
"""用户登出(前端清除令牌即可)"""
return {"message": "登出成功"}