feat: клик по модели — лог всех проверок с ошибками
Build and deploy / build-deploy (push) Has been cancelled

Кнопка 📋 Лог открывает /api/checks/{model_id} — все проверки модели
за период (по умолчанию 100 последних, limit через CHECKS_LIMIT) с
ok/latency_ms/checked_at/error. Заполняет history для перерисовки.
This commit is contained in:
2026-10-22 08:48:00 +00:00
parent 1fb770fc52
commit 463d80c4fe
2 changed files with 36 additions and 0 deletions
+25
View File
@@ -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) @app.get("/", response_class=HTMLResponse)
def index(): def index():
with open("/app/app/static/index.html") as f: with open("/app/app/static/index.html") as f:
+11
View File
@@ -363,6 +363,7 @@ function renderRow(m) {
<td>${statusCell(m.last_ok)}</td> <td>${statusCell(m.last_ok)}</td>
<td>${uptimeCell(m.uptime_pct, m.history)}</td> <td>${uptimeCell(m.uptime_pct, m.history)}</td>
<td><button class="check-btn" onclick="checkOne('${m.id}', this)">▶ Тест</button></td> <td><button class="check-btn" onclick="checkOne('${m.id}', this)">▶ Тест</button></td>
<td><button class="check-btn" onclick="openCheckHistory('${m.id}', this)">📋 Лог</button></td>
</tr>`; </tr>`;
} }
@@ -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-refresh').addEventListener('click', loadData);
document.getElementById('btn-sync').addEventListener('click', async () => { document.getElementById('btn-sync').addEventListener('click', async () => {
const btn = document.getElementById('btn-sync'); const btn = document.getElementById('btn-sync');