feat: prove real-time transport, fallback, and state isolation

This commit is contained in:
2026-08-28 00:35:27 +03:00
parent 0619c6be9c
commit ef141358e7
4 changed files with 174 additions and 3 deletions
+41
View File
@@ -1,5 +1,10 @@
(() => {
const target = document.querySelector('#health');
const role = new URLSearchParams(location.search).get('role') || 'spectator';
let socket;
let retryIndex = 0;
let retryTimer;
let pollTimer;
const labels = {
uptime_ms: 'Время работы (мс)', wifi_state: 'Wi-Fi', rssi_dbm: 'RSSI (дБм)',
free_heap_bytes: 'Свободная память (байт)', min_free_heap_bytes: 'Мин. свободная память (байт)',
@@ -27,6 +32,42 @@
}
}
async function pollState() {
try {
const response = await fetch(`/api/state?role=${encodeURIComponent(role)}`, { cache: 'no-store' });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const state = await response.json();
document.title = `Морской бой — версия ${state.version}`;
} catch (_) {}
}
function beginPolling() {
if (!pollTimer) pollTimer = window.setInterval(pollState, 2000);
pollState();
}
function stopPolling() {
if (pollTimer) window.clearInterval(pollTimer);
pollTimer = undefined;
}
function connectSocket() {
socket = new WebSocket(`ws://${location.host}/ws?role=${encodeURIComponent(role)}`);
socket.onopen = () => { retryIndex = 0; stopPolling(); };
socket.onmessage = event => {
const state = JSON.parse(event.data);
document.title = `Морской бой — версия ${state.version}`;
};
socket.onclose = () => {
beginPolling();
const delays = [1000, 2000, 5000, 10000];
const delay = delays[Math.min(retryIndex++, delays.length - 1)];
retryTimer = window.setTimeout(connectSocket, delay);
};
socket.onerror = () => socket.close();
}
refresh();
window.setInterval(refresh, 5000);
connectSocket();
})();