From 60e12cc4ddf83271b9a3d08fc90c3b1ed858b929 Mon Sep 17 00:00:00 2001 From: CTO H3R7Tech Date: Sun, 24 May 2026 11:29:33 +0200 Subject: [PATCH] feat(HRT-226): Dashboard consommation IA + alertes visuelles - 3 nouvelles pages HTML servies sous /dashboard/consumption* - Proxy routes /api/v1/consumption/* -> port 8784 (consumption-tracker) - Proxy routes /api/v1/ai/usage* -> port 8783 (AI Router) - consumption_dashboard.html: KPI cards, Chart.js tokens/cost/provider/calls charts, period selector 24h/7j/30j, auto-refresh 30s, alertes visuelles temps reel - consumption_history.html: Tableau pagine, filtres provider/status/date range, tri colonnes, export CSV - consumption_alerts.html: CRUD alertes, toggle actif/inactif, seuils visuels badges, modal creation/edition Co-Authored-By: Paperclip --- consumption_alerts.html | 335 ++++++++++++++++++++++++++++++ consumption_dashboard.html | 415 +++++++++++++++++++++++++++++++++++++ consumption_history.html | 327 +++++++++++++++++++++++++++++ portal_server.py | 57 +++++ 4 files changed, 1134 insertions(+) create mode 100644 consumption_alerts.html create mode 100644 consumption_dashboard.html create mode 100644 consumption_history.html diff --git a/consumption_alerts.html b/consumption_alerts.html new file mode 100644 index 0000000..156025b --- /dev/null +++ b/consumption_alerts.html @@ -0,0 +1,335 @@ + + + + + + Consommation IA — Alertes + + + +🏠Accueil + +
+
+ 🔔 Gestion des Alertes + ← Dashboard +
+
+ +
+
+ +
+
+ Règles d'alerte consommation +
+ +
+
+
Chargement des alertes...
+
+ +
+
+ + + +
+ + + + diff --git a/consumption_dashboard.html b/consumption_dashboard.html new file mode 100644 index 0000000..1568e4d --- /dev/null +++ b/consumption_dashboard.html @@ -0,0 +1,415 @@ + + + + + + Consommation IA — Dashboard + + + + +🏠Accueil + +
+
+ 📊 Consommation IA + Historique → +
+
+ ⚙️ Alertes + +
+
+ +
+
+ ⚠️ +
+ +
+ +
+ + + +
+ +
+
+
Chargement des données...
+
+ + +
+ + + + diff --git a/consumption_history.html b/consumption_history.html new file mode 100644 index 0000000..6789cd6 --- /dev/null +++ b/consumption_history.html @@ -0,0 +1,327 @@ + + + + + + Consommation IA — Historique + + + +🏠Accueil + +
+
+ 📋 Historique Consommation + ← Dashboard +
+
+ + +
+
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ +
+
+
Chargement de l'historique...
+
+ + + + +
+ +
+ + + + diff --git a/portal_server.py b/portal_server.py index 5ce2408..592bcb4 100755 --- a/portal_server.py +++ b/portal_server.py @@ -755,6 +755,63 @@ def turf_static(filename): return send_from_directory("/home/h3r7/turf_saas", filename) +# --- Consumption Dashboard (HRT-226) --- +CONSUMPTION_API_URL = "http://localhost:8784" +CONSUMPTION_API_KEY = os.environ.get("CONSUMPTION_API_KEY", "dev-key-change-in-production") + + +@app.route("/dashboard/consumption") +def consumption_dashboard(): + return send_from_directory(SAAS_DIR, "consumption_dashboard.html") + + +@app.route("/dashboard/consumption/history") +def consumption_history(): + return send_from_directory(SAAS_DIR, "consumption_history.html") + + +@app.route("/dashboard/consumption/alerts") +def consumption_alerts(): + return send_from_directory(SAAS_DIR, "consumption_alerts.html") + + +# Proxy: /api/v1/consumption/* -> consumption-tracker (port 8784) +@app.route("/api/v1/consumption/", methods=["GET", "POST", "PUT", "DELETE", "PATCH"]) +def proxy_consumption_api(subpath): + full_url = f"{CONSUMPTION_API_URL}/api/v1/consumption/{subpath}" + if request.query_string: + full_url += "?" + request.query_string.decode() + try: + headers = {k: v for k, v in request.headers if k.lower() not in ("host", "content-length", "transfer-encoding", "connection")} + if not any(k.lower() == "authorization" for k in headers): + headers["Authorization"] = f"Bearer {CONSUMPTION_API_KEY}" + raw_body = request.get_data() + resp = requests.request(request.method, full_url, headers=headers, data=raw_body, cookies=request.cookies, allow_redirects=False, timeout=15) + response = make_response(resp.content, resp.status_code) + for k, v in resp.headers.items(): + if k.lower() not in ("content-encoding", "transfer-encoding", "connection"): + response.headers[k] = v + return response + except Exception as e: + return jsonify({"error": f"Consumption proxy error: {e}"}), 502 + + +# Proxy: /api/v1/ai/usage* -> AI Router (port 8783) +@app.route("/api/v1/ai/usage") +@app.route("/api/v1/ai/usage/") +def proxy_ai_usage(subpath=""): + full_url = f"http://localhost:8783/api/v1/ai/usage" + if subpath: + full_url += "/" + subpath + if request.query_string: + full_url += "?" + request.query_string.decode() + try: + resp = requests.get(full_url, timeout=15) + return resp.content, resp.status_code, {"Content-Type": "application/json"} + except Exception as e: + return jsonify({"error": f"AI usage proxy error: {e}"}), 502 + + # --- POD Routes --- @app.route("/pod/") @app.route("/pod/")