CI / Build & Test (push) Successful in 3m10s
- Trigger getState + applyPAC after successful restoreBackup (same as setVirtualHost/removeVirtualHost) - Always re-apply PAC when getState returns state so restored/different vhost TLDs are reflected
188 lines
7.2 KiB
JavaScript
188 lines
7.2 KiB
JavaScript
/**
|
|
* Central chrome.runtime.onMessage handler for the background service worker.
|
|
* Routes messages from content scripts and the dashboard to local handlers or
|
|
* the native host. Handled actions:
|
|
* - send Forward a typed payload to the native host (async).
|
|
* - registerSwarm Track a swarm ID for a tab (local, not forwarded).
|
|
* - subscribe Subscribe a tab to tunnel lifecycle events.
|
|
* - unsubscribe Unsubscribe a tab from tunnel lifecycle events.
|
|
* - registerDashboard Add a tab to the dashboard broadcast set.
|
|
* - unregisterDashboard Remove a tab from the dashboard broadcast set.
|
|
* - getState Fetch full extension + native host state (async).
|
|
* Depends on: state.js, logs.js, proxy.js (getActiveTlds, applyPAC),
|
|
* native-messaging.js (send, port, subscribedTabs, dashboardTabs)
|
|
*/
|
|
|
|
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
let responded = false;
|
|
function reply(value) {
|
|
if (responded) return;
|
|
responded = true;
|
|
try { sendResponse(value); } catch (_) {}
|
|
}
|
|
|
|
if (message.target !== 'holesail-native') {
|
|
reply({ error: 'Unknown target' });
|
|
return false;
|
|
}
|
|
|
|
// Handle registerSwarm locally (legacy) - don't forward to native host
|
|
if (message.action === 'send' && message.payload?.type === 'registerSwarm') {
|
|
const swarmId = message.payload?.payload?.swarmId;
|
|
if (swarmId && sender.tab?.id != null) {
|
|
if (!tabSwarms.has(sender.tab.id)) {
|
|
tabSwarms.set(sender.tab.id, new Set());
|
|
}
|
|
tabSwarms.get(sender.tab.id).add(swarmId);
|
|
const count = swarmRefCount.get(swarmId) || 0;
|
|
swarmRefCount.set(swarmId, count + 1);
|
|
log('Registered swarm', swarmId, 'for tab', sender.tab.id, '- refcount:', count + 1);
|
|
}
|
|
reply({ ok: true });
|
|
return false;
|
|
}
|
|
|
|
if (message.action === 'send') {
|
|
send(message.payload)
|
|
.then((r) => {
|
|
// After setVirtualHost, removeVirtualHost, or restoreBackup, refresh virtualHosts and re-apply PAC
|
|
if (r && r.ok && (
|
|
message.payload?.type === 'setVirtualHost' ||
|
|
message.payload?.type === 'removeVirtualHost' ||
|
|
message.payload?.type === 'restoreBackup'
|
|
)) {
|
|
send({ type: 'getState', payload: {} }).then((state) => {
|
|
if (state && state.ok && Array.isArray(state.virtualHosts)) {
|
|
extensionState.virtualHosts = state.virtualHosts;
|
|
const newTlds = getActiveTlds(extensionState.virtualHosts);
|
|
applyPAC(newTlds);
|
|
}
|
|
}).catch(() => {});
|
|
}
|
|
reply(r);
|
|
})
|
|
.catch((err) => reply({ error: err.message }));
|
|
return true; // async response
|
|
}
|
|
|
|
if (message.action === 'subscribe' && sender.tab && sender.tab.id != null) {
|
|
subscribedTabs.add(sender.tab.id);
|
|
reply({ ok: true });
|
|
return false;
|
|
}
|
|
if (message.action === 'unsubscribe' && sender.tab && sender.tab.id != null) {
|
|
subscribedTabs.delete(sender.tab.id);
|
|
reply({ ok: true });
|
|
return false;
|
|
}
|
|
|
|
if (message.action === 'registerDashboard' && sender.tab && sender.tab.id != null) {
|
|
dashboardTabs.add(sender.tab.id);
|
|
log('Dashboard registered for tab', sender.tab.id);
|
|
reply({ ok: true, logs: logs.slice(0) });
|
|
return false;
|
|
}
|
|
|
|
if (message.action === 'unregisterDashboard' && sender.tab && sender.tab.id != null) {
|
|
dashboardTabs.delete(sender.tab.id);
|
|
reply({ ok: true });
|
|
return false;
|
|
}
|
|
|
|
if (message.action === 'getState') {
|
|
debugLog('getState action: port=', !!port);
|
|
updateExtensionState();
|
|
if (port) {
|
|
send({ type: 'getState', payload: {} })
|
|
.then((payload) => {
|
|
debugLog('getState response: ok=', payload && payload.ok, 'servers=', payload && payload.servers && payload.servers.length, 'virtualHosts=', payload && payload.virtualHosts && payload.virtualHosts.length, 'proxyPort=', payload && payload.proxyPort, 'connectProxyPort=', payload && payload.connectProxyPort);
|
|
if (payload && payload.ok === true && (payload.servers || payload.virtualHosts || payload.proxyPort != null || payload.connectProxyPort != null)) {
|
|
extensionState.proxyPort = payload.proxyPort ?? null;
|
|
extensionState.connectProxyPort = payload.connectProxyPort ?? null;
|
|
extensionState.virtualHosts = payload.virtualHosts || [];
|
|
if (payload.settings && typeof payload.settings.notifyOnDisconnect === 'boolean') {
|
|
notifyOnDisconnect = payload.settings.notifyOnDisconnect;
|
|
}
|
|
if (payload.settings && typeof payload.settings.notifyOnTunnelError === 'boolean') {
|
|
notifyOnTunnelError = payload.settings.notifyOnTunnelError;
|
|
}
|
|
// Always re-apply PAC when state is synced so restored/different virtual hosts are reflected
|
|
applyPAC(getActiveTlds(extensionState.virtualHosts));
|
|
reply({
|
|
ok: true,
|
|
state: {
|
|
hostConnected: true,
|
|
servers: payload.servers || [],
|
|
virtualHosts: payload.virtualHosts || [],
|
|
serviceTunnels: payload.serviceTunnels || [],
|
|
proxyPort: payload.proxyPort ?? null,
|
|
connectProxyPort: payload.connectProxyPort ?? null,
|
|
caInstalled: payload.caInstalled ?? false,
|
|
settings: payload.settings || {},
|
|
sshConnections: payload.sshConnections || [],
|
|
rdpConnections: payload.rdpConnections || [],
|
|
trafficStats: payload.trafficStats || null,
|
|
stats: { ...extensionState.stats, ...payload.stats }
|
|
}
|
|
});
|
|
} else {
|
|
reply({
|
|
ok: true,
|
|
state: {
|
|
hostConnected: true,
|
|
servers: extensionState.servers || [],
|
|
virtualHosts: extensionState.virtualHosts || [],
|
|
serviceTunnels: [],
|
|
sshConnections: [],
|
|
rdpConnections: [],
|
|
proxyPort: null,
|
|
connectProxyPort: null,
|
|
caInstalled: false,
|
|
stats: extensionState.stats
|
|
}
|
|
});
|
|
}
|
|
})
|
|
.catch((err) => {
|
|
debugLog('getState send failed:', err && err.message);
|
|
reply({
|
|
ok: true,
|
|
state: {
|
|
hostConnected: extensionState.hostConnected,
|
|
servers: extensionState.servers || [],
|
|
virtualHosts: extensionState.virtualHosts || [],
|
|
serviceTunnels: [],
|
|
sshConnections: [],
|
|
rdpConnections: [],
|
|
proxyPort: null,
|
|
connectProxyPort: null,
|
|
caInstalled: false,
|
|
stats: extensionState.stats
|
|
}
|
|
});
|
|
});
|
|
} else {
|
|
debugLog('getState: no port, replying hostConnected=false');
|
|
reply({
|
|
ok: true,
|
|
state: {
|
|
hostConnected: false,
|
|
servers: [],
|
|
virtualHosts: [],
|
|
serviceTunnels: [],
|
|
sshConnections: [],
|
|
rdpConnections: [],
|
|
proxyPort: null,
|
|
connectProxyPort: null,
|
|
caInstalled: false,
|
|
stats: extensionState.stats
|
|
}
|
|
});
|
|
}
|
|
return true;
|
|
}
|
|
|
|
reply({ error: 'Unknown action' });
|
|
return false;
|
|
});
|