fix(zt-gateway): prevent health dots flashing red on each poll cycle

Cache last-known health state per region so renderGatewayRow uses
correct values instead of defaulting to unreachable. Parallelize
refreshHealth with Promise.all so all gateway pings resolve together
instead of sequentially (3 gateways × -W 3 timeout was ~18s worst case).
This commit is contained in:
2026-07-13 12:18:55 +05:30
parent 1e4a46c4bf
commit 10081b33e2

View File

@@ -59,7 +59,8 @@ function ubusSetup(command) {
return view.extend({
state: {
selectedRegion: null,
switchMode: 'force'
switchMode: 'force',
healthCache: {}
},
load() {
@@ -87,11 +88,14 @@ return view.extend({
change: ev => this.onSelectRegion(ev.target.value),
checked: isSelected || null,
})),
E('td', { class: 'zt-region-cell' }, [
this.renderHealthDot(false, null),
' ',
(() => {
const cached = this.state.healthCache[gw.region];
const dot = this.renderHealthDot(cached ? cached.reachable : false, cached ? cached.latency_ms : null);
return E('td', { class: 'zt-region-cell' }, [
dot, ' ',
isActive ? E('strong', {}, gw.region) : document.createTextNode(gw.region)
]),
]);
})(),
E('td', {}, gw.label),
E('td', {}, gw.ip),
E('td', {}, isActive ? E('em', {}, _('active')) : '')
@@ -250,25 +254,34 @@ return view.extend({
async refreshHealth(data) {
const gws = data.gateways || [];
for (let i = 0; i < gws.length; i++) {
const region = gws[i].region;
try {
const h = await ubusHealth(region);
const checks = gws.map(gw => {
return ubusHealth(gw.region).then(h => {
if (h) {
this.state.healthCache[gw.region] = {
reachable: !!h.reachable,
latency_ms: h.latency_ms ?? null
};
}
return { region: gw.region, h };
}).catch(() => {
this.state.healthCache[gw.region] = { reachable: false, latency_ms: null };
return { region: gw.region, h: null };
});
});
const results = await Promise.all(checks);
for (const { region, h } of results) {
if (!h) continue;
const row = document.querySelector('.zt-gateway-row[data-region="%s"]'.format(region));
if (row && h) {
if (!row) continue;
const node = row.querySelector('.zt-health');
if (node) {
if (!node) continue;
node.classList.toggle('zt-health-up', !!h.reachable);
node.classList.toggle('zt-health-down', !h.reachable);
node.title = h.reachable && h.latency_ms != null
? _('UP (%sms)').format(h.latency_ms)
: (h.reachable ? _('UP') : _('DOWN'));
}
}
} catch (_) {
/* health check is best-effort */
}
}
},
async refreshState() {