aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-client
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-client')
-rw-r--r--packages/meshbay-client/src/main.js61
-rw-r--r--packages/meshbay-client/src/preload.js4
2 files changed, 65 insertions, 0 deletions
diff --git a/packages/meshbay-client/src/main.js b/packages/meshbay-client/src/main.js
index 15c3cc0..dd7f38e 100644
--- a/packages/meshbay-client/src/main.js
+++ b/packages/meshbay-client/src/main.js
@@ -882,6 +882,54 @@ function registerBridge() {
});
}
+ // ── Windows: switching INTO or OUT OF service mode after install ───────────
+ // build/installer.nsh's mode question is effectively one-shot: it skips
+ // itself the moment the firewall rules already exist, and per-user mode
+ // sets those up on its own, with no Scheduled Task involved. So declining
+ // once (or the rules existing for any other reason) is a dead end through
+ // the installer alone — this is the other door in, driven from the Node
+ // page instead of setup. It runs the exact same packaging/win/service-mode.ps1
+ // the installer does (task + firewall, one elevation), so the two paths
+ // can never disagree about what "service mode" means.
+ const MB_PWSH = 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe';
+
+ function winElevateServiceMode(action) {
+ return new Promise((resolve, reject) => {
+ const script = path.join(process.resourcesPath, 'service-mode.ps1');
+ if (!fs.existsSync(script)) {
+ reject(new Error('service-mode.ps1 not found — only available in an installed build'));
+ return;
+ }
+ // Start-Process -Verb RunAs is the one UAC prompt; -Wait -PassThru hands
+ // its exit code back to this unelevated process, so a decline ("The
+ // operation was canceled by the user") surfaces as a rejection here
+ // instead of silently doing nothing. Written to a temp .ps1 and run via
+ // -File (not -Command) so the target path and its own arguments bind
+ // through real PowerShell parameters instead of nested string quoting.
+ const elevator = path.join(os.tmpdir(), 'meshbay-elevate-service-mode.ps1');
+ const elevatorSrc = [
+ 'param([string]$Target, [string]$TargetArgs)',
+ '$ErrorActionPreference = "Stop"',
+ '$p = Start-Process -FilePath $Target -ArgumentList $TargetArgs -Verb RunAs -Wait -PassThru',
+ 'exit $p.ExitCode',
+ '',
+ ].join('\r\n');
+ fs.writeFileSync(elevator, elevatorSrc);
+ const targetArgs =
+ `-NoProfile -ExecutionPolicy Bypass -File "${script}" -Action ${action}`;
+ execFile(MB_PWSH,
+ ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', elevator,
+ '-Target', MB_PWSH, '-TargetArgs', targetArgs],
+ (err) => {
+ if (err) {
+ reject(new Error('Elevation was declined, or the operation failed.'));
+ return;
+ }
+ resolve();
+ });
+ });
+ }
+
async function spawnNodeDetached() {
const bin = await findNodeBinary();
if (!bin) throw new Error('meshbay-node not found on PATH');
@@ -1050,6 +1098,19 @@ function registerBridge() {
return { supported: true, installed: winAutostartInstalled() };
});
+ // Turn service mode on or off after install — one elevation, task + firewall
+ // together, via the same service-mode.ps1 the installer runs. See
+ // winElevateServiceMode() above for why this is needed at all.
+ ipcMain.handle('node:service-mode', async (_e, action) => {
+ if (process.platform !== 'win32') return { supported: false };
+ if (action !== 'install' && action !== 'remove') {
+ throw new Error(`unknown service-mode action: ${action}`);
+ }
+ await winElevateServiceMode(action);
+ const svc = await winServiceTaskStatus();
+ return { supported: true, installed: svc.installed };
+ });
+
async function probeNode() {
const nc = readNodeConfig();
const dataDir = nc ? nc.dataDir : meshbayDataDir();
diff --git a/packages/meshbay-client/src/preload.js b/packages/meshbay-client/src/preload.js
index 38c1a57..97a5063 100644
--- a/packages/meshbay-client/src/preload.js
+++ b/packages/meshbay-client/src/preload.js
@@ -110,6 +110,10 @@ contextBridge.exposeInMainWorld('meshbay', {
// action: 'install' | 'remove' | 'status' (default). Elsewhere returns
// { supported: false }.
autostart: (action) => ipcRenderer.invoke('node:autostart', action),
+ // Windows only: switch into/out of the boot-time service (Scheduled Task
+ // + firewall, one elevation). action: 'install' | 'remove'. Elsewhere
+ // returns { supported: false }.
+ serviceMode: (action) => ipcRenderer.invoke('node:service-mode', action),
},
// LAN cast relay. The main process runs a local HTTP server and the