mirror of
https://github.com/shawnkim1997/All-in-one-Financial-Analysis.git
synced 2026-08-16 11:58:07 +00:00
125 lines
4.6 KiB
Python
125 lines
4.6 KiB
Python
"""
|
|
ATLAS Terminal — FastAPI Backend
|
|
Unified entry point with PostgreSQL + SQLite support.
|
|
"""
|
|
import json
|
|
import math
|
|
import os
|
|
import logging
|
|
from contextlib import asynccontextmanager
|
|
from typing import Any
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.responses import JSONResponse
|
|
|
|
|
|
class _NanSafeEncoder(json.JSONEncoder):
|
|
"""Replace NaN/Inf with None so JSON serialization never crashes."""
|
|
|
|
def default(self, o: Any) -> Any:
|
|
return super().default(o)
|
|
|
|
def encode(self, o: Any) -> str:
|
|
return super().encode(_sanitize(o))
|
|
|
|
|
|
def _sanitize(obj: Any) -> Any:
|
|
if isinstance(obj, float):
|
|
if math.isnan(obj) or math.isinf(obj):
|
|
return None
|
|
return obj
|
|
if isinstance(obj, dict):
|
|
return {k: _sanitize(v) for k, v in obj.items()}
|
|
if isinstance(obj, (list, tuple)):
|
|
return [_sanitize(v) for v in obj]
|
|
return obj
|
|
|
|
|
|
class NanSafeJSONResponse(JSONResponse):
|
|
def render(self, content: Any) -> bytes:
|
|
return json.dumps(
|
|
_sanitize(content),
|
|
ensure_ascii=False,
|
|
separators=(",", ":"),
|
|
).encode("utf-8")
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
"""Initialize database on startup, close on shutdown."""
|
|
from server.db.unified_repo import repo
|
|
await repo.init_db()
|
|
logger.info("ATLAS Terminal backend started.")
|
|
yield
|
|
await repo.close_db()
|
|
logger.info("ATLAS Terminal backend stopped.")
|
|
|
|
|
|
app = FastAPI(
|
|
title="ATLAS Terminal API",
|
|
description="Personal Bloomberg Terminal — Hybrid AI + Quantitative Analysis",
|
|
version="2.0.0",
|
|
lifespan=lifespan,
|
|
default_response_class=NanSafeJSONResponse,
|
|
)
|
|
|
|
# CORS — allow local frontend
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=[
|
|
"http://localhost:3000",
|
|
"http://localhost:3001",
|
|
"http://127.0.0.1:3000",
|
|
],
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
# --- Mount routers ---
|
|
from server.routers import edgar, analysis, valuation, market_data, news, crypto, fx, portfolio, technical, financials, estimates, earnings, insider, screener, markets, fmp, macro, dart, edinet, research, chat, copilot, credentials, calendar, tax # noqa: E402
|
|
|
|
app.include_router(edgar.router, prefix="/api/edgar", tags=["SEC EDGAR"])
|
|
app.include_router(analysis.router, prefix="/api/analysis", tags=["AI Analysis"])
|
|
app.include_router(valuation.router, prefix="/api/valuation", tags=["Valuation"])
|
|
app.include_router(market_data.router, prefix="/api/market", tags=["Market Data"])
|
|
app.include_router(financials.router, prefix="/api/financials", tags=["Financials"])
|
|
app.include_router(estimates.router, prefix="/api/estimates", tags=["Estimates"])
|
|
app.include_router(news.router, prefix="/api/news", tags=["News"])
|
|
app.include_router(crypto.router, prefix="/api/crypto", tags=["Crypto"])
|
|
app.include_router(fx.router, prefix="/api/fx", tags=["FX"])
|
|
app.include_router(markets.router, prefix="/api/markets", tags=["Markets"])
|
|
app.include_router(portfolio.router, prefix="/api/portfolio", tags=["Portfolio"])
|
|
app.include_router(technical.router, prefix="/api/technical", tags=["Technical"])
|
|
app.include_router(earnings.router, prefix="/api/earnings", tags=["Earnings"])
|
|
app.include_router(insider.router, prefix="/api/insider", tags=["Insider Trading"])
|
|
app.include_router(screener.router, prefix="/api/screener", tags=["Screener"])
|
|
app.include_router(fmp.router, prefix="/api/fmp", tags=["FMP"])
|
|
app.include_router(macro.router, prefix="/api/macro", tags=["Macro"])
|
|
app.include_router(dart.router, prefix="/api/dart", tags=["DART"])
|
|
app.include_router(edinet.router, prefix="/api/edinet", tags=["EDINET"])
|
|
app.include_router(research.router, prefix="/api/research", tags=["Research"])
|
|
app.include_router(chat.router, prefix="/api/chat", tags=["Chat"])
|
|
app.include_router(copilot.router, prefix="/api/copilot", tags=["Copilot"])
|
|
app.include_router(credentials.router, prefix="/api/credentials", tags=["Credentials"])
|
|
app.include_router(calendar.router, prefix="/api/calendar", tags=["Calendar"])
|
|
app.include_router(tax.router, prefix="/api/tax", tags=["Tax"])
|
|
|
|
|
|
@app.get("/health")
|
|
async def health_check():
|
|
"""Health check endpoint."""
|
|
db_type = "postgresql" if os.getenv("DATABASE_URL") else "sqlite"
|
|
return {"status": "ok", "db": db_type, "version": "2.0.0"}
|
|
|
|
|
|
@app.get("/api/health")
|
|
async def api_health_check():
|
|
"""Health check endpoint (via /api prefix for Next.js proxy)."""
|
|
db_type = "postgresql" if os.getenv("DATABASE_URL") else "sqlite"
|
|
return {"status": "ok", "db": db_type, "version": "2.0.0"}
|