From e89a57bb97b5a0d624e8d490b6b8aa38ba140817 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Sat, 5 Sep 2026 13:06:55 +0200 Subject: fix(win): graceful shutdown, one startup-mode control, and a stray-\r bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Windows-only changes, all found by actually running the previous session's work rather than by review alone: - CTRL_CLOSE_EVENT/LOGOFF/SHUTDOWN handler (platform.py, ctypes SetConsoleCtrlHandler) so closing a console window, signing off, or a system shutdown runs the daemon's real _shutdown() instead of Windows just ending the process — closing WebRTC sessions and any in-flight ffmpeg transcode instead of orphaning it. `taskkill /F` itself stays uncatchable (like SIGKILL), so autostart_run() now spawns with CREATE_NEW_PROCESS_GROUP instead of DETACHED_PROCESS and autostart_end() tries CTRL_BREAK_EVENT against the recorded pid first, falling back to the hard kill only if that doesn't stop it in time. - Replaced the Node page's two independent autostart/service-mode toggles with one "start automatically" select (off / at sign-in / as a background service). The old pair let both be active at once — starting the daemon twice, at boot and at sign-in — and their layout broke wrapping inside .node-service's flex row. The new control always removes whichever mechanism is active before installing the target; platform.py's service_install() does the same on the CLI side. The "background service" option disables itself (with a hint pointing at the CLI) when running unpackaged, since service-mode.ps1/service.ps1/firewall.ps1 all assume an installed build's layout — verified live rather than assumed by actually running those scripts unelevated. - findNodeBinary() no longer bakes a stray \r into resolved paths. Found by rebooting after enabling per-user autostart: where.exe listed two matches, and stdout.trim().split('\n')[0] only strips the whole string's ends, leaving line one's own trailing \r attached — which landed inside the Startup .vbs's quoted path and broke it with "Unterminated string constant" at boot. Fixed by splitting on \r?\n and trimming every line. - Dependency audit for the Windows installer (docs/WINDOWS-PORT.md): no VC++ Redistributable needed, confirmed by inspecting the built node-runtime's actual import table rather than assuming. New docs/windows-build.md: a concise clone-to-installer build guide. Co-Authored-By: Claude Sonnet 5 --- .../src/meshbay_hub/static/locales/de.js | 10 +- .../src/meshbay_hub/static/locales/en.js | 10 +- .../src/meshbay_hub/static/locales/es.js | 10 +- .../src/meshbay_hub/static/locales/fr.js | 10 +- .../src/meshbay_hub/static/locales/it.js | 10 +- .../src/meshbay_hub/static/locales/ja.js | 10 +- .../src/meshbay_hub/static/locales/nl.js | 10 +- .../src/meshbay_hub/static/locales/pl.js | 10 +- .../src/meshbay_hub/static/locales/pt-BR.js | 10 +- .../src/meshbay_hub/static/locales/zh-CN.js | 10 +- .../src/meshbay_hub/static/node-page.js | 111 +++++++++++++-------- 11 files changed, 127 insertions(+), 84 deletions(-) (limited to 'packages/meshbay-hub/src') diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js index bb81fee..49b5f9e 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js @@ -678,11 +678,13 @@ export default { 'node.service_stopping': 'Wird angehalten…', 'node.service_restart': 'Neu starten', 'node.service_restarting': 'Wird neu gestartet…', - 'node.autostart_label': 'Automatisch bei der Anmeldung starten', - 'node.autostart_updating': 'Wird aktualisiert…', 'node.service_mode_hint': 'Läuft als Hintergrunddienst — startet beim Booten, vor der Anmeldung.', - 'node.service_mode_label': 'Als Hintergrunddienst ausführen (startet beim Booten, vor der Anmeldung)', - 'node.service_mode_updating': 'Modus wird gewechselt — achten Sie auf eine Administrator-Eingabeaufforderung…', + 'node.startup_mode_label': 'Automatisch starten:', + 'node.startup_mode_off': 'Aus (manuell starten)', + 'node.startup_mode_signin': 'Bei der Anmeldung', + 'node.startup_mode_service': 'Als Hintergrunddienst (startet beim Booten)', + 'node.startup_mode_updating': 'Modus wird gewechselt — achten Sie auf eine Administrator-Eingabeaufforderung…', + 'node.startup_mode_service_unavailable_hint': 'Der Hintergrunddienst-Modus erfordert eine installierte Version. Führen Sie zum lokalen Testen "meshbay-node service install" in einer PowerShell mit Administratorrechten aus.', 'node.not_operator': 'Ihr Node konnte nicht erreicht werden. Stellen Sie sicher, dass er läuft.', 'node.offline': 'Node ist offline', 'node.retry': 'Erneut versuchen', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js index 2817432..7ea8989 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js @@ -734,11 +734,13 @@ export default { 'node.service_stopping': 'Stopping…', 'node.service_restart': 'Restart', 'node.service_restarting': 'Restarting…', - 'node.autostart_label': 'Start automatically at sign-in', - 'node.autostart_updating': 'Updating…', 'node.service_mode_hint': 'Running as a background service — it starts at boot, before sign-in.', - 'node.service_mode_label': 'Run as a background service (starts at boot, before sign-in)', - 'node.service_mode_updating': 'Switching mode — check for an administrator prompt…', + 'node.startup_mode_label': 'Start automatically:', + 'node.startup_mode_off': 'Off (start manually)', + 'node.startup_mode_signin': 'At sign-in', + 'node.startup_mode_service': 'As a background service (starts at boot)', + 'node.startup_mode_updating': 'Switching mode — check for an administrator prompt…', + 'node.startup_mode_service_unavailable_hint': 'Background service mode needs an installed build. For local testing, run "meshbay-node service install" from an elevated PowerShell.', 'node.offline': 'Node is offline', 'node.no_groups': 'No groups configured on this node.', 'node.retry': 'Retry', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js index 0f3ce21..6109d4f 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js @@ -674,11 +674,13 @@ export default { 'node.service_stopping': 'Deteniendo…', 'node.service_restart': 'Reiniciar', 'node.service_restarting': 'Reiniciando…', - 'node.autostart_label': 'Iniciar automáticamente al iniciar sesión', - 'node.autostart_updating': 'Actualizando…', 'node.service_mode_hint': 'Se ejecuta como servicio en segundo plano — se inicia al arrancar, antes de iniciar sesión.', - 'node.service_mode_label': 'Ejecutar como servicio en segundo plano (se inicia al arrancar, antes de iniciar sesión)', - 'node.service_mode_updating': 'Cambiando de modo — compruebe si aparece un aviso de administrador…', + 'node.startup_mode_label': 'Iniciar automáticamente:', + 'node.startup_mode_off': 'Desactivado (iniciar manualmente)', + 'node.startup_mode_signin': 'Al iniciar sesión', + 'node.startup_mode_service': 'Como servicio en segundo plano (se inicia al arrancar)', + 'node.startup_mode_updating': 'Cambiando de modo — compruebe si aparece un aviso de administrador…', + 'node.startup_mode_service_unavailable_hint': 'El modo de servicio en segundo plano requiere una versión instalada. Para pruebas locales, ejecute "meshbay-node service install" desde una PowerShell con privilegios de administrador.', 'node.not_operator': 'No se pudo contactar con su node. Asegúrese de que esté en ejecución.', 'node.offline': 'Node sin conexión', 'node.retry': 'Reintentar', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js index c3c4846..926f64a 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js @@ -677,11 +677,13 @@ export default { 'node.service_stopping': 'Arrêt…', 'node.service_restart': 'Redémarrer', 'node.service_restarting': 'Redémarrage…', - 'node.autostart_label': 'Démarrer automatiquement à l\'ouverture de session', - 'node.autostart_updating': 'Mise à jour…', 'node.service_mode_hint': 'Fonctionne comme service en arrière-plan — démarre au boot, avant l\'ouverture de session.', - 'node.service_mode_label': 'Exécuter comme service en arrière-plan (démarre au boot, avant l\'ouverture de session)', - 'node.service_mode_updating': 'Changement de mode — vérifiez une invite d\'administrateur…', + 'node.startup_mode_label': 'Démarrer automatiquement :', + 'node.startup_mode_off': 'Désactivé (démarrage manuel)', + 'node.startup_mode_signin': 'À l\'ouverture de session', + 'node.startup_mode_service': 'Comme service en arrière-plan (démarre au boot)', + 'node.startup_mode_updating': 'Changement de mode — vérifiez une invite d\'administrateur…', + 'node.startup_mode_service_unavailable_hint': 'Le mode service en arrière-plan nécessite une version installée. Pour un test local, exécutez "meshbay-node service install" depuis un PowerShell administrateur.', 'node.not_operator': 'Impossible de joindre votre node. Vérifiez qu\'il est en cours d\'exécution.', 'node.offline': 'Node hors ligne', 'node.retry': 'Réessayer', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js index f39ab3c..f7011ec 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js @@ -676,11 +676,13 @@ export default { 'node.service_stopping': 'Arresto…', 'node.service_restart': 'Riavvia', 'node.service_restarting': 'Riavvio…', - 'node.autostart_label': 'Avvia automaticamente all\'accesso', - 'node.autostart_updating': 'Aggiornamento…', 'node.service_mode_hint': 'In esecuzione come servizio in background — si avvia all\'avvio del sistema, prima dell\'accesso.', - 'node.service_mode_label': 'Esegui come servizio in background (si avvia all\'avvio del sistema, prima dell\'accesso)', - 'node.service_mode_updating': 'Cambio modalità — controlli se compare una richiesta di amministratore…', + 'node.startup_mode_label': 'Avvia automaticamente:', + 'node.startup_mode_off': 'Disattivato (avvio manuale)', + 'node.startup_mode_signin': 'All\'accesso', + 'node.startup_mode_service': 'Come servizio in background (si avvia all\'avvio del sistema)', + 'node.startup_mode_updating': 'Cambio modalità — controlli se compare una richiesta di amministratore…', + 'node.startup_mode_service_unavailable_hint': 'La modalità servizio in background richiede una build installata. Per test locali, eseguire "meshbay-node service install" da un PowerShell con privilegi di amministratore.', 'node.not_operator': 'Impossibile raggiungere il suo node. Si assicuri che sia in esecuzione.', 'node.offline': 'Node non in linea', 'node.retry': 'Riprova', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js index 1544940..592f939 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js @@ -664,11 +664,13 @@ export default { 'node.service_stopping': '停止中…', 'node.service_restart': '再起動', 'node.service_restarting': '再起動中…', - 'node.autostart_label': 'サインイン時に自動的に開始する', - 'node.autostart_updating': '更新中…', 'node.service_mode_hint': 'バックグラウンドサービスとして実行中 — サインインより前、起動時に開始します。', - 'node.service_mode_label': 'バックグラウンドサービスとして実行する(サインインより前、起動時に開始)', - 'node.service_mode_updating': 'モードを切り替え中 — 管理者の確認ダイアログをご確認ください…', + 'node.startup_mode_label': '自動的に開始:', + 'node.startup_mode_off': 'オフ(手動で開始)', + 'node.startup_mode_signin': 'サインイン時', + 'node.startup_mode_service': 'バックグラウンドサービスとして(起動時に開始)', + 'node.startup_mode_updating': 'モードを切り替え中 — 管理者の確認ダイアログをご確認ください…', + 'node.startup_mode_service_unavailable_hint': 'バックグラウンドサービスモードにはインストール済みのビルドが必要です。ローカルでテストする場合は、管理者権限の PowerShell で "meshbay-node service install" を実行してください。', 'node.not_operator': 'node に接続できませんでした。node が実行中であることをご確認ください。', 'node.offline': 'Node はオフラインです', 'node.retry': '再試行', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js index 19feec2..eba234e 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js @@ -678,11 +678,13 @@ export default { 'node.service_stopping': 'Stoppen…', 'node.service_restart': 'Herstarten', 'node.service_restarting': 'Herstarten…', - 'node.autostart_label': 'Automatisch starten bij aanmelden', - 'node.autostart_updating': 'Bijwerken…', 'node.service_mode_hint': 'Actief als achtergrondservice — start bij het opstarten, vóór het aanmelden.', - 'node.service_mode_label': 'Uitvoeren als achtergrondservice (start bij het opstarten, vóór het aanmelden)', - 'node.service_mode_updating': 'Modus wijzigen — let op een beheerdersprompt…', + 'node.startup_mode_label': 'Automatisch starten:', + 'node.startup_mode_off': 'Uit (handmatig starten)', + 'node.startup_mode_signin': 'Bij aanmelden', + 'node.startup_mode_service': 'Als achtergrondservice (start bij het opstarten)', + 'node.startup_mode_updating': 'Modus wijzigen — let op een beheerdersprompt…', + 'node.startup_mode_service_unavailable_hint': 'Achtergrondservice-modus vereist een geïnstalleerde build. Voer voor lokaal testen "meshbay-node service install" uit vanuit een PowerShell met beheerdersrechten.', 'node.not_operator': 'Uw node is niet bereikbaar. Controleer of hij draait.', 'node.offline': 'Node is offline', 'node.retry': 'Opnieuw proberen', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js index 512c4f3..999e689 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js @@ -696,11 +696,13 @@ export default { 'node.service_stopping': 'Zatrzymywanie…', 'node.service_restart': 'Uruchom ponownie', 'node.service_restarting': 'Ponowne uruchamianie…', - 'node.autostart_label': 'Uruchamiaj automatycznie przy logowaniu', - 'node.autostart_updating': 'Aktualizowanie…', 'node.service_mode_hint': 'Działa jako usługa w tle — uruchamia się przy starcie systemu, przed zalogowaniem.', - 'node.service_mode_label': 'Uruchom jako usługę w tle (uruchamia się przy starcie systemu, przed zalogowaniem)', - 'node.service_mode_updating': 'Zmiana trybu — proszę sprawdzić, czy pojawiło się okno uprawnień administratora…', + 'node.startup_mode_label': 'Uruchamiaj automatycznie:', + 'node.startup_mode_off': 'Wyłączone (uruchamianie ręczne)', + 'node.startup_mode_signin': 'Przy logowaniu', + 'node.startup_mode_service': 'Jako usługa w tle (uruchamia się przy starcie systemu)', + 'node.startup_mode_updating': 'Zmiana trybu — proszę sprawdzić, czy pojawiło się okno uprawnień administratora…', + 'node.startup_mode_service_unavailable_hint': 'Tryb usługi w tle wymaga zainstalowanej wersji. Aby przetestować lokalnie, uruchom "meshbay-node service install" w PowerShell z uprawnieniami administratora.', 'node.not_operator': 'Nie udało się połączyć z Pana/Pani node. Upewnij się, że działa.', 'node.offline': 'Node jest niedostępny', 'node.retry': 'Ponów', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js index 8e33d74..79d2079 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js @@ -675,11 +675,13 @@ export default { 'node.service_stopping': 'Parando…', 'node.service_restart': 'Reiniciar', 'node.service_restarting': 'Reiniciando…', - 'node.autostart_label': 'Iniciar automaticamente ao entrar na sessão', - 'node.autostart_updating': 'Atualizando…', 'node.service_mode_hint': 'Em execução como serviço em segundo plano — inicia na inicialização, antes do login.', - 'node.service_mode_label': 'Executar como serviço em segundo plano (inicia na inicialização, antes do login)', - 'node.service_mode_updating': 'Alternando modo — verifique se aparece um aviso de administrador…', + 'node.startup_mode_label': 'Iniciar automaticamente:', + 'node.startup_mode_off': 'Desativado (iniciar manualmente)', + 'node.startup_mode_signin': 'Ao entrar na sessão', + 'node.startup_mode_service': 'Como serviço em segundo plano (inicia na inicialização)', + 'node.startup_mode_updating': 'Alternando modo — verifique se aparece um aviso de administrador…', + 'node.startup_mode_service_unavailable_hint': 'O modo de serviço em segundo plano requer uma versão instalada. Para testes locais, execute "meshbay-node service install" em um PowerShell com privilégios de administrador.', 'node.not_operator': 'Não foi possível alcançar seu node. Verifique se ele está em execução.', 'node.offline': 'Node está off-line', 'node.retry': 'Tentar novamente', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js index 299dc4f..d67c34b 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js @@ -651,11 +651,13 @@ export default { 'node.service_stopping': '正在停止…', 'node.service_restart': '重启', 'node.service_restarting': '正在重启…', - 'node.autostart_label': '登录时自动启动', - 'node.autostart_updating': '正在更新…', 'node.service_mode_hint': '以后台服务方式运行 — 在开机时启动,早于登录。', - 'node.service_mode_label': '以后台服务方式运行(在开机时启动,早于登录)', - 'node.service_mode_updating': '正在切换模式 — 请留意管理员权限提示…', + 'node.startup_mode_label': '自动启动:', + 'node.startup_mode_off': '关闭(手动启动)', + 'node.startup_mode_signin': '登录时', + 'node.startup_mode_service': '作为后台服务(开机时启动)', + 'node.startup_mode_updating': '正在切换模式 — 请留意管理员权限提示…', + 'node.startup_mode_service_unavailable_hint': '后台服务模式需要已安装的版本。如需本地测试,请在具有管理员权限的 PowerShell 中运行 "meshbay-node service install"。', 'node.not_operator': '无法连接到您的 node。请确保它正在运行。', 'node.offline': 'Node 已离线', 'node.retry': '重试', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/node-page.js b/packages/meshbay-hub/src/meshbay_hub/static/node-page.js index 2a0b0d7..ec73128 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/node-page.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/node-page.js @@ -47,21 +47,35 @@ function NodeServicePanel({ onChanged }) { } }, [refresh, onChanged]); - const toggleAutostart = useCallback(() => { - act('autostart', () => (info && info.autostart - ? platform.node.autostart.remove() - : platform.node.autostart.install())); - }, [act, info]); + // "off" / "signin" / "service" -- derived from the status payload, no new + // backend field needed: mode/autostart already distinguish all three. + const startupMode = (i) => { + if (!i) return 'off'; + if (i.mode === 'service') return i.mode; + return i.autostart ? 'signin' : 'off'; + }; // Switching mode itself — the installer's own choice is effectively one-shot // (it skips the question once the firewall rules exist for any reason, and // per-user mode sets those up on its own with no Scheduled Task), so this is // the only way back in if service mode was declined, or out if it is no // longer wanted. One elevation, task + firewall together, same script. - const toggleServiceMode = useCallback(() => { - act('serviceMode', () => (info && info.mode === 'service' - ? platform.node.serviceMode.remove() - : platform.node.serviceMode.install())); + // + // The two mechanisms are mutually exclusive by construction here: never + // both installed at once, which would start the daemon twice (once at + // boot via the Scheduled Task, again at sign-in via the Startup .vbs). + // Always remove whichever one is currently active before installing the + // target, so every transition -- not just the two that used to be + // separate toggles -- keeps that invariant. + const changeStartupMode = useCallback((target) => { + const current = startupMode(info); + if (target === current) return; + act('startupMode', async () => { + if (current === 'service') await platform.node.serviceMode.remove(); + else if (current === 'signin') await platform.node.autostart.remove(); + if (target === 'service') await platform.node.serviceMode.install(); + else if (target === 'signin') await platform.node.autostart.install(); + }); }, [act, info]); if (!platform.node.service.available) return null; @@ -79,44 +93,53 @@ function NodeServicePanel({ onChanged }) { const label = info.installed ? t('node.service_state_' + stateKey) : t('node.service_not_installed'); + // Own row, below the status/actions card rather than a further item + // crammed into its flex-wrap line -- that (plus two independent toggles + // for what is really one choice) is what made this a mess before. + const showStartupRow = (platform.node.autostart.available + || platform.node.serviceMode.available) && typeof info.mode === 'string'; + return html` -
-
- - ${label} -
- ${err && html`
${err}
`} -
- - ${info.installed && html` - - +
+
+
+ + ${label} +
+ ${err && html`
${err}
`} +
+ + ${info.installed && html` + + + `} +
+ ${info.mode === 'service' && html` +

${t('node.service_mode_hint')}

`}
- ${info.mode === 'service' && html` -

${t('node.service_mode_hint')}

- `} - ${platform.node.autostart.available && typeof info.autostart === 'boolean' && html` - - `} - ${platform.node.serviceMode.available && typeof info.mode === 'string' && html` - + ${showStartupRow && html` +
+ ${t('node.startup_mode_label')} + +
+ ${busy === 'startupMode' && html` +

${t('node.startup_mode_updating')}

`} + ${!info.canElevate && html` +

${t('node.startup_mode_service_unavailable_hint')}

`} `}
`; } -- cgit v1.2.3