60 lines
2.6 KiB
JavaScript
60 lines
2.6 KiB
JavaScript
// Operator status strip mounted at the top of every .main pane.
|
|
(function () {
|
|
function esc(s) {
|
|
if (!s) return '';
|
|
return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
|
}
|
|
function mount() {
|
|
const main = document.querySelector('.main');
|
|
if (!main || main.querySelector('.topbar-strip')) return;
|
|
const strip = document.createElement('div');
|
|
strip.className = 'topbar-strip';
|
|
strip.innerHTML =
|
|
'<span class="ts-dot" id="tsDot" title="System pulse"></span>' +
|
|
'<span class="ts-label">Z-AMPP</span>' +
|
|
'<span class="ts-now" id="tsNow">00:00:00</span>' +
|
|
'<span class="ts-sep"></span>' +
|
|
'<span class="ts-label">Page</span>' +
|
|
'<span class="ts-value" id="tsPage">' + esc(pageName()) + '</span>' +
|
|
'<span class="ts-sep"></span>' +
|
|
'<span class="ts-label">API</span>' +
|
|
'<span class="ts-value" id="tsApi">--</span>';
|
|
main.insertBefore(strip, main.firstChild);
|
|
tick(); setInterval(tick, 1000);
|
|
ping(); setInterval(ping, 10000);
|
|
}
|
|
function pageName() {
|
|
const p = location.pathname.replace(/\.html$/, '').replace(/^\//, '');
|
|
if (!p || p === 'index') return 'Library';
|
|
return p.charAt(0).toUpperCase() + p.slice(1);
|
|
}
|
|
function tick() {
|
|
const n = new Date();
|
|
const hh = String(n.getHours()).padStart(2, '0');
|
|
const mm = String(n.getMinutes()).padStart(2, '0');
|
|
const ss = String(n.getSeconds()).padStart(2, '0');
|
|
const el = document.getElementById('tsNow');
|
|
if (el) el.textContent = hh + ':' + mm + ':' + ss;
|
|
}
|
|
async function ping() {
|
|
const dot = document.getElementById('tsDot');
|
|
const apiEl = document.getElementById('tsApi');
|
|
const t0 = performance.now();
|
|
try {
|
|
const r = await fetch('/api/v1/projects', { credentials: 'include', signal: AbortSignal.timeout(4000) });
|
|
const ms = Math.round(performance.now() - t0);
|
|
if (r.ok) {
|
|
if (dot) { dot.style.background = 'var(--signal-good)'; dot.style.boxShadow = '0 0 8px var(--signal-good)'; }
|
|
if (apiEl) apiEl.textContent = ms + ' ms';
|
|
} else {
|
|
if (dot) { dot.style.background = 'var(--signal-warn)'; dot.style.boxShadow = '0 0 8px var(--signal-warn)'; }
|
|
if (apiEl) apiEl.textContent = 'HTTP ' + r.status;
|
|
}
|
|
} catch (e) {
|
|
if (dot) { dot.style.background = 'var(--signal-bad)'; dot.style.boxShadow = '0 0 8px var(--signal-bad)'; }
|
|
if (apiEl) apiEl.textContent = 'offline';
|
|
}
|
|
}
|
|
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', mount);
|
|
else mount();
|
|
})();
|