Files
FastapiAdmin/backend/app/core/http_limit.py
T
zhangtao 5ff72b086f refactor: 重构项目模块结构与代码细节优化
1.  调整后端模块路由与插件文件结构,迁移部分模块代码至api/v1目录
2.  优化代码注释格式与文档字符串,简化冗余代码
3.  添加监控仪表盘、工单评论等新模块功能
4.  修复前端部分组件交互逻辑与类型定义
5.  清理冗余的初始化数据与废弃配置文件
6.  新增租户注册表单字段与国际化支持
7.  优化HTTP请求拦截器与快捷键功能
2026-07-10 00:18:11 +08:00

68 lines
2.0 KiB
Python

from math import ceil
from typing import NoReturn
from fastapi import Request
from slowapi import Limiter
from slowapi.util import get_remote_address
from starlette.websockets import WebSocket
from app.config.setting import settings
# 全局限流器 —— slowapi 通过 storage_uri 创建自己的 Redis 连接
limiter = Limiter(
key_func=get_remote_address,
default_limits=["200/10seconds"],
storage_uri=settings.REDIS_URI,
)
def http_limit_callback(request: Request, response, expire: int) -> NoReturn:
"""HTTP 触发限流时的默认回调:抛出 429。"""
expires = ceil(expire / 1000)
from app.core.exceptions import CustomException
raise CustomException(
status_code=429,
msg="请求过于频繁,请稍后重试!",
data={"Retry-After": str(expires)},
)
async def ws_limit_callback(ws: WebSocket, expire: int) -> None:
"""WebSocket 触发限流时的默认回调:关闭连接。"""
expires = ceil(expire / 1000)
await ws.close(
code=1008,
reason=f"请求过于频繁,请稍后重试!{expires} 秒后重试",
)
class WebSocketRateLimiter:
"""WebSocket 限流依赖(slowapi 不内置支持 WS,自行实现)。"""
def __init__(self, max_calls: int = 200, period: int = 10):
self.max_calls = max_calls
self.period = period
async def __call__(self, ws: WebSocket) -> None:
import time
redis = getattr(ws.app.state, "redis", None)
if not redis:
return
now = int(time.time())
window = now // self.period
key = f"fastapiadmin:request_limiter:ws:{ws.client.host if ws.client else 'unknown'}:{window}"
count = await redis.incr(key)
if count == 1:
await redis.expire(key, self.period + 1)
if count > self.max_calls:
retry_after = self.period - (now % self.period) + 1
await ws.close(
code=1008,
reason=f"请求过于频繁,请稍后重试!{retry_after} 秒后重试",
)