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>
64 lines
2.8 KiB
JavaScript
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();
|
|
})();
|