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:
@@ -59,7 +59,8 @@ function ubusSetup(command) {
|
|||||||
return view.extend({
|
return view.extend({
|
||||||
state: {
|
state: {
|
||||||
selectedRegion: null,
|
selectedRegion: null,
|
||||||
switchMode: 'force'
|
switchMode: 'force',
|
||||||
|
healthCache: {}
|
||||||
},
|
},
|
||||||
|
|
||||||
load() {
|
load() {
|
||||||
@@ -87,11 +88,14 @@ return view.extend({
|
|||||||
change: ev => this.onSelectRegion(ev.target.value),
|
change: ev => this.onSelectRegion(ev.target.value),
|
||||||
checked: isSelected || null,
|
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);
|
||||||
isActive ? E('strong', {}, gw.region) : document.createTextNode(gw.region)
|
return E('td', { class: 'zt-region-cell' }, [
|
||||||
]),
|
dot, ' ',
|
||||||
|
isActive ? E('strong', {}, gw.region) : document.createTextNode(gw.region)
|
||||||
|
]);
|
||||||
|
})(),
|
||||||
E('td', {}, gw.label),
|
E('td', {}, gw.label),
|
||||||
E('td', {}, gw.ip),
|
E('td', {}, gw.ip),
|
||||||
E('td', {}, isActive ? E('em', {}, _('active')) : '')
|
E('td', {}, isActive ? E('em', {}, _('active')) : '')
|
||||||
@@ -250,24 +254,33 @@ return view.extend({
|
|||||||
|
|
||||||
async refreshHealth(data) {
|
async refreshHealth(data) {
|
||||||
const gws = data.gateways || [];
|
const gws = data.gateways || [];
|
||||||
for (let i = 0; i < gws.length; i++) {
|
const checks = gws.map(gw => {
|
||||||
const region = gws[i].region;
|
return ubusHealth(gw.region).then(h => {
|
||||||
try {
|
if (h) {
|
||||||
const h = await ubusHealth(region);
|
this.state.healthCache[gw.region] = {
|
||||||
const row = document.querySelector('.zt-gateway-row[data-region="%s"]'.format(region));
|
reachable: !!h.reachable,
|
||||||
if (row && h) {
|
latency_ms: h.latency_ms ?? null
|
||||||
const node = row.querySelector('.zt-health');
|
};
|
||||||
if (node) {
|
|
||||||
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'));
|
|
||||||
}
|
}
|
||||||
}
|
return { region: gw.region, h };
|
||||||
} catch (_) {
|
}).catch(() => {
|
||||||
/* health check is best-effort */
|
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) continue;
|
||||||
|
const node = row.querySelector('.zt-health');
|
||||||
|
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'));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user