159 lines
3.9 KiB
Python
159 lines
3.9 KiB
Python
"""
|
||
认证API
|
||
"""
|
||
from fastapi import APIRouter, HTTPException, Depends, Request
|
||
from app.models.auth import (
|
||
SendCodeRequest, SendCodeResponse,
|
||
LoginRequest, LoginResponse,
|
||
RefreshTokenResponse, UserInfo
|
||
)
|
||
from app.models.database import User
|
||
from app.services.auth_service import auth_service
|
||
from app.services.jwt_service import jwt_service
|
||
from app.services.db_service import db_service
|
||
from app.middleware.auth_middleware import get_current_user, get_client_ip
|
||
from app.utils.logger import logger
|
||
|
||
router = APIRouter(prefix="/api/auth", tags=["认证"])
|
||
|
||
|
||
@router.post("/send-code", response_model=SendCodeResponse)
|
||
async def send_verification_code(
|
||
request_data: SendCodeRequest,
|
||
request: Request
|
||
):
|
||
"""
|
||
发送验证码
|
||
|
||
- 同一手机号60秒内只能发送一次
|
||
- 同一IP每小时最多发送10次
|
||
- 验证码5分钟有效期
|
||
"""
|
||
try:
|
||
client_ip = get_client_ip(request)
|
||
db = db_service.get_session()
|
||
|
||
try:
|
||
result = await auth_service.send_verification_code(
|
||
db=db,
|
||
phone=request_data.phone,
|
||
ip_address=client_ip
|
||
)
|
||
|
||
return SendCodeResponse(**result)
|
||
|
||
finally:
|
||
db.close()
|
||
|
||
except Exception as e:
|
||
logger.error(f"发送验证码失败: {e}")
|
||
raise HTTPException(status_code=500, detail="发送验证码失败")
|
||
|
||
|
||
@router.post("/login", response_model=LoginResponse)
|
||
async def login_with_code(
|
||
request_data: LoginRequest,
|
||
request: Request
|
||
):
|
||
"""
|
||
验证码登录
|
||
|
||
- 验证验证码是否正确且未过期
|
||
- 用户不存在则自动注册
|
||
- 生成JWT token(7天有效期)
|
||
- 更新最后登录时间
|
||
"""
|
||
try:
|
||
client_ip = get_client_ip(request)
|
||
db = db_service.get_session()
|
||
|
||
try:
|
||
result = await auth_service.login_with_code(
|
||
db=db,
|
||
phone=request_data.phone,
|
||
code=request_data.code,
|
||
ip_address=client_ip
|
||
)
|
||
|
||
if not result["success"]:
|
||
return LoginResponse(
|
||
success=False,
|
||
message=result["message"]
|
||
)
|
||
|
||
return LoginResponse(
|
||
success=True,
|
||
token=result["token"],
|
||
user=UserInfo(**result["user"])
|
||
)
|
||
|
||
finally:
|
||
db.close()
|
||
|
||
except Exception as e:
|
||
logger.error(f"登录失败: {e}")
|
||
raise HTTPException(status_code=500, detail="登录失败")
|
||
|
||
|
||
@router.post("/refresh", response_model=RefreshTokenResponse)
|
||
async def refresh_token(
|
||
current_user: User = Depends(get_current_user)
|
||
):
|
||
"""
|
||
刷新Token
|
||
|
||
需要提供有效的JWT token
|
||
"""
|
||
try:
|
||
# 生成新的token
|
||
new_token = jwt_service.create_access_token(
|
||
current_user.id,
|
||
current_user.phone
|
||
)
|
||
|
||
return RefreshTokenResponse(
|
||
success=True,
|
||
token=new_token
|
||
)
|
||
|
||
except Exception as e:
|
||
logger.error(f"刷新token失败: {e}")
|
||
raise HTTPException(status_code=500, detail="刷新token失败")
|
||
|
||
|
||
@router.get("/me", response_model=UserInfo)
|
||
async def get_current_user_info(
|
||
current_user: User = Depends(get_current_user)
|
||
):
|
||
"""
|
||
获取当前用户信息
|
||
|
||
需要提供有效的JWT token
|
||
"""
|
||
# 手机号脱敏
|
||
masked_phone = f"{current_user.phone[:3]}****{current_user.phone[-4:]}"
|
||
|
||
return UserInfo(
|
||
id=current_user.id,
|
||
phone=masked_phone,
|
||
created_at=current_user.created_at,
|
||
last_login_at=current_user.last_login_at
|
||
)
|
||
|
||
|
||
@router.post("/logout")
|
||
async def logout(
|
||
current_user: User = Depends(get_current_user)
|
||
):
|
||
"""
|
||
登出
|
||
|
||
主要在前端清除token,后端记录日志
|
||
"""
|
||
logger.info(f"用户登出: {current_user.phone}")
|
||
|
||
return {
|
||
"success": True,
|
||
"message": "已登出"
|
||
}
|