resource integration
Some checks failed
Zone / build (push) Failing after 7s

This commit is contained in:
2026-06-26 14:44:40 +01:00
parent 9e182e01d6
commit 60695b377b
4 changed files with 111 additions and 25 deletions

View File

@@ -5,12 +5,13 @@ const logStreams = new Map();
let statusPollTimer = null;
/** Fallback when manifest API fails and static file is an older v1 without widgets. */
const DEFAULT_WIDGETS = [
{ id: 'authoring-queue', type: 'queue', title: 'Authoring builds', integration: 'authoring' },
{ id: 'authoring-detail', type: 'authoringDetail', title: 'Authoring detail', integration: 'authoring' },
{ id: 'watcher', type: 'badge', title: 'Lima watcher', integration: 'watcher' },
{ id: 'timesheet-summary', type: 'timesheet', title: 'Hours this week', integration: 'timesheet' },
{ id: 'scripts-health', type: 'log', title: 'Script logs', integration: 'scripts', logKey: 'orgWebBuild' },
const DEFAULT_WIDGETS = [
{ id: 'authoring-queue', type: 'queue', title: 'Authoring builds', integration: 'authoring' },
{ id: 'authoring-detail', type: 'authoringDetail', title: 'Authoring detail', integration: 'authoring' },
{ id: 'watcher', type: 'badge', title: 'Lima watcher', integration: 'watcher' },
{ id: 'system-resources', type: 'systemResources', title: 'System resources', integration: 'resources' },
{ id: 'timesheet-summary', type: 'timesheet', title: 'Hours this week', integration: 'timesheet' },
{ id: 'scripts-health', type: 'log', title: 'Script logs', integration: 'scripts', logKey: 'orgWebBuild' },
{ id: 'calibre-sync', type: 'log', title: 'Calibre export', integration: 'scripts', logKey: 'calibreSync' },
{ id: 'org-web-build', type: 'buildStatus', title: 'Website build', integration: 'builds', lastRunKey: 'org_web' },
];
@@ -209,15 +210,28 @@ function updateWidgetCard(widget, status) {
body.textContent = w.SubState ? `SubState: ${w.SubState}` : (w.message || 'Watcher');
break;
}
case 'timesheet': {
const t = status.timesheet || {};
setBadge(badge, t.available ? 'OK' : '—', t.available ? 'ok' : 'neutral');
body.textContent = t.available
? `Week ${t.weekStart}: ${t.hoursWorked}h worked (${t.delta >= 0 ? '+' : ''}${t.delta}h vs contracted)`
: (t.message || 'No data');
break;
}
case 'log': {
case 'timesheet': {
const t = status.timesheet || {};
setBadge(badge, t.available ? 'OK' : '—', t.available ? 'ok' : 'neutral');
body.textContent = t.available
? `Week ${t.weekStart}: ${t.hoursWorked}h worked (${t.delta >= 0 ? '+' : ''}${t.delta}h vs contracted)`
: (t.message || 'No data');
break;
}
case 'systemResources': {
const resources = status.systemResources || {};
const cpuPct = numberOrNull(resources.cpu?.usagePct);
const memoryPct = numberOrNull(resources.memory?.usedPct);
const storagePct = numberOrNull(resources.storage?.usedPct);
const maxPct = Math.max(cpuPct ?? 0, memoryPct ?? 0, storagePct ?? 0);
setBadge(badge, maxPct >= 90 ? 'High' : 'OK', maxPct >= 90 ? 'error' : maxPct >= 75 ? 'warn' : 'ok');
body.innerHTML = `
${resourceRow('CPU', cpuPct, resources.cpu?.cores ? `${resources.cpu.cores} cores` : '')}
${resourceRow('RAM', memoryPct, formatBytes(resources.memory?.usedBytes, resources.memory?.totalBytes))}
${resourceRow('Disk', storagePct, formatBytes(resources.storage?.usedBytes, resources.storage?.totalBytes))}`;
break;
}
case 'log': {
const info = (status.scripts || {})[widget.logKey] || {};
const label = info.label || widget.logKey;
const stale = info.stale;
@@ -245,14 +259,46 @@ function updateWidgetCard(widget, status) {
body.textContent = `Unknown widget type: ${type}`;
setBadge(badge, '?', 'neutral');
}
}
}
function setBadge(el, text, tone) {
if (!el) return;
el.textContent = text;
el.className = `badge ${tone} widget-badge`;
}
function numberOrNull(value) {
const parsed = Number(value);
return Number.isFinite(parsed) ? parsed : null;
}
function formatPct(value) {
return value == null ? '—' : `${Math.round(value)}%`;
}
function formatBytes(used, total) {
const usedNum = Number(used);
const totalNum = Number(total);
if (!Number.isFinite(usedNum) || !Number.isFinite(totalNum) || totalNum <= 0) return '';
return `${bytesToGiB(usedNum)} / ${bytesToGiB(totalNum)}`;
}
function bytesToGiB(bytes) {
return `${(bytes / 1073741824).toFixed(1)} GiB`;
}
function resourceRow(label, pct, detail) {
const width = Math.max(0, Math.min(100, pct ?? 0));
return `
<div class="resource-row">
<div class="resource-line">
<span>${label}</span>
<span>${formatPct(pct)}${detail ? ` · ${detail}` : ''}</span>
</div>
<div class="resource-meter"><span style="width:${width}%"></span></div>
</div>`;
}
function safeDomId(value) {
return String(value).replace(/[^a-zA-Z0-9_-]/g, '-');
}