feat(api): add rate limiting for API requests
This commit is contained in:
@@ -28,6 +28,7 @@ DEBUG=false
|
||||
CORS_URLS='[]'
|
||||
SERVER_URL="http://localhost:8000"
|
||||
FRONTEND_URL=
|
||||
ENABLE_RATE_LIMIT=true
|
||||
|
||||
# SignalR Settings
|
||||
SIGNALR_NEGOTIATE_TIMEOUT=30
|
||||
|
||||
@@ -93,6 +93,7 @@ class Settings(BaseSettings):
|
||||
cors_urls: list[HttpUrl] = []
|
||||
server_url: HttpUrl = HttpUrl("http://localhost:8000")
|
||||
frontend_url: HttpUrl | None = None
|
||||
enable_rate_limit: bool = True
|
||||
|
||||
@property
|
||||
def web_url(self):
|
||||
|
||||
30
main.py
30
main.py
@@ -4,7 +4,7 @@ from contextlib import asynccontextmanager
|
||||
from pathlib import Path
|
||||
|
||||
from app.config import settings
|
||||
from app.dependencies.database import engine, redis_client
|
||||
from app.dependencies.database import engine, get_redis, redis_client
|
||||
from app.dependencies.fetcher import get_fetcher
|
||||
from app.dependencies.scheduler import start_scheduler, stop_scheduler
|
||||
from app.log import logger
|
||||
@@ -37,16 +37,19 @@ from app.service.osu_rx_statistics import create_rx_statistics
|
||||
from app.service.redis_message_system import redis_message_system
|
||||
from app.utils import bg_tasks, utcnow
|
||||
|
||||
from fastapi import FastAPI, HTTPException, Request
|
||||
from fastapi import Depends, FastAPI, HTTPException, Request
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi_limiter import FastAPILimiter
|
||||
from fastapi_limiter.depends import RateLimiter
|
||||
import sentry_sdk
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
# on startup
|
||||
await FastAPILimiter.init(get_redis())
|
||||
await get_fetcher() # 初始化 fetcher
|
||||
await init_geoip() # 初始化 GeoIP 数据库
|
||||
await create_rx_statistics()
|
||||
@@ -80,7 +83,7 @@ async def lifespan(app: FastAPI):
|
||||
await redis_client.aclose()
|
||||
|
||||
|
||||
desc = """osu! API 模拟服务器,支持 osu! API v1, v2 和 osu!lazer 的绝大部分功能。
|
||||
desc = f"""osu! API 模拟服务器,支持 osu! API v1, v2 和 osu!lazer 的绝大部分功能。
|
||||
|
||||
## 端点说明
|
||||
|
||||
@@ -100,6 +103,19 @@ v2 API 采用 OAuth 2.0 鉴权,支持以下鉴权方式:
|
||||
|
||||
v1 API 采用 API Key 鉴权,将 API Key 放入 Query `k` 中。
|
||||
|
||||
{
|
||||
'''
|
||||
## 速率限制
|
||||
|
||||
所有 API 请求均受到速率限制,具体限制规则如下:
|
||||
|
||||
- 每分钟最多可以发送 1200 个请求
|
||||
- 突发请求限制为每秒最多 200 个请求
|
||||
'''
|
||||
if settings.enable_rate_limit
|
||||
else ""
|
||||
}
|
||||
|
||||
## 参考
|
||||
|
||||
- v2 API 文档:[osu-web 文档](https://osu.ppy.sh/docs/index.html)
|
||||
@@ -136,6 +152,14 @@ app = FastAPI(
|
||||
lifespan=lifespan,
|
||||
description=desc,
|
||||
)
|
||||
if settings.enable_rate_limit:
|
||||
app.router.dependencies.extend(
|
||||
[
|
||||
Depends(RateLimiter(times=1200, minutes=1)),
|
||||
Depends(RateLimiter(times=200, seconds=1)),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
app.include_router(api_v2_router)
|
||||
app.include_router(api_v1_router)
|
||||
|
||||
@@ -13,6 +13,7 @@ dependencies = [
|
||||
"bcrypt>=4.1.2",
|
||||
"cryptography>=41.0.7",
|
||||
"fastapi>=0.104.1",
|
||||
"fastapi-limiter>=0.1.6",
|
||||
"httpx>=0.28.1",
|
||||
"loguru>=0.7.3",
|
||||
"maxminddb>=2.8.2",
|
||||
|
||||
15
uv.lock
generated
15
uv.lock
generated
@@ -459,6 +459,19 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/47/d63c60f59a59467fda0f93f46335c9d18526d7071f025cb5b89d5353ea42/fastapi-0.116.1-py3-none-any.whl", hash = "sha256:c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565", size = 95631, upload-time = "2025-07-11T16:22:30.485Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastapi-limiter"
|
||||
version = "0.1.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "fastapi" },
|
||||
{ name = "redis" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7f/99/c7903234488d4dca5f9bccb4f88c2f582a234f0dca33348781c9cf8a48c6/fastapi_limiter-0.1.6.tar.gz", hash = "sha256:6f5fde8efebe12eb33861bdffb91009f699369a3c2862cdc7c1d9acf912ff443", size = 8307, upload-time = "2024-01-05T09:14:48.628Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/b5/6f6b4d18bee1cafc857eae12738b3a03b7d1102b833668be868938c57b9d/fastapi_limiter-0.1.6-py3-none-any.whl", hash = "sha256:2e53179a4208b8f2c8795e38bb001324d3dc37d2800ff49fd28ec5caabf7a240", size = 15829, upload-time = "2024-01-05T09:14:47.613Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filelock"
|
||||
version = "3.19.1"
|
||||
@@ -541,6 +554,7 @@ dependencies = [
|
||||
{ name = "bcrypt" },
|
||||
{ name = "cryptography" },
|
||||
{ name = "fastapi" },
|
||||
{ name = "fastapi-limiter" },
|
||||
{ name = "httpx" },
|
||||
{ name = "loguru" },
|
||||
{ name = "maxminddb" },
|
||||
@@ -581,6 +595,7 @@ requires-dist = [
|
||||
{ name = "bcrypt", specifier = ">=4.1.2" },
|
||||
{ name = "cryptography", specifier = ">=41.0.7" },
|
||||
{ name = "fastapi", specifier = ">=0.104.1" },
|
||||
{ name = "fastapi-limiter", specifier = ">=0.1.6" },
|
||||
{ name = "httpx", specifier = ">=0.28.1" },
|
||||
{ name = "loguru", specifier = ">=0.7.3" },
|
||||
{ name = "maxminddb", specifier = ">=2.8.2" },
|
||||
|
||||
Reference in New Issue
Block a user