From c9f53d0ecec7b0df033e3b64efec5d8b86fac3d4 Mon Sep 17 00:00:00 2001 From: Nolan Kovacs Date: Thu, 26 Mar 2026 11:06:56 -0400 Subject: [PATCH] Fix NaN serialization in cache endpoints --- api/main.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/api/main.py b/api/main.py index d880841..2ca2f17 100644 --- a/api/main.py +++ b/api/main.py @@ -1,3 +1,4 @@ +import math import os import hmac import hashlib @@ -55,6 +56,14 @@ def verify_jwt(authorization: Optional[str] = Header(None)): raise HTTPException(status_code=401, detail="Invalid token") +def _clean(row: dict) -> dict: + """Convert NaN/Inf floats → None so FastAPI can serialize the row to JSON.""" + return { + k: (None if isinstance(v, float) and not math.isfinite(v) else v) + for k, v in row.items() + } + + def verify_admin(authorization: Optional[str] = Header(None)): if not authorization or authorization != f"Bearer {ADMIN_KEY}": raise HTTPException(status_code=401, detail="Unauthorized") @@ -117,7 +126,7 @@ def get_analyst_cache(payload=Depends(verify_jwt), db=Depends(get_db)): cur = db.cursor(cursor_factory=psycopg2.extras.RealDictCursor) table = "beta_analyst_cache" if payload.get("channel") == "beta" else "analyst_cache" cur.execute(f"SELECT * FROM {table}") - return {"data": cur.fetchall()} + return {"data": [_clean(dict(r)) for r in cur.fetchall()]} @app.get("/cache/fundamentals") @@ -125,14 +134,15 @@ def get_fundamentals_cache(payload=Depends(verify_jwt), db=Depends(get_db)): cur = db.cursor(cursor_factory=psycopg2.extras.RealDictCursor) table = "beta_fundamentals_cache" if payload.get("channel") == "beta" else "fundamentals_cache" cur.execute(f"SELECT * FROM {table}") - return {"data": cur.fetchall()} + return {"data": [_clean(dict(r)) for r in cur.fetchall()]} @app.get("/cache/sector") def get_sector_stats(payload=Depends(verify_jwt), db=Depends(get_db)): cur = db.cursor(cursor_factory=psycopg2.extras.RealDictCursor) - cur.execute("SELECT * FROM sector_stats ORDER BY sector") - return {"data": cur.fetchall()} + table = "beta_sector_stats" if payload.get("channel") == "beta" else "sector_stats" + cur.execute(f"SELECT * FROM {table} ORDER BY sector") + return {"data": [_clean(dict(r)) for r in cur.fetchall()]} @app.get("/update")