Files
a.gritsenkoandClaude Sonnet 5 09159a5f31 Initial import: nasmon health monitor, split out of home_nas
Go service (nasmon/) plus nfpm packaging (packaging/), carried over from
the home_nas monorepo. Adds rpm packaging alongside the existing deb
build (Fedora/RHEL/Rocky dependency names via nfpm overrides, per-format
systemd unit path, dpkg+rpm-compatible maintainer scripts) and a Gitea
Actions workflow that builds both formats on every push.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-19 10:24:34 +03:00

64 lines
2.8 KiB
JavaScript

// Auto-refresh: re-fetch the current page's content block every few seconds
// and swap it in. Rendering stays server-side; this keeps the UI live without
// duplicating templates in JS.
(function () {
var page = document.body.dataset.page;
var interval = { dashboard: 5000, zfs: 15000, alerts: 15000, disk: 30000 }[page];
if (!interval) return;
var main = document.getElementById('content');
var dot = document.getElementById('refresh-state');
var timer = null;
// A refresh replaces the whole content block, which would wipe a form the
// user is typing into: hold off while a form is focused or has been edited.
main.addEventListener('input', function (e) {
var f = e.target.closest('form');
if (f) f.classList.add('dirty');
});
// The ✕ / "show again" forms are single buttons, not something being typed
// into, so they never hold a refresh back.
function editing() {
if (main.querySelector('form.dirty:not(.x-form)')) return true;
var a = document.activeElement;
return !!(a && a.closest && a.closest('#content form:not(.x-form)'));
}
function tick() {
if (document.hidden || editing()) { dot.className = 'refresh paused'; return; }
var open = [].slice.call(main.querySelectorAll('details[open]')).map(function (d) { return d.className; });
var url = new URL(location.href);
url.searchParams.set('partial', '1');
url.searchParams.delete('msg');
var n = null;
fetch(url, { cache: 'no-store' }).then(function (r) {
if (!r.ok) throw new Error(r.status);
n = r.headers.get('X-Alerts-Open');
return r.text();
}).then(function (html) {
main.innerHTML = html;
open.forEach(function (cls) {
var d = main.querySelector('details.' + cls.split(' ').join('.'));
if (d) d.open = true;
});
var nav = document.querySelector('nav a[href="/alerts"]');
if (nav && n !== null) nav.innerHTML = 'Alerts' + (n !== '0' ? ' <span class="badge crit">' + n + '</span>' : '');
dot.className = 'refresh';
}).catch(function () { dot.className = 'refresh stale'; });
}
// ✕ / "show again" forms: post in place and refresh the content block
document.addEventListener('submit', function (ev) {
var f = ev.target;
if (!f.classList.contains('x-form')) return;
ev.preventDefault();
if (document.activeElement && document.activeElement.blur) document.activeElement.blur();
fetch(f.action, { method: 'POST', headers: { 'X-Requested-With': 'fetch' } })
.then(function (r) { if (!r.ok) throw new Error(r.status); tick(); })
.catch(function () { f.submit(); });
});
function start() { if (!timer) timer = setInterval(tick, interval); }
document.addEventListener('visibilitychange', function () { if (!document.hidden) tick(); });
start();
})();