feat(ui): add a system page with health checks, version and logs
This commit is contained in:
1 parent
4441c5a24d
commit
175dface50
4 files changed
+119
-1
No files matched your search
+59
-1
@@ -93,7 +93,7 @@ async function refreshMissingBadge() {
|
||||
|
||||
// ---- navigation ----
|
||||
// the view lives in the hash so reload, back button and links keep it
|
||||
const VIEWS = ["search", "missing", "imported", "import", "activity", "settings"];
|
||||
const VIEWS = ["search", "missing", "imported", "import", "activity", "settings", "system"];
|
||||
|
||||
function showView() {
|
||||
const hash = location.hash.slice(1);
|
||||
@@ -102,6 +102,7 @@ function showView() {
|
||||
$$("main > section").forEach((s) => (s.hidden = s.id !== "view-" + name));
|
||||
if (name === "missing" || name === "imported") loadRequests(name);
|
||||
if (name === "activity") loadHistory();
|
||||
if (name === "system") loadSystem();
|
||||
if (name === "settings") loadLibraries().catch((err) => toast(err.message, true));
|
||||
}
|
||||
|
||||
@@ -117,6 +118,7 @@ window.addEventListener("hashchange", showView);
|
||||
// ---- libraries ----
|
||||
async function loadLibraries() {
|
||||
libraries = await api("/api/libraries");
|
||||
refreshHealth();
|
||||
const tbody = $("#lib-table tbody");
|
||||
tbody.innerHTML = libraries
|
||||
.map(
|
||||
@@ -699,6 +701,59 @@ function renderHistory() {
|
||||
.join("");
|
||||
}
|
||||
|
||||
// ---- system ----
|
||||
let healthIssues = [];
|
||||
let logRows = [];
|
||||
|
||||
// also called after library changes, so a fixed root path clears the badge
|
||||
async function refreshHealth() {
|
||||
try {
|
||||
healthIssues = await api("/api/system/health");
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
const badge = $("#health-badge");
|
||||
badge.textContent = healthIssues.length;
|
||||
badge.hidden = !healthIssues.length;
|
||||
badge.classList.toggle("error", healthIssues.some((i) => i.level === "error"));
|
||||
badge.classList.toggle("warn", !healthIssues.some((i) => i.level === "error"));
|
||||
renderHealth();
|
||||
}
|
||||
|
||||
async function loadSystem() {
|
||||
refreshHealth();
|
||||
try {
|
||||
logRows = await api("/api/system/logs");
|
||||
} catch (err) {
|
||||
toast(err.message, true);
|
||||
}
|
||||
renderLogs();
|
||||
}
|
||||
|
||||
function renderHealth() {
|
||||
$("#health-list").innerHTML = healthIssues.length
|
||||
? healthIssues.map((i) => `<li class="${i.level}">${esc(t("health." + i.code, i))}</li>`).join("")
|
||||
: `<li class="ok">${t("system.healthy")}</li>`;
|
||||
}
|
||||
|
||||
function renderLogs() {
|
||||
const tbody = $("#log-table tbody");
|
||||
if (!logRows.length) {
|
||||
tbody.innerHTML = `<tr><td colspan="4" class="muted">${t("common.nothingHere")}</td></tr>`;
|
||||
return;
|
||||
}
|
||||
const locale = lang === "de" ? "de-DE" : "en-GB";
|
||||
const LEVEL_STATE = { WARNING: "warn", ERROR: "failed", CRITICAL: "failed" };
|
||||
tbody.innerHTML = logRows
|
||||
.map((r) => `<tr>
|
||||
<td class="state">${esc(new Date(r.at).toLocaleString(locale))}</td>
|
||||
<td class="state ${LEVEL_STATE[r.level] || ""}">${esc(r.level)}</td>
|
||||
<td class="muted mono">${esc(r.logger)}</td>
|
||||
<td class="mono">${esc(r.message)}</td>
|
||||
</tr>`)
|
||||
.join("");
|
||||
}
|
||||
|
||||
// ---- request detail dialog ----
|
||||
let detailRequest = null;
|
||||
|
||||
@@ -1905,6 +1960,8 @@ function onLangChange() {
|
||||
renderRequests("missing");
|
||||
renderRequests("imported");
|
||||
renderHistory();
|
||||
renderHealth();
|
||||
renderLogs();
|
||||
if (libraries.length) loadLibraries();
|
||||
const editId = $("#lib-form").dataset.editId;
|
||||
editId ? startLibraryEdit(parseInt(editId)) : endLibraryEdit();
|
||||
@@ -1920,6 +1977,7 @@ async function showVersion() {
|
||||
// the version alone rarely moves, so the stamp is what tells deploys apart
|
||||
$("#app-version").textContent = `v${v.version} · ${v.build || v.built_at}`;
|
||||
$("#app-version").title = `${v.built_at} UTC${v.build ? " · " + v.build : ""}`;
|
||||
$("#system-version").textContent = `v${v.version}${v.build ? " · " + v.build : ""} · ${v.built_at} UTC`;
|
||||
} catch {}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user