mirror of
https://github.com/smittix/intercept.git
synced 2026-07-08 09:38:12 -07:00
Handle transient network suspension in frontend polling and SSE
This commit is contained in:
@@ -7,6 +7,7 @@ const AlertCenter = (function() {
|
|||||||
let rules = [];
|
let rules = [];
|
||||||
let eventSource = null;
|
let eventSource = null;
|
||||||
let reconnectTimer = null;
|
let reconnectTimer = null;
|
||||||
|
let lastConnectionWarningAt = 0;
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
loadRules();
|
loadRules();
|
||||||
@@ -31,7 +32,14 @@ const AlertCenter = (function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
eventSource.onerror = 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);
|
if (reconnectTimer) clearTimeout(reconnectTimer);
|
||||||
reconnectTimer = setTimeout(connect, 2500);
|
reconnectTimer = setTimeout(connect, 2500);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -92,8 +92,9 @@ const RunState = (function() {
|
|||||||
renderHealth(data);
|
renderHealth(data);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
renderHealth(null, err);
|
renderHealth(null, err);
|
||||||
|
const transient = isTransientFailure(err);
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (typeof reportActionableError === 'function' && (now - lastErrorToastAt) > 30000) {
|
if (!transient && typeof reportActionableError === 'function' && (now - lastErrorToastAt) > 30000) {
|
||||||
lastErrorToastAt = now;
|
lastErrorToastAt = now;
|
||||||
reportActionableError('Run State', err, { persistent: false });
|
reportActionableError('Run State', err, { persistent: false });
|
||||||
}
|
}
|
||||||
@@ -214,6 +215,17 @@ const RunState = (function() {
|
|||||||
return String(err);
|
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() {
|
function getLastHealth() {
|
||||||
return lastHealth;
|
return lastHealth;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -208,9 +208,31 @@ const AppFeedback = (function() {
|
|||||||
return state;
|
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) {
|
function isNetworkError(message) {
|
||||||
const text = String(message || '').toLowerCase();
|
return isTransientNetworkError(message);
|
||||||
return text.includes('networkerror') || text.includes('failed to fetch') || text.includes('timeout');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isSettingsError(message) {
|
function isSettingsError(message) {
|
||||||
@@ -224,6 +246,9 @@ const AppFeedback = (function() {
|
|||||||
reportError,
|
reportError,
|
||||||
removeToast,
|
removeToast,
|
||||||
renderCollectionState,
|
renderCollectionState,
|
||||||
|
isOffline,
|
||||||
|
isTransientNetworkError,
|
||||||
|
isTransientOrOffline,
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@@ -243,6 +268,18 @@ window.renderCollectionState = function(container, options) {
|
|||||||
return AppFeedback.renderCollectionState(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', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
AppFeedback.init();
|
AppFeedback.init();
|
||||||
});
|
});
|
||||||
|
|||||||
+11
-2
@@ -5655,10 +5655,19 @@
|
|||||||
renderSdrStatus(devices);
|
renderSdrStatus(devices);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.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');
|
const container = document.getElementById('sdrStatusList');
|
||||||
if (container) {
|
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>';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -971,7 +971,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user