Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions backend/app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from app.rate_limit import setup_rate_limiting

# Initialize FastAPI with title
api = FastAPI(title="Eigent Multi-Agent System API")

Expand All @@ -26,3 +28,4 @@
allow_methods=["*"],
allow_headers=["*"],
)
setup_rate_limiting(api)
3 changes: 3 additions & 0 deletions backend/app/controller/health_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
from fastapi import APIRouter
from pydantic import BaseModel

from app.rate_limit import limiter

logger = logging.getLogger("health_controller")

router = APIRouter(tags=["Health"])
Expand All @@ -28,6 +30,7 @@ class HealthResponse(BaseModel):


@router.get("/health", name="health check", response_model=HealthResponse)
@limiter.exempt
async def health_check():
"""Health check endpoint for verifying backend
is ready to accept requests."""
Expand Down
58 changes: 58 additions & 0 deletions backend/app/rate_limit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
"""
Rate limiting for API endpoints to mitigate abuse and DoS risks.
Uses SlowAPI with in-memory storage (configurable via RATE_LIMIT_DEFAULT).
"""

import logging

from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.errors import RateLimitExceeded
from slowapi.middleware import SlowAPIMiddleware
from slowapi.util import get_remote_address

logger = logging.getLogger("rate_limit")


def _get_default_limits() -> list[str]:
"""Parse RATE_LIMIT_DEFAULT env (e.g. '100/minute') into slowapi format."""
from app.component.environment import env

default = env("RATE_LIMIT_DEFAULT", "100/minute")
if not default or env("RATE_LIMIT_ENABLED", "true").lower() in ("false", "0", "no"):
return []
return [default.strip()]


limiter = Limiter(
key_func=get_remote_address,
default_limits=_get_default_limits(),
)


def setup_rate_limiting(api):
"""
Register rate limiting middleware and exception handler on the FastAPI app.
Call this after creating the app and before including routers.
"""
limits = _get_default_limits()
if not limits:
logger.info("Rate limiting disabled (RATE_LIMIT_ENABLED=false or empty RATE_LIMIT_DEFAULT)")
return

api.state.limiter = limiter
api.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
api.add_middleware(SlowAPIMiddleware)
logger.info("Rate limiting enabled: %s", limits)
1 change: 1 addition & 0 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ dependencies = [
"opentelemetry-api>=1.34.1",
"opentelemetry-sdk>=1.34.1",
"opentelemetry-exporter-otlp-proto-http>=1.34.1",
"slowapi>=0.1.9",
]


Expand Down
4 changes: 4 additions & 0 deletions server/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ debug=false
url_prefix=/api
secret_key=postgres
database_url=postgresql://postgres:123456@localhost:5432/postgres
# Rate limiting (mitigates abuse and DoS)
# RATE_LIMIT_ENABLED=true
# RATE_LIMIT_DEFAULT=100/minute

# Chat Share Secret Key
CHAT_SHARE_SECRET_KEY=put-your-secret-key-here
CHAT_SHARE_SALT=put-your-encode-salt-here
3 changes: 3 additions & 0 deletions server/app/controller/health_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
from fastapi import APIRouter
from pydantic import BaseModel

from app.rate_limit import limiter

router = APIRouter(tags=["Health"])


Expand All @@ -24,6 +26,7 @@ class HealthResponse(BaseModel):


@router.get("/health", name="health check", response_model=HealthResponse)
@limiter.exempt
async def health_check():
"""Health check endpoint for monitoring and container orchestration."""
return HealthResponse(status="ok", service="eigent-server")
2 changes: 2 additions & 0 deletions server/app/middleware/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@

from app import api
from app.component.babel import babel_configs
from app.rate_limit import setup_rate_limiting

api.add_middleware(BabelMiddleware, babel_configs=babel_configs)
setup_rate_limiting(api)
58 changes: 58 additions & 0 deletions server/app/rate_limit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
"""
Rate limiting for API endpoints to mitigate abuse and DoS risks.
Uses SlowAPI with in-memory storage (configurable via RATE_LIMIT_DEFAULT).
"""

import logging

from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.errors import RateLimitExceeded
from slowapi.middleware import SlowAPIMiddleware
from slowapi.util import get_remote_address

logger = logging.getLogger("rate_limit")


def _get_default_limits() -> list[str]:
"""Parse RATE_LIMIT_DEFAULT env (e.g. '100/minute') into slowapi format."""
from app.component.environment import env

default = env("RATE_LIMIT_DEFAULT", "100/minute")
if not default or env("RATE_LIMIT_ENABLED", "true").lower() in ("false", "0", "no"):
return []
return [default.strip()]


limiter = Limiter(
key_func=get_remote_address,
default_limits=_get_default_limits(),
)


def setup_rate_limiting(api):
"""
Register rate limiting middleware and exception handler on the FastAPI app.
Call this after creating the app and before including routers.
"""
limits = _get_default_limits()
if not limits:
logger.info("Rate limiting disabled (RATE_LIMIT_ENABLED=false or empty RATE_LIMIT_DEFAULT)")
return

api.state.limiter = limiter
api.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
api.add_middleware(SlowAPIMiddleware)
logger.info("Rate limiting enabled: %s", limits)
1 change: 1 addition & 0 deletions server/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ dependencies = [
"cryptography>=45.0.4",
"sqids>=0.5.2",
"exa-py>=1.14.16",
"slowapi>=0.1.9",
]

[tool.ruff]
Expand Down