major-domo-database/app/main.py
Cal Corum 16f3f8d8de
All checks were successful
Build Docker Image / build (pull_request) Successful in 2m32s
Fix unbounded API queries causing Gunicorn worker timeouts
Add MAX_LIMIT=500 cap across all list endpoints, empty string
stripping middleware, and limit/offset to /transactions. Resolves #98.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 17:23:25 -05:00

132 lines
3.6 KiB
Python

import datetime
import logging
from logging.handlers import RotatingFileHandler
import os
from urllib.parse import parse_qsl, urlencode
from fastapi import Depends, FastAPI, Request
from fastapi.openapi.docs import get_swagger_ui_html
from fastapi.openapi.utils import get_openapi
# from fastapi.openapi.docs import get_swagger_ui_html
# from fastapi.openapi.utils import get_openapi
from .routers_v3 import (
current,
players,
results,
schedules,
standings,
teams,
transactions,
battingstats,
pitchingstats,
fieldingstats,
draftpicks,
draftlist,
managers,
awards,
draftdata,
keepers,
stratgame,
stratplay,
injuries,
decisions,
divisions,
sbaplayers,
custom_commands,
help_commands,
views,
)
# date = f'{datetime.datetime.now().year}-{datetime.datetime.now().month}-{datetime.datetime.now().day}'
log_level = logging.INFO if os.environ.get("LOG_LEVEL") == "INFO" else logging.WARNING
# logging.basicConfig(
# filename=f'logs/database/{date}.log',
# format='%(asctime)s - sba-database - %(levelname)s - %(message)s',
# level=log_level
# )
logger = logging.getLogger("discord_app")
logger.setLevel(log_level)
handler = RotatingFileHandler(
filename="./logs/sba-database.log",
# encoding='utf-8',
maxBytes=8 * 1024 * 1024, # 8 MiB
backupCount=5, # Rotate through 5 files
)
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)
app = FastAPI(
# root_path='/api',
responses={404: {"description": "Not found"}},
docs_url="/api/docs",
redoc_url="/api/redoc",
)
logger.info(f"Starting up now...")
@app.middleware("http")
async def strip_empty_query_params(request: Request, call_next):
qs = request.scope.get("query_string", b"")
if qs:
pairs = parse_qsl(qs.decode(), keep_blank_values=True)
filtered = [(k, v) for k, v in pairs if v != ""]
new_qs = urlencode(filtered).encode()
request.scope["query_string"] = new_qs
if hasattr(request, "_query_params"):
del request._query_params
return await call_next(request)
app.include_router(current.router)
app.include_router(players.router)
app.include_router(results.router)
app.include_router(schedules.router)
app.include_router(teams.router)
app.include_router(transactions.router)
app.include_router(standings.router)
app.include_router(battingstats.router)
app.include_router(pitchingstats.router)
app.include_router(fieldingstats.router)
app.include_router(draftpicks.router)
app.include_router(draftlist.router)
app.include_router(managers.router)
app.include_router(awards.router)
app.include_router(draftdata.router)
app.include_router(keepers.router)
app.include_router(stratgame.router)
app.include_router(stratplay.router)
app.include_router(injuries.router)
app.include_router(decisions.router)
app.include_router(divisions.router)
app.include_router(sbaplayers.router)
app.include_router(custom_commands.router)
app.include_router(help_commands.router)
app.include_router(views.router)
logger.info(f"Loaded all routers.")
@app.get("/api/docs", include_in_schema=False)
async def get_docs(req: Request):
logger.debug(req.scope)
return get_swagger_ui_html(
openapi_url=req.scope.get("root_path") + "/openapi.json", title="Swagger"
)
@app.get("/api/openapi.json", include_in_schema=False)
async def openapi():
return get_openapi(title="SBa API Docs", version=f"0.1.1", routes=app.routes)
# @app.get("/api")
# async def root():
# return {"message": "Hello Bigger Applications!"}