Release v1.3.1 reliability improvements
This commit is contained in:
@@ -4,7 +4,7 @@ from urllib.parse import quote
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.responses import HTMLResponse, JSONResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
|
||||
from scanner import (
|
||||
@@ -13,6 +13,7 @@ from scanner import (
|
||||
MARKET_CONFIGS,
|
||||
MARKET_CRYPTO,
|
||||
MARKET_TRADFI,
|
||||
count_current_signals,
|
||||
db_connect,
|
||||
init_db,
|
||||
)
|
||||
@@ -21,8 +22,8 @@ from scanner import (
|
||||
app = FastAPI(title="Crypto ATR Signal")
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
MADRID_TZ = ZoneInfo("Europe/Madrid")
|
||||
APP_VERSION = "v1.3.0"
|
||||
APP_RELEASE_DATE = "2026-06-22"
|
||||
APP_VERSION = "v1.3.1"
|
||||
APP_RELEASE_DATE = "2026-06-23"
|
||||
APP_AUTHOR = "Z"
|
||||
|
||||
|
||||
@@ -36,6 +37,7 @@ PAGE_SHOW_CRYPTO = env_bool("PAGE_SHOW_CRYPTO", True)
|
||||
PAGE_SHOW_TRADFI = env_bool("PAGE_SHOW_TRADFI", True)
|
||||
PAGE_GROUP_BY_MARKET = env_bool("PAGE_GROUP_BY_MARKET", True)
|
||||
PAGE_SHOW_VERSION = env_bool("PAGE_SHOW_VERSION", True)
|
||||
HEALTH_MAX_SCAN_AGE_HOURS = float(os.getenv("HEALTH_MAX_SCAN_AGE_HOURS", "8"))
|
||||
|
||||
|
||||
def fmt_decimal(value: str | None, places: int = 6) -> str:
|
||||
@@ -104,6 +106,75 @@ def startup() -> None:
|
||||
conn.close()
|
||||
|
||||
|
||||
@app.get("/health", response_class=JSONResponse)
|
||||
def health() -> JSONResponse:
|
||||
checked_at = datetime.now(timezone.utc)
|
||||
payload = {
|
||||
"status": "ok",
|
||||
"version": APP_VERSION,
|
||||
"checked_at": checked_at.isoformat(),
|
||||
"database": "ok",
|
||||
"markets": {},
|
||||
}
|
||||
healthy = True
|
||||
conn = None
|
||||
try:
|
||||
conn = db_connect(DB_PATH)
|
||||
integrity = conn.execute("PRAGMA quick_check;").fetchone()[0]
|
||||
if integrity != "ok":
|
||||
payload["database"] = integrity
|
||||
healthy = False
|
||||
|
||||
for market_type, slug in ((MARKET_CRYPTO, "crypto"), (MARKET_TRADFI, "tradfi")):
|
||||
latest_run = conn.execute(
|
||||
"""
|
||||
SELECT status, finished_at, symbols_failed, error_summary
|
||||
FROM scan_runs
|
||||
WHERE market_type = ?
|
||||
ORDER BY finished_at DESC
|
||||
LIMIT 1
|
||||
""",
|
||||
(market_type,),
|
||||
).fetchone()
|
||||
if latest_run:
|
||||
finished_at = datetime.fromisoformat(latest_run["finished_at"])
|
||||
age_hours = max(0.0, (checked_at - finished_at).total_seconds() / 3600)
|
||||
market_healthy = (
|
||||
latest_run["status"] == "success"
|
||||
and age_hours <= HEALTH_MAX_SCAN_AGE_HOURS
|
||||
)
|
||||
payload["markets"][slug] = {
|
||||
"status": latest_run["status"],
|
||||
"last_scan_at": finished_at.isoformat(),
|
||||
"age_hours": round(age_hours, 2),
|
||||
"failed_symbols": latest_run["symbols_failed"],
|
||||
"current_signals": count_current_signals(conn, market_type),
|
||||
"error": latest_run["error_summary"],
|
||||
}
|
||||
else:
|
||||
market_healthy = False
|
||||
payload["markets"][slug] = {
|
||||
"status": "unavailable",
|
||||
"last_scan_at": None,
|
||||
"age_hours": None,
|
||||
"failed_symbols": None,
|
||||
"current_signals": count_current_signals(conn, market_type),
|
||||
"error": "no scan history",
|
||||
}
|
||||
healthy = healthy and market_healthy
|
||||
except Exception as exc: # noqa: BLE001 - health endpoint must report failures as JSON.
|
||||
healthy = False
|
||||
payload["database"] = "error"
|
||||
payload["error"] = str(exc)
|
||||
finally:
|
||||
if conn is not None:
|
||||
conn.close()
|
||||
|
||||
if not healthy:
|
||||
payload["status"] = "degraded"
|
||||
return JSONResponse(payload, status_code=200 if healthy else 503)
|
||||
|
||||
|
||||
def load_market_view(
|
||||
conn, market_type: str, sort_sql: str
|
||||
) -> dict:
|
||||
|
||||
Reference in New Issue
Block a user