Handle transient network suspension in frontend polling and SSE

This commit is contained in:
Smittix
2026-02-24 22:25:59 +00:00
parent 0344862a0c
commit afd3d34f43
5 changed files with 78 additions and 7 deletions

View File

@@ -7,6 +7,7 @@ const AlertCenter = (function() {
let rules = [];
let eventSource = null;
let reconnectTimer = null;
let lastConnectionWarningAt = 0;
function init() {
loadRules();
@@ -31,7 +32,14 @@ const AlertCenter = (function() {
};
eventSource.onerror = function() {
console.warn('[Alerts] SSE connection error');
const now = Date.now();
const offline = (typeof window.isOffline === 'function' && window.isOffline()) ||
(typeof navigator !== 'undefined' && navigator.onLine === false);
const shouldLog = !offline && !document.hidden && (now - lastConnectionWarningAt) > 15000;
if (shouldLog) {
lastConnectionWarningAt = now;
console.warn('[Alerts] SSE connection error; retrying');
}
if (reconnectTimer) clearTimeout(reconnectTimer);
reconnectTimer = setTimeout(connect, 2500);
};

View File

@@ -92,8 +92,9 @@ const RunState = (function() {
renderHealth(data);
} catch (err) {
renderHealth(null, err);
const transient = isTransientFailure(err);
const now = Date.now();
if (typeof reportActionableError === 'function' && (now - lastErrorToastAt) > 30000) {
if (!transient && typeof reportActionableError === 'function' && (now - lastErrorToastAt) > 30000) {
lastErrorToastAt = now;
reportActionableError('Run State', err, { persistent: false });
}
@@ -214,6 +215,17 @@ const RunState = (function() {
return String(err);
}
function isTransientFailure(err) {
if (typeof window.isTransientOrOffline === 'function' && window.isTransientOrOffline(err)) {
return true;
}
if (typeof navigator !== 'undefined' && navigator.onLine === false) {
return true;
}
const text = extractMessage(err).toLowerCase();
return text.includes('failed to fetch') || text.includes('network') || text.includes('timeout');
}
function getLastHealth() {
return lastHealth;
}

View File

@@ -208,9 +208,31 @@ const AppFeedback = (function() {
return state;
}
function isOffline() {
return typeof navigator !== 'undefined' && navigator.onLine === false;
}
function isTransientNetworkError(error) {
const text = String(extractMessage(error) || '').toLowerCase();
if (!text) return false;
return text.includes('networkerror') ||
text.includes('failed to fetch') ||
text.includes('network request failed') ||
text.includes('load failed') ||
text.includes('err_network_io_suspended') ||
text.includes('network io suspended') ||
text.includes('the network connection was lost') ||
text.includes('connection reset') ||
text.includes('timeout');
}
function isTransientOrOffline(error) {
return isOffline() || isTransientNetworkError(error);
}
function isNetworkError(message) {
const text = String(message || '').toLowerCase();
return text.includes('networkerror') || text.includes('failed to fetch') || text.includes('timeout');
return isTransientNetworkError(message);
}
function isSettingsError(message) {
@@ -224,6 +246,9 @@ const AppFeedback = (function() {
reportError,
removeToast,
renderCollectionState,
isOffline,
isTransientNetworkError,
isTransientOrOffline,
};
})();
@@ -243,6 +268,18 @@ window.renderCollectionState = function(container, options) {
return AppFeedback.renderCollectionState(container, options);
};
window.isOffline = function() {
return AppFeedback.isOffline();
};
window.isTransientNetworkError = function(error) {
return AppFeedback.isTransientNetworkError(error);
};
window.isTransientOrOffline = function(error) {
return AppFeedback.isTransientOrOffline(error);
};
document.addEventListener('DOMContentLoaded', () => {
AppFeedback.init();
});

View File

@@ -5655,10 +5655,19 @@
renderSdrStatus(devices);
})
.catch(err => {
console.error('Failed to fetch SDR status:', err);
const transient = (typeof window.isTransientOrOffline === 'function' && window.isTransientOrOffline(err)) ||
(typeof navigator !== 'undefined' && navigator.onLine === false) ||
/failed to fetch|network io suspended|networkerror|timeout/i.test(String((err && err.message) || err || ''));
if (!transient) {
console.error('Failed to fetch SDR status:', err);
}
const container = document.getElementById('sdrStatusList');
if (container) {
container.innerHTML = '<div style="padding: 8px; color: #ff6666; font-size: 11px; text-align: center;">Error loading status</div>';
if (transient) {
container.innerHTML = '<div style="padding: 8px; color: #888; font-size: 11px; text-align: center;">Status temporarily unavailable</div>';
} else {
container.innerHTML = '<div style="padding: 8px; color: #ff6666; font-size: 11px; text-align: center;">Error loading status</div>';
}
}
});
}

View File

@@ -971,7 +971,12 @@
}
}
} catch (err) {
console.error('Position update error:', err);
const transient = (typeof window.isTransientOrOffline === 'function' && window.isTransientOrOffline(err)) ||
(typeof navigator !== 'undefined' && navigator.onLine === false) ||
/failed to fetch|network io suspended|networkerror|timeout/i.test(String((err && err.message) || err || ''));
if (!transient) {
console.error('Position update error:', err);
}
}
}