"""认证模块路由(/auth/*,对应《03-接口文档》第 3 章)""" import redis.asyncio as aioredis from fastapi import APIRouter, Depends, Header, Request from sqlalchemy.ext.asyncio import AsyncSession from app.core.response import ok from app.core.security import decode_token from app.database import get_db from app.deps import Identity, get_identity, require_user from app.redis_client import get_redis from app.schemas.auth import ( EmailSendReq, LoginReq, PasswordResetReq, RefreshReq, RegisterReq, SmsSendReq, ) from app.services import auth_service router = APIRouter(prefix="/auth", tags=["认证"]) def _client_ip(request: Request) -> str: # Nginx 反代后取真实 IP return request.headers.get("X-Real-IP") or (request.client.host if request.client else "") @router.post("/sms/send") async def sms_send(req: SmsSendReq, request: Request, db: AsyncSession = Depends(get_db), redis: aioredis.Redis = Depends(get_redis)): data = await auth_service.send_code(db, redis, "sms", req.phone, req.scene, _client_ip(request)) return ok(data) @router.post("/email/send") async def email_send(req: EmailSendReq, request: Request, db: AsyncSession = Depends(get_db), redis: aioredis.Redis = Depends(get_redis)): data = await auth_service.send_code(db, redis, "email", req.email, req.scene, _client_ip(request)) return ok(data) @router.post("/register") async def register(req: RegisterReq, request: Request, db: AsyncSession = Depends(get_db), redis: aioredis.Redis = Depends(get_redis), identity: Identity = Depends(get_identity)): data = await auth_service.register(db, redis, req, identity.device_id, _client_ip(request)) return ok(data) @router.post("/login") async def login(req: LoginReq, request: Request, db: AsyncSession = Depends(get_db), redis: aioredis.Redis = Depends(get_redis)): data = await auth_service.login(db, redis, req, _client_ip(request)) return ok(data) @router.post("/token/refresh") async def token_refresh(req: RefreshReq, db: AsyncSession = Depends(get_db), redis: aioredis.Redis = Depends(get_redis)): return ok(await auth_service.refresh(db, redis, req.refresh_token)) @router.post("/logout") async def logout(authorization: str = Header(...), redis: aioredis.Redis = Depends(get_redis), identity: Identity = Depends(require_user)): payload = decode_token(authorization.removeprefix("Bearer ").strip()) await auth_service.logout(redis, identity.user_id, payload.get("jti", ""), int(payload.get("exp", 0))) return ok() @router.post("/password/reset") async def password_reset(req: PasswordResetReq, db: AsyncSession = Depends(get_db), redis: aioredis.Redis = Depends(get_redis)): await auth_service.reset_password(db, redis, req) return ok()