diff options
| author | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2026-04-02 15:48:46 +0900 |
|---|---|---|
| committer | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2026-04-02 15:48:46 +0900 |
| commit | 13c939468ed0143e4a4f9ee1c0b847483dcd8199 (patch) | |
| tree | e5318ae8973c7a54fe1de81117f56f91329b884a /services/api/src/trading_api/routers | |
| parent | 776376dda8005635c4c3365905ca7df857789fec (diff) | |
feat: add API security (auth, CORS, rate limiting, input validation)
- Add Bearer token authentication via API_AUTH_TOKEN (disabled when unset)
- Add CORS middleware with configurable origins
- Add rate limiting (60/min) on order and signal endpoints via slowapi
- Add Query parameter bounds: orders/signals limit 1-1000, snapshots days 1-365
Diffstat (limited to 'services/api/src/trading_api/routers')
| -rw-r--r-- | services/api/src/trading_api/routers/orders.py | 11 | ||||
| -rw-r--r-- | services/api/src/trading_api/routers/portfolio.py | 4 |
2 files changed, 10 insertions, 5 deletions
diff --git a/services/api/src/trading_api/routers/orders.py b/services/api/src/trading_api/routers/orders.py index a29ae2f..217efef 100644 --- a/services/api/src/trading_api/routers/orders.py +++ b/services/api/src/trading_api/routers/orders.py @@ -2,18 +2,22 @@ import logging -from fastapi import APIRouter, HTTPException, Request +from fastapi import APIRouter, HTTPException, Query, Request from shared.sa_models import OrderRow, SignalRow +from slowapi import Limiter +from slowapi.util import get_remote_address from sqlalchemy import select from sqlalchemy.exc import OperationalError logger = logging.getLogger(__name__) router = APIRouter() +limiter = Limiter(key_func=get_remote_address) @router.get("/") -async def get_orders(request: Request, limit: int = 50): +@limiter.limit("60/minute") +async def get_orders(request: Request, limit: int = Query(50, ge=1, le=1000)): """Get recent orders.""" try: db = request.app.state.db @@ -45,7 +49,8 @@ async def get_orders(request: Request, limit: int = 50): @router.get("/signals") -async def get_signals(request: Request, limit: int = 50): +@limiter.limit("60/minute") +async def get_signals(request: Request, limit: int = Query(50, ge=1, le=1000)): """Get recent signals.""" try: db = request.app.state.db diff --git a/services/api/src/trading_api/routers/portfolio.py b/services/api/src/trading_api/routers/portfolio.py index 3907a86..fde90cb 100644 --- a/services/api/src/trading_api/routers/portfolio.py +++ b/services/api/src/trading_api/routers/portfolio.py @@ -2,7 +2,7 @@ import logging -from fastapi import APIRouter, HTTPException, Request +from fastapi import APIRouter, HTTPException, Query, Request from shared.sa_models import PositionRow from sqlalchemy import select from sqlalchemy.exc import OperationalError @@ -39,7 +39,7 @@ async def get_positions(request: Request): @router.get("/snapshots") -async def get_snapshots(request: Request, days: int = 30): +async def get_snapshots(request: Request, days: int = Query(30, ge=1, le=365)): """Get portfolio snapshots for the last N days.""" try: db = request.app.state.db |
