fix: resolve infinite WebSocket reconnection loop
The ui_server proxies WebSocket connections. It was accepting the client's WebSocket connection (ws.onopen triggered on the client), but if it couldn't connect to the backend (or if the backend disconnected), it would drop the client connection right away (ws.onclose triggered).
The issue was that reconnectAttempts was being reset to 0 inside the ws.onopen handler. Because the connection was briefly succeeding before failing, the reconnectAttempts counter was resetting to 0 on every attempt, completely circumventing the exponential backoff mechanism and causing a tight reconnection loop.
Modified the WebSocket logic across all relevant UI components to delay resetting reconnectAttempts = 0. Instead of resetting immediately upon the TCP socket opening, it now safely waits until a valid JSON payload {"type": "connected"} is successfully received from the backend.
This commit is contained in:
parent
bfc8f4da77
commit
7c1deca8ae
9 changed files with 53 additions and 19 deletions
|
|
@ -1101,6 +1101,7 @@
|
||||||
const r = JSON.parse(e.data);
|
const r = JSON.parse(e.data);
|
||||||
if (r.type === "connected") {
|
if (r.type === "connected") {
|
||||||
console.log("WebSocket welcome message:", r);
|
console.log("WebSocket welcome message:", r);
|
||||||
|
reconnectAttempts = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (r.bot_id) {
|
if (r.bot_id) {
|
||||||
|
|
@ -1154,7 +1155,6 @@
|
||||||
ws.readyState,
|
ws.readyState,
|
||||||
);
|
);
|
||||||
updateConnectionStatus("connected");
|
updateConnectionStatus("connected");
|
||||||
reconnectAttempts = 0;
|
|
||||||
hasReceivedInitialMessage = false;
|
hasReceivedInitialMessage = false;
|
||||||
};
|
};
|
||||||
ws.onclose = function (e) {
|
ws.onclose = function (e) {
|
||||||
|
|
|
||||||
|
|
@ -1322,7 +1322,6 @@
|
||||||
"WebSocket connected for attendant:",
|
"WebSocket connected for attendant:",
|
||||||
currentAttendantId,
|
currentAttendantId,
|
||||||
);
|
);
|
||||||
reconnectAttempts = 0;
|
|
||||||
showToast(
|
showToast(
|
||||||
"Connected to notification service",
|
"Connected to notification service",
|
||||||
"success",
|
"success",
|
||||||
|
|
@ -1367,6 +1366,7 @@
|
||||||
switch (msgType) {
|
switch (msgType) {
|
||||||
case "connected":
|
case "connected":
|
||||||
console.log("WebSocket connected:", data.message);
|
console.log("WebSocket connected:", data.message);
|
||||||
|
reconnectAttempts = 0;
|
||||||
break;
|
break;
|
||||||
case "new_conversation":
|
case "new_conversation":
|
||||||
showToast("New conversation in queue", "info");
|
showToast("New conversation in queue", "info");
|
||||||
|
|
|
||||||
|
|
@ -886,7 +886,6 @@
|
||||||
ws.onopen = function () {
|
ws.onopen = function () {
|
||||||
clearTimeout(connectionTimeout);
|
clearTimeout(connectionTimeout);
|
||||||
console.log("WebSocket connected to:", url);
|
console.log("WebSocket connected to:", url);
|
||||||
reconnectAttempts = 0;
|
|
||||||
disconnectNotified = false;
|
disconnectNotified = false;
|
||||||
updateConnectionStatus("connected");
|
updateConnectionStatus("connected");
|
||||||
};
|
};
|
||||||
|
|
@ -897,7 +896,10 @@
|
||||||
console.log("Chat WebSocket received:", data);
|
console.log("Chat WebSocket received:", data);
|
||||||
|
|
||||||
// Ignore connection confirmation
|
// Ignore connection confirmation
|
||||||
if (data.type === "connected") return;
|
if (data.type === "connected") {
|
||||||
|
reconnectAttempts = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Process system events (theme changes, etc)
|
// Process system events (theme changes, etc)
|
||||||
if (data.event) {
|
if (data.event) {
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
<!-- Local JS requirements per AGENTS.md / UI.md -->
|
<!-- Local JS requirements per AGENTS.md / UI.md -->
|
||||||
<script src="/suite/js/vendor/htmx.min.js"></script>
|
<script src="/suite/js/vendor/htmx.min.js"></script>
|
||||||
<script src="/suite/js/window-manager.js"></script>
|
<script src="/suite/js/window-manager.js"></script>
|
||||||
|
<script src="/suite/js/theme-manager.js"></script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
|
@ -214,10 +215,13 @@
|
||||||
<div id="taskbar-apps">
|
<div id="taskbar-apps">
|
||||||
<!-- Taskbar items populated automatically by window-manager.js -->
|
<!-- Taskbar items populated automatically by window-manager.js -->
|
||||||
</div>
|
</div>
|
||||||
<div class="toolbar-time">
|
<div class="toolbar-time" style="display: flex; align-items: center; gap: 15px;">
|
||||||
|
<div id="themeSelectorContainer"></div>
|
||||||
|
<div style="text-align: right;">
|
||||||
<div id="clock-time">00:00</div>
|
<div id="clock-time">00:00</div>
|
||||||
<div id="clock-date">01/01/2026</div>
|
<div id="clock-date">01/01/2026</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -231,6 +235,11 @@
|
||||||
} else {
|
} else {
|
||||||
console.error("WindowManager class not loaded from window-manager.js");
|
console.error("WindowManager class not loaded from window-manager.js");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize ThemeManager
|
||||||
|
if (typeof window.ThemeManager !== 'undefined') {
|
||||||
|
window.ThemeManager.init();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen to HTMX afterRequest event
|
// Listen to HTMX afterRequest event
|
||||||
|
|
@ -248,6 +257,15 @@
|
||||||
window.wm.open(appId, title, htmlContent);
|
window.wm.open(appId, title, htmlContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure Theme dropdown is re-injected if wiped
|
||||||
|
if (window.ThemeManager) {
|
||||||
|
const container = document.getElementById('themeSelectorContainer');
|
||||||
|
if (container && !container.hasChildNodes()) {
|
||||||
|
// Quick and dirty way to re-init
|
||||||
|
window.ThemeManager.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Simple Clock implementation matching the screenshot bottom right corner
|
// Simple Clock implementation matching the screenshot bottom right corner
|
||||||
|
|
|
||||||
|
|
@ -192,7 +192,6 @@
|
||||||
|
|
||||||
document.body.addEventListener("htmx:wsOpen", () => {
|
document.body.addEventListener("htmx:wsOpen", () => {
|
||||||
updateConnectionStatus("connected");
|
updateConnectionStatus("connected");
|
||||||
reconnectAttempts = 0;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
document.body.addEventListener("htmx:wsClose", () => {
|
document.body.addEventListener("htmx:wsClose", () => {
|
||||||
|
|
@ -205,6 +204,10 @@
|
||||||
function handleWebSocketMessage(message) {
|
function handleWebSocketMessage(message) {
|
||||||
const messageType = message.type || message.event;
|
const messageType = message.type || message.event;
|
||||||
|
|
||||||
|
if (messageType === "connected") {
|
||||||
|
reconnectAttempts = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Debug logging
|
// Debug logging
|
||||||
console.log("handleWebSocketMessage called with:", { messageType, message });
|
console.log("handleWebSocketMessage called with:", { messageType, message });
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,8 @@ const ThemeManager = (() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateDropdown() {
|
function updateDropdown() {
|
||||||
// Dropdown removed
|
const select = document.getElementById("themeDropdown");
|
||||||
|
if (select) select.value = currentThemeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createDropdown() {
|
function createDropdown() {
|
||||||
|
|
@ -214,9 +215,12 @@ const ThemeManager = (() => {
|
||||||
currentThemeId = saved;
|
currentThemeId = saved;
|
||||||
loadTheme(saved);
|
loadTheme(saved);
|
||||||
|
|
||||||
// Dropdown injection removed
|
// Dropdown injection restored for the window manager
|
||||||
// const container = document.getElementById("themeSelectorContainer");
|
const container = document.getElementById("themeSelectorContainer");
|
||||||
// if (container) container.appendChild(createDropdown());
|
if (container) {
|
||||||
|
container.innerHTML = '';
|
||||||
|
container.appendChild(createDropdown());
|
||||||
|
}
|
||||||
|
|
||||||
console.log("✓ Theme Manager initialized");
|
console.log("✓ Theme Manager initialized");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -875,7 +875,6 @@
|
||||||
|
|
||||||
ws.onopen = function () {
|
ws.onopen = function () {
|
||||||
console.log("WebSocket connected");
|
console.log("WebSocket connected");
|
||||||
reconnectAttempts = 0;
|
|
||||||
disconnectNotified = false;
|
disconnectNotified = false;
|
||||||
updateConnectionStatus("connected");
|
updateConnectionStatus("connected");
|
||||||
};
|
};
|
||||||
|
|
@ -886,7 +885,10 @@
|
||||||
console.log("Chat WebSocket received:", data);
|
console.log("Chat WebSocket received:", data);
|
||||||
|
|
||||||
// Ignore connection confirmation
|
// Ignore connection confirmation
|
||||||
if (data.type === "connected") return;
|
if (data.type === "connected") {
|
||||||
|
reconnectAttempts = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Process system events (theme changes, etc)
|
// Process system events (theme changes, etc)
|
||||||
if (data.event) {
|
if (data.event) {
|
||||||
|
|
|
||||||
|
|
@ -177,10 +177,13 @@
|
||||||
<div id="taskbar-apps">
|
<div id="taskbar-apps">
|
||||||
<!-- Taskbar items populated automatically by window-manager.js -->
|
<!-- Taskbar items populated automatically by window-manager.js -->
|
||||||
</div>
|
</div>
|
||||||
<div class="toolbar-time">
|
<div class="toolbar-time" style="display: flex; align-items: center; gap: 15px;">
|
||||||
|
<div id="themeSelectorContainer"></div>
|
||||||
|
<div style="text-align: right;">
|
||||||
<div id="clock-time">00:00</div>
|
<div id="clock-time">00:00</div>
|
||||||
<div id="clock-date">01/01/2026</div>
|
<div id="clock-date">01/01/2026</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -875,7 +875,6 @@
|
||||||
|
|
||||||
ws.onopen = function () {
|
ws.onopen = function () {
|
||||||
console.log("WebSocket connected");
|
console.log("WebSocket connected");
|
||||||
reconnectAttempts = 0;
|
|
||||||
disconnectNotified = false;
|
disconnectNotified = false;
|
||||||
updateConnectionStatus("connected");
|
updateConnectionStatus("connected");
|
||||||
};
|
};
|
||||||
|
|
@ -886,7 +885,10 @@
|
||||||
console.log("Chat WebSocket received:", data);
|
console.log("Chat WebSocket received:", data);
|
||||||
|
|
||||||
// Ignore connection confirmation
|
// Ignore connection confirmation
|
||||||
if (data.type === "connected") return;
|
if (data.type === "connected") {
|
||||||
|
reconnectAttempts = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Process system events (theme changes, etc)
|
// Process system events (theme changes, etc)
|
||||||
if (data.event) {
|
if (data.event) {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue