From 463d80c4fee066748f20f201d2f43b9d13d6598b Mon Sep 17 00:00:00 2001 From: ai_coder Date: Thu, 22 Oct 2026 08:48:00 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=BA=D0=BB=D0=B8=D0=BA=20=D0=BF=D0=BE?= =?UTF-8?q?=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB=D0=B8=20=E2=80=94=20=D0=BB?= =?UTF-8?q?=D0=BE=D0=B3=20=D0=B2=D1=81=D0=B5=D1=85=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=B5=D1=80=D0=BE=D0=BA=20=D1=81=20=D0=BE=D1=88=D0=B8?= =?UTF-8?q?=D0=B1=D0=BA=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Кнопка 📋 Лог открывает /api/checks/{model_id} — все проверки модели за период (по умолчанию 100 последних, limit через CHECKS_LIMIT) с ok/latency_ms/checked_at/error. Заполняет history для перерисовки. --- app/main.py | 25 +++++++++++++++++++++++++ app/static/index.html | 11 +++++++++++ 2 files changed, 36 insertions(+) diff --git a/app/main.py b/app/main.py index dc24c33..169e9d9 100644 --- a/app/main.py +++ b/app/main.py @@ -536,6 +536,31 @@ def api_check(model_id: str): } +@app.get("/api/checks/{model_id:path}") +def api_checks(model_id: str): + """Все проверки модели за период — для дропдауна с деталями ошибок.""" + limit = int(os.environ.get("CHECKS_LIMIT", "100")) + db = get_db() + rows = db.execute( + "SELECT ok, latency_ms, checked_at, error FROM checks " + "WHERE model_id=? ORDER BY checked_at DESC LIMIT ?", + (model_id, limit), + ).fetchall() + db.close() + return { + "model_id": model_id, + "checks": [ + { + "ok": bool(r["ok"]), + "latency_ms": r["latency_ms"], + "checked_at": r["checked_at"], + "error": r["error"], + } + for r in rows + ], + } + + @app.get("/", response_class=HTMLResponse) def index(): with open("/app/app/static/index.html") as f: diff --git a/app/static/index.html b/app/static/index.html index 6b5683c..a6f8034 100644 --- a/app/static/index.html +++ b/app/static/index.html @@ -363,6 +363,7 @@ function renderRow(m) { ${statusCell(m.last_ok)} ${uptimeCell(m.uptime_pct, m.history)} + `; } @@ -487,6 +488,16 @@ async function checkOne(modelId, btn) { } } +async function openCheckHistory(modelId, btn) { + const res = await fetch(`/api/checks/${encodeURIComponent(modelId)}`); + const data = await res.json(); + const idx = allData.findIndex(m => m.id === modelId); + if (idx >= 0) { + allData[idx].history = (data.checks || []).map(c => ({ ok: c.ok, latency_ms: c.latency_ms, at: c.checked_at, error: c.error })); + } + applyFilters(); +} + document.getElementById('btn-refresh').addEventListener('click', loadData); document.getElementById('btn-sync').addEventListener('click', async () => { const btn = document.getElementById('btn-sync');