Ai_GirlFriend/lover/deps.py

118 lines
4.2 KiB
Python
Raw Normal View History

2026-01-31 19:15:41 +08:00
from typing import Optional
import requests
from fastapi import Header, HTTPException, status, Cookie
from pydantic import BaseModel
from .config import settings
class AuthedUser(BaseModel):
id: int
reg_step: int = 1
gender: int = 0 # 0/1/2
nickname: str = ""
token: str = ""
def _fetch_user_from_php(token: str) -> Optional[dict]:
"""通过 PHP/FastAdmin 接口获取用户信息。"""
try:
resp = requests.get(
settings.USER_INFO_API,
headers={"token": token},
timeout=5,
)
except Exception as exc: # 网络/超时
raise HTTPException(
status_code=status.HTTP_502_BAD_GATEWAY,
detail="用户中心接口不可用",
) from exc
if resp.status_code != 200:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="用户中心返回非 200",
)
data = resp.json()
# 兼容常见 FastAdmin 响应结构
if isinstance(data, dict):
payload = data.get("data") or data.get("user") or data
else:
payload = None
if not payload:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="用户中心未返回用户信息",
)
return payload
def get_current_user(
authorization: Optional[str] = Header(default=None),
x_token: Optional[str] = Header(default=None, alias="X-Token"),
token_header: Optional[str] = Header(default=None, alias="token"),
token_cookie: Optional[str] = Cookie(default=None, alias="token"),
x_user_id: Optional[int] = Header(default=None, alias="X-User-Id"),
):
"""
鉴权顺序
1) Authorization: Bearer <token>
2) X-Token: <token>
3) token: <token> (header)
4) token: <token> (cookie)
5) X-User-Id仅调试用不经过 PHP 鉴权
生产流程直接调用 USER_INFO_API 拉取用户信息不再依赖本地 nf_user 缓存
"""
import logging
logger = logging.getLogger(__name__)
logger.info(f"认证调试 - APP_ENV: {settings.APP_ENV}, DEBUG: {settings.DEBUG}")
logger.info(f"认证调试 - authorization: {authorization}, x_token: {x_token}, token_header: {token_header}, token_cookie: {token_cookie}, x_user_id: {x_user_id}")
token = None
if authorization and authorization.lower().startswith("bearer "):
token = authorization.split(" ", 1)[1].strip()
if not token and x_token:
token = x_token
if not token and token_header:
token = token_header
if not token and token_cookie:
token = token_cookie
logger.info(f"认证调试 - 提取的 token: {token}")
if token:
try:
payload = _fetch_user_from_php(token)
user_id = payload.get("id") or payload.get("user_id")
reg_step = payload.get("reg_step") or payload.get("stage") or 1
gender = payload.get("gender") or 0
nickname = payload.get("nickname") or payload.get("username") or ""
if not user_id:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="用户中心缺少用户ID"
)
return AuthedUser(
id=user_id,
reg_step=reg_step,
gender=gender,
nickname=nickname,
token=token,
)
except HTTPException:
# 如果是开发环境token 验证失败时也返回测试用户
if settings.APP_ENV == "development" and settings.DEBUG:
logger.warning(f"开发环境token 验证失败,使用测试用户")
return AuthedUser(id=84, reg_step=2, gender=0, nickname="test-user", token="")
# 调试兜底:仅凭 X-User-Id 不校验 PHP方便联调
if x_user_id is not None:
return AuthedUser(id=x_user_id, reg_step=2, gender=0, nickname="debug-user", token="")
# 开发环境兜底:如果没有任何认证信息,返回默认测试用户
if settings.APP_ENV == "development" and settings.DEBUG:
return AuthedUser(id=84, reg_step=2, gender=0, nickname="test-user", token="")
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="用户不存在或未授权")