feat(pwa): experimental — add my.dash.board virtual host for secure PWA install
CI / Build & Test (push) Successful in 2m51s

Serve the dashboard over a local HTTPS virtual host (my.dash.board) so
Chrome treats it as a secure origin and shows the PWA install prompt.

- Add native-host/dashboard-server.js: bare-http1 static file server
  backed by bare-bundle assets in distribution mode, disk fallback in dev
- Add setLocalVirtualHost() to virtual-hosts.js; type:local entries are
  protected from removal and filtered out of saveState persistence
- Export setLocalVirtualHost from holesail-manager/index.js
- Start dashboard server in startup.js after proxies are ready and
  register my.dash.board as a local virtual host
- Stop dashboard server in message-router.js cleanup()
- Update manifest.webmanifest: start_url, id, scope → https://my.dash.board/
- Embed all extension/dashboard/ files as bare-bundle assets in
  build-distributable.js patchBundle() — no installer copy step needed
- Hide checkbox, hs:// URL, Reconnect, and Remove controls for built-in
  my.dash.board row in the virtual hosts table UI
This commit is contained in:
Raven Scott
2026-03-01 02:58:52 -05:00
parent cbc663cacb
commit a838c3a345
8 changed files with 255 additions and 14 deletions
+25 -1
View File
@@ -129,6 +129,29 @@ async function setVirtualHost(payload) {
}
}
/**
* Register a built-in local virtual host that is served by an internal HTTP
* server rather than a Holesail P2P tunnel. The entry is marked `type: 'local'`
* so it is never persisted to state.json and cannot be removed by the user.
* @param {string} hostname - The hostname (e.g. `my.dash.board`).
* @param {number} localPort - The local port the internal server is listening on.
*/
function setLocalVirtualHost(hostname, localPort) {
virtualHosts.set(hostname, {
hostname,
hsUrl: null,
holesail: null,
localHost: TUNNEL_HOST,
localPort,
state: 'ready',
type: 'local',
createdAt: Date.now(),
reconnectTimer: null,
reconnectDelay: 0
});
debugLog('setLocalVirtualHost: hostname=', hostname, 'localPort=', localPort);
}
/**
* Remove a virtual host, close its tunnel, and release its local port.
* @param {object} payload
@@ -140,6 +163,7 @@ async function removeVirtualHost(payload) {
debugLog('removeVirtualHost: hostname=', hostname);
const entry = virtualHosts.get(hostname);
if (!entry) return { ok: false, error: 'Virtual host not found' };
if (entry.type === 'local') return { ok: false, error: 'Built-in host cannot be removed' };
if (entry.reconnectTimer) { clearTimeout(entry.reconnectTimer); entry.reconnectTimer = null; }
if (entry.localPort) releaseTunnelPort(entry.localPort);
if (entry.holesail) { try { await entry.holesail.close(); } catch (_) {} }
@@ -205,4 +229,4 @@ async function cleanupVirtualHosts() {
virtualHosts.clear();
}
module.exports = { init, setVirtualHost, removeVirtualHost, getVirtualHosts, getLocalPortForHostname, getLocalBackend, getVirtualHostMap, cleanupVirtualHosts, RECONNECT_BASE_MS, RECONNECT_MAX_MS };
module.exports = { init, setVirtualHost, setLocalVirtualHost, removeVirtualHost, getVirtualHosts, getLocalPortForHostname, getLocalBackend, getVirtualHostMap, cleanupVirtualHosts, RECONNECT_BASE_MS, RECONNECT_MAX_MS };