Files
Aether/_deprecated_py_src/utils/request_utils.py
fawney19 1d9c77522a refactor: 移除 Python 后端源码,全面迁移至 Rust gateway 架构
- 删除全部 Python 源码 (src/) 及 Alembic 迁移脚本,归档至 _deprecated_py_src/
- 重构 Rust gateway ai_pipeline: 拆分 planner/finalize 模块,新增 contracts/adaptation 层
- 重组 handlers 模块为 admin/public/proxy/internal/shared 子模块结构
- 新增 executor 模块,引入 Rust 原生数据库迁移 (aether-data/migrations)
- 简化 CI/Docker 构建流程,移除 base image 二级构建,统一为单一 app image
- 移除 Python 相关基础设施文件 (entrypoint.sh, gunicorn_conf.py, Dockerfile.base)
2026-04-03 16:26:16 +08:00

185 lines
5.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
请求处理工具函数
提供统一的HTTP请求信息提取功能
"""
from __future__ import annotations
from dataclasses import asdict, dataclass
from fastapi import Request
TRACE_ID_HEADER = "x-trace-id"
_MISSING = object()
@dataclass(frozen=True)
class RequestIdentityMetadata:
"""请求身份元数据(最小集合)。"""
request_id: str | None
client_ip: str
user_agent: str
def get_client_ip(request: Request) -> str:
"""
获取客户端真实IP地址
按优先级检查:
1. X-Real-IP 头(最可靠,由最外层可信 Nginx 直接设置)
2. X-Forwarded-For 头的第一个 IP原始客户端
3. 直接客户端IP
安全说明:
- X-Real-IP 优先级最高,因为它通常由最外层 Nginx 设置为 $remote_addr
Nginx 会直接覆盖这个头,不会传递客户端伪造的值
- 只要最外层 Nginx 配置了 proxy_set_header X-Real-IP $remote_addr; 即可正确获取真实 IP
Args:
request: FastAPI Request 对象
Returns:
str: 客户端IP地址如果无法获取则返回 "unknown"
"""
# 优先检查 X-Real-IP 头(由最外层 Nginx 设置,最可靠)
real_ip = request.headers.get("X-Real-IP")
if real_ip:
return real_ip.strip()
# 检查 X-Forwarded-For 头,取第一个 IP原始客户端
forwarded_for = request.headers.get("X-Forwarded-For")
if forwarded_for:
# X-Forwarded-For 格式: "client, proxy1, proxy2"
ips = [ip.strip() for ip in forwarded_for.split(",") if ip.strip()]
if ips:
return ips[0]
# 回退到直接客户端IP
if request.client and request.client.host:
return request.client.host
return "unknown"
def get_user_agent(request: Request) -> str:
"""
获取用户代理字符串
Args:
request: FastAPI Request 对象
Returns:
str: User-Agent 字符串,如果无法获取则返回 "unknown"
"""
return request.headers.get("User-Agent", "unknown")
def get_request_id(request: Request) -> str | None:
"""
获取请求ID如果存在
Args:
request: FastAPI Request 对象
Returns:
Optional[str]: 请求ID如果不存在则返回 None
"""
request_id = getattr(request.state, "request_id", None)
if request_id:
return request_id
trace_id = request.headers.get(TRACE_ID_HEADER)
if trace_id:
return trace_id.strip() or None
return None
def update_request_state(
request: Request,
*,
request_id: object = _MISSING,
user_id: object = _MISSING,
api_key_id: object = _MISSING,
management_token_id: object = _MISSING,
user_session_id: object = _MISSING,
prefetched_balance_remaining: object = _MISSING,
gateway_execution_path: object = _MISSING,
rate_limit_scope: object = _MISSING,
) -> None:
"""集中维护请求级 runtime state减少散点赋值。"""
if request_id is not _MISSING:
request.state.request_id = request_id
if user_id is not _MISSING:
request.state.user_id = user_id
if api_key_id is not _MISSING:
request.state.api_key_id = api_key_id
if management_token_id is not _MISSING:
request.state.management_token_id = management_token_id
if user_session_id is not _MISSING:
request.state.user_session_id = user_session_id
if prefetched_balance_remaining is not _MISSING:
request.state.prefetched_balance_remaining = prefetched_balance_remaining
if gateway_execution_path is not _MISSING:
request.state.gateway_execution_path = gateway_execution_path
if rate_limit_scope is not _MISSING:
request.state.rate_limit_scope = rate_limit_scope
def get_request_metadata(request: Request) -> dict:
"""
获取请求的完整元数据
Args:
request: FastAPI Request 对象
Returns:
dict: 包含请求元数据的字典
"""
identity = get_request_identity_metadata(request)
return {
**asdict(identity),
"method": request.method,
"path": request.url.path,
"query_params": str(request.query_params) if request.query_params else None,
"content_type": request.headers.get("Content-Type"),
"content_length": request.headers.get("Content-Length"),
}
def get_request_identity_metadata(request: Request) -> RequestIdentityMetadata:
"""集中读取 request_id/client_ip/user_agent避免散点访问。"""
return RequestIdentityMetadata(
request_id=get_request_id(request),
client_ip=get_client_ip(request),
user_agent=get_user_agent(request),
)
def extract_ip_from_headers(headers: dict) -> str:
"""
从HTTP头字典中提取IP地址用于中间件等场景
Args:
headers: HTTP头字典
Returns:
str: 客户端IP地址
"""
# 优先检查 X-Real-IP由最外层 Nginx 设置,最可靠)
real_ip = headers.get("x-real-ip", "")
if real_ip:
return real_ip.strip()
# 检查 X-Forwarded-For取第一个 IP
forwarded_for = headers.get("x-forwarded-for", "")
if forwarded_for:
ips = [ip.strip() for ip in forwarded_for.split(",") if ip.strip()]
if ips:
return ips[0]
return "unknown"