99 lines
2.6 KiB
Python
99 lines
2.6 KiB
Python
"""
|
|
FlightAware tracks service.
|
|
- GET /health — healthcheck
|
|
- POST /run?date=YYYY-MM-DD — manual trigger
|
|
"""
|
|
import logging
|
|
import sys
|
|
import time
|
|
from datetime import date, timedelta, datetime, timezone
|
|
|
|
import psycopg2
|
|
import psycopg2.extras
|
|
from flask import Flask, jsonify, request
|
|
|
|
from config import config
|
|
from fa_worker import run as worker_run
|
|
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format="%(asctime)s [tracks-fa] %(levelname)s %(message)s",
|
|
datefmt="%Y-%m-%dT%H:%M:%S",
|
|
handlers=[logging.StreamHandler(sys.stdout)],
|
|
)
|
|
log = logging.getLogger("tracks_fa")
|
|
|
|
app = Flask(__name__)
|
|
|
|
_last_run: dict = {"at": None, "status": "never", "stats": {}}
|
|
_conn = None
|
|
|
|
|
|
def get_conn():
|
|
global _conn
|
|
if _conn is None or _conn.closed:
|
|
_conn = psycopg2.connect(config.DB_DSN)
|
|
psycopg2.extras.register_uuid(_conn)
|
|
log.info("DB connection established")
|
|
return _conn
|
|
|
|
|
|
def wait_for_db(max_attempts: int = 30):
|
|
for attempt in range(1, max_attempts + 1):
|
|
try:
|
|
get_conn()
|
|
return
|
|
except psycopg2.OperationalError as e:
|
|
log.warning("DB not ready (%d/%d): %s", attempt, max_attempts, e)
|
|
time.sleep(3)
|
|
raise SystemExit("Could not connect to DB")
|
|
|
|
|
|
@app.get("/health")
|
|
def health():
|
|
try:
|
|
get_conn().cursor().execute("SELECT 1")
|
|
db_ok = True
|
|
except Exception:
|
|
db_ok = False
|
|
return jsonify({
|
|
"status": "ok" if db_ok else "degraded",
|
|
"db": "ok" if db_ok else "error",
|
|
"last_run": _last_run,
|
|
}), 200 if db_ok else 503
|
|
|
|
|
|
@app.post("/run")
|
|
def run_manual():
|
|
date_str = request.args.get("date")
|
|
if date_str:
|
|
try:
|
|
target = date.fromisoformat(date_str)
|
|
except ValueError:
|
|
return jsonify({"error": "invalid date, use YYYY-MM-DD"}), 400
|
|
else:
|
|
target = date.today() - timedelta(days=1)
|
|
|
|
if _last_run.get("status") == "running":
|
|
return jsonify({"error": "already running"}), 409
|
|
|
|
_last_run["at"] = datetime.now(timezone.utc).isoformat()
|
|
_last_run["status"] = "running"
|
|
|
|
try:
|
|
conn = get_conn()
|
|
stats = worker_run(target, conn)
|
|
_last_run.update(status="ok", stats=stats)
|
|
return jsonify({"status": "ok", "stats": stats})
|
|
except Exception as e:
|
|
_last_run["status"] = f"error: {e}"
|
|
log.error("run failed: %s", e)
|
|
return jsonify({"error": str(e)}), 500
|
|
|
|
|
|
if __name__ == "__main__":
|
|
wait_for_db()
|
|
open("/tmp/ready", "w").close()
|
|
log.info("Starting FA tracks service on port 8002")
|
|
app.run(host="0.0.0.0", port=8002, debug=False)
|