File size: 2,191 Bytes
3f2df04
9f09117
36d7e96
fd0bd3b
 
 
36d7e96
fd0bd3b
9f09117
fd0bd3b
 
 
 
 
3f2df04
36d7e96
3f2df04
fd0bd3b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36d7e96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fd0bd3b
 
 
 
9f09117
 
 
 
 
 
 
 
 
 
 
 
fd0bd3b
 
9f09117
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
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__)


@asynccontextmanager
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)


@app.exception_handler(Exception)
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)
)


@app.get("/", include_in_schema=False)
async def index():
    return HTMLResponse(content=_INDEX_HTML)