From 9c71e8d4935885d992aadb32dfb1ea43198aaa46 Mon Sep 17 00:00:00 2001 From: kguerineau Date: Wed, 1 Apr 2026 15:23:53 +0200 Subject: [PATCH] [ADD] Add api --- check_providers_api.py | 135 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 check_providers_api.py diff --git a/check_providers_api.py b/check_providers_api.py new file mode 100644 index 0000000..043e0ad --- /dev/null +++ b/check_providers_api.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- +#------------------------------------------------------------------------------- +# Name: check_providers_api.py +# Purpose: Flask API to expose check_providers state and history +# +# Routes: +# GET /status -> current state of all providers (from JSON state file) +# GET /history -> event history from SQLite +# ?provider=NAME -> filter by provider name +# ?limit=N -> number of rows (default: 200) +# ?transitions=true -> only show up<->down transitions +# ?days=N -> limit to last N days (default: 7) +#------------------------------------------------------------------------------- + +import os +import json +import sqlite3 + +from flask import Flask, jsonify, request, Response + +app = Flask(__name__) + +BASE_DIR = '/opt/check_providers' +STATE_FILE = os.path.join(BASE_DIR, 'check-providers-state.json') +DB_PATH = os.path.join(BASE_DIR, 'check-providers.db') + + +def get_db(): + conn = sqlite3.connect(DB_PATH) + conn.row_factory = sqlite3.Row + return conn + + +# --------------------------------------------------------------------------- +# Routes +# --------------------------------------------------------------------------- + +@app.route('/status') +def status(): + """Return the current state of all providers from the JSON state file.""" + try: + with open(STATE_FILE) as f: + content = f.read() + return Response(content, mimetype='application/json') + except FileNotFoundError: + return jsonify({'error': 'No state file found — is the monitor running?'}), 503 + except Exception as e: + return jsonify({'error': str(e)}), 500 + + +@app.route('/history') +def history(): + """ + Return event history from SQLite. + + Query params: + provider : filter by provider name + limit : max rows returned (default 200, max 5000) + transitions : if 'true', only return up<->down transitions + days : only return events from the last N days (default 7) + """ + provider = request.args.get('provider') + limit = min(int(request.args.get('limit', 200)), 5000) + only_transitions = request.args.get('transitions', 'false').lower() == 'true' + days = int(request.args.get('days', 7)) + + query = ''' + SELECT id, ts, provider, available, rtt, loss, status, transition + FROM events + ''' + where, params = [], [] + + where.append("ts >= datetime('now', '-{} days')".format(days)) + + if provider: + where.append('provider = ?') + params.append(provider) + + if only_transitions: + where.append('transition = 1') + + if where: + query += ' WHERE ' + ' AND '.join(where) + + query += ' ORDER BY id DESC LIMIT ?' + params.append(limit) + + try: + with get_db() as conn: + rows = conn.execute(query, params).fetchall() + return jsonify([dict(r) for r in rows]) + except Exception as e: + return jsonify({'error': str(e)}), 500 + + +@app.route('/history/summary') +def history_summary(): + """ + Return per-provider stats: total events, transitions, last seen, avg rtt. + + Query params: + days : window in days (default 7) + """ + days = int(request.args.get('days', 7)) + query = ''' + SELECT + provider, + COUNT(*) AS total_events, + SUM(transition) AS total_transitions, + SUM(CASE WHEN available=1 THEN 1 ELSE 0 END) AS up_count, + SUM(CASE WHEN available=0 THEN 1 ELSE 0 END) AS down_count, + ROUND(AVG(rtt), 2) AS avg_rtt, + MAX(ts) AS last_seen + FROM events + WHERE ts >= datetime('now', '-{} days') + GROUP BY provider + ORDER BY provider + '''.format(days) + + try: + with get_db() as conn: + rows = conn.execute(query).fetchall() + return jsonify([dict(r) for r in rows]) + except Exception as e: + return jsonify({'error': str(e)}), 500 + + +# --------------------------------------------------------------------------- +# Entry point +# --------------------------------------------------------------------------- + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5050, debug=False) +