""" 会话验证路由 - 实现类似 osu! 的邮件验证流程 (API v2) """ from __future__ import annotations from typing import Annotated from app.database import User from app.dependencies import get_current_user from app.dependencies.database import Database, get_redis from app.service.email_verification_service import ( EmailVerificationService, ) from app.service.login_log_service import LoginLogService from .router import router from fastapi import Depends, Form, HTTPException, Request, Security, status from fastapi.responses import Response from pydantic import BaseModel from redis.asyncio import Redis class SessionReissueResponse(BaseModel): """重新发送验证码响应""" success: bool message: str @router.post( "/session/verify", name="验证会话", description="验证邮件验证码并完成会话认证", status_code=204, tags=["验证"] ) async def verify_session( request: Request, db: Database, redis: Annotated[Redis, Depends(get_redis)], verification_key: str = Form(..., description="8位邮件验证码"), current_user: User = Security(get_current_user), ) -> Response: """ 验证邮件验证码并完成会话认证 对应 osu! 的 session/verify 接口 成功时返回 204 No Content,失败时返回 401 Unauthorized """ try: from app.dependencies.geoip import get_client_ip ip_address = get_client_ip(request) # noqa: F841 user_agent = request.headers.get("User-Agent", "Unknown") # noqa: F841 # 从当前认证用户获取信息 user_id = current_user.id if not user_id: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="用户未认证") # 验证邮件验证码 success, message = await EmailVerificationService.verify_code(db, redis, user_id, verification_key) if success: # 记录成功的邮件验证 await LoginLogService.record_login( db=db, user_id=user_id, request=request, login_method="email_verification", login_success=True, notes="邮件验证成功", ) # 返回 204 No Content 表示验证成功 return Response(status_code=status.HTTP_204_NO_CONTENT) else: # 记录失败的邮件验证尝试 await LoginLogService.record_failed_login( db=db, request=request, attempted_username=current_user.username, login_method="email_verification", notes=f"邮件验证失败: {message}", ) # 返回 401 Unauthorized 表示验证失败 raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=message) except ValueError: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="无效的用户会话") except Exception: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="验证过程中发生错误") @router.post( "/session/verify/reissue", name="重新发送验证码", description="重新发送邮件验证码", response_model=SessionReissueResponse, tags=["验证"], ) async def reissue_verification_code( request: Request, db: Database, redis: Annotated[Redis, Depends(get_redis)], current_user: User = Security(get_current_user), ) -> SessionReissueResponse: """ 重新发送邮件验证码 对应 osu! 的 session/verify/reissue 接口 """ try: from app.dependencies.geoip import get_client_ip ip_address = get_client_ip(request) user_agent = request.headers.get("User-Agent", "Unknown") # 从当前认证用户获取信息 user_id = current_user.id if not user_id: return SessionReissueResponse(success=False, message="用户未认证") # 重新发送验证码 success, message = await EmailVerificationService.resend_verification_code( db, redis, user_id, current_user.username, current_user.email, ip_address, user_agent, ) return SessionReissueResponse(success=success, message=message) except ValueError: return SessionReissueResponse(success=False, message="无效的用户会话") except Exception: return SessionReissueResponse(success=False, message="重新发送过程中发生错误")