Spaces:
Running
Running
| import logging | |
| import time | |
| import traceback | |
| from contextlib import asynccontextmanager | |
| from pathlib import Path | |
| from fastapi import FastAPI, Request | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.responses import HTMLResponse, JSONResponse | |
| from fastapi.staticfiles import StaticFiles | |
| from app.routers.api import router as api_router | |
| from app.services.analytics import init_db | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| async def lifespan(app: FastAPI): | |
| init_db() | |
| yield | |
| app = FastAPI( | |
| title="NEM Battery SCADA Data Explorer", | |
| description="Explore 4-second BESS SCADA data from Australia's National Electricity Market.", | |
| version="1.0.0", | |
| lifespan=lifespan, | |
| ) | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_methods=["GET"], | |
| allow_headers=["*"], | |
| ) | |
| app.include_router(api_router) | |
| async def unhandled_exception_handler(request: Request, exc: Exception) -> JSONResponse: | |
| """ | |
| Catch-all for any exception not already handled by a specific handler or | |
| HTTPException. Logs the full traceback so silent 500s become visible in | |
| the application log, then returns a JSON 500 response. | |
| """ | |
| logger.error( | |
| "Unhandled exception on %s %s\n%s", | |
| request.method, | |
| request.url, | |
| traceback.format_exc(), | |
| ) | |
| return JSONResponse( | |
| status_code=500, | |
| content={"detail": "An internal server error occurred. Please try again."}, | |
| ) | |
| STATIC_DIR = Path(__file__).parent / "static" | |
| app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static") | |
| # Bust the browser cache for /static/js/app.js and /static/css/style.css on | |
| # every container start so deploys don't get served stale assets. Each new | |
| # container produces a new __VERSION__ value; the HTML is rendered once at | |
| # startup and served from memory. | |
| _BUILD_VERSION = str(int(time.time())) | |
| _INDEX_HTML = ( | |
| (STATIC_DIR / "index.html") | |
| .read_text() | |
| .replace("__VERSION__", _BUILD_VERSION) | |
| ) | |
| async def index(): | |
| return HTMLResponse(content=_INDEX_HTML) | |