110 lines
4.5 KiB
JavaScript
110 lines
4.5 KiB
JavaScript
/**
|
|
* Orchestrates a full dashboard state refresh cycle.
|
|
* Fetches state from the background service worker, dispatches it to all page
|
|
* renderers, pings latency badges, and merges settings defaults.
|
|
*/
|
|
// Orchestrates a full state refresh cycle.
|
|
// Depends on: core/messaging.js (fetchState), core/state.js (settings, SETTINGS_DEFAULTS, currentState),
|
|
// pages/ssh.js (sshConnections, renderSshGrid),
|
|
// pages/rdp.js (rdpConnections, renderRdpGrid),
|
|
// pages/overview.js (updateDashboard),
|
|
// pages/virtual-hosts.js (updateConnectionsTable),
|
|
// pages/servers.js (updateSwarmsTable),
|
|
// pages/proxy-ca.js (updateTabsTable),
|
|
// pages/service-tunnels.js (updateServiceTunnelsTable),
|
|
// pages/settings.js (updateSettingsUI),
|
|
// pages/backups.js (refreshBackups)
|
|
|
|
const _pingInFlight = new Set();
|
|
|
|
/**
|
|
* Ping all latency badges visible in the DOM.
|
|
* Updates badge text/color in-place with a CSS transition so the table never
|
|
* reflows. Skips badges that already have an in-flight request for the same
|
|
* host:port key. Respects the `latencyPingEnabled` and `latencyPingIntervalMs`
|
|
* settings — callers should check `latencyPingEnabled` before calling this.
|
|
*/
|
|
function _pingLatencyBadges() {
|
|
if (settings.latencyPingEnabled === false) return;
|
|
const badges = document.querySelectorAll('.latency-badge[data-ping-port]');
|
|
badges.forEach(badge => {
|
|
const port = parseInt(badge.dataset.pingPort, 10);
|
|
const host = badge.dataset.pingHost || '127.0.0.1';
|
|
if (!port) return;
|
|
const key = host + ':' + port;
|
|
if (_pingInFlight.has(key)) return;
|
|
_pingInFlight.add(key);
|
|
// Dim badge while request is in-flight — avoids a hard jump on update
|
|
badge.classList.add('pinging');
|
|
chrome.runtime.sendMessage(
|
|
{ target: 'holesail-native', action: 'send', payload: { type: 'pingTunnel', payload: { host, port } } },
|
|
(response) => {
|
|
_pingInFlight.delete(key);
|
|
void chrome.runtime.lastError;
|
|
if (!badge.isConnected) return;
|
|
badge.classList.remove('pinging');
|
|
if (response && response.ok) {
|
|
const ms = response.latencyMs;
|
|
const color = ms < 50 ? 'var(--green)' : ms < 200 ? 'var(--amber)' : 'var(--red)';
|
|
// Only update if the value actually changed to avoid unnecessary repaints
|
|
const next = ms + 'ms';
|
|
if (badge.textContent !== next) badge.textContent = next;
|
|
badge.style.color = color;
|
|
} else {
|
|
if (badge.textContent !== '—') badge.textContent = '—';
|
|
badge.style.color = 'var(--text4)';
|
|
}
|
|
}
|
|
);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Fetch the latest extension state and update all dashboard page renderers.
|
|
* Also pings latency badges and syncs settings defaults.
|
|
* Called on a 2-second interval by `init.js`.
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async function refresh() {
|
|
const state = await fetchState();
|
|
if (state) {
|
|
// Sync SSH connections from native host state — decode base64 password if present
|
|
if (Array.isArray(state.sshConnections)) {
|
|
sshConnections = state.sshConnections.map(c => {
|
|
const existing = sshConnections.find(e => e.id === c.id);
|
|
let password = (existing && existing.password) || '';
|
|
if (!password && c.passwordB64) {
|
|
try { password = decodeURIComponent(escape(atob(c.passwordB64))); } catch (_) {}
|
|
}
|
|
return { ...c, password };
|
|
});
|
|
renderSshGrid();
|
|
}
|
|
// Sync RDP connections from native host state — decode base64 password if present
|
|
if (Array.isArray(state.rdpConnections)) {
|
|
rdpConnections = state.rdpConnections.map(c => {
|
|
const existing = rdpConnections.find(e => e.id === c.id);
|
|
let password = (existing && existing.password) || '';
|
|
if (!password && c.passwordB64) {
|
|
try { password = decodeURIComponent(escape(atob(c.passwordB64))); } catch (_) {}
|
|
}
|
|
return { ...c, password };
|
|
});
|
|
renderRdpGrid();
|
|
}
|
|
// Sync settings from native host state
|
|
if (state.settings && typeof state.settings === 'object') {
|
|
settings = { ...SETTINGS_DEFAULTS, ...state.settings };
|
|
}
|
|
updateDashboard(state);
|
|
updateConnectionsTable(state);
|
|
updateSwarmsTable(state);
|
|
updateTabsTable(state);
|
|
updateServiceTunnelsTable(state);
|
|
updateSettingsUI();
|
|
}
|
|
const sshCountEl = $('sshCount');
|
|
if (sshCountEl) sshCountEl.textContent = sshConnections.length;
|
|
refreshBackups();
|
|
}
|