diff --git a/ui/desktop/CHANGELOG.md b/ui/desktop/CHANGELOG.md index 614ad5f8a..93e8fb472 100644 --- a/ui/desktop/CHANGELOG.md +++ b/ui/desktop/CHANGELOG.md @@ -4,6 +4,10 @@ Canonical reference for changes, improvements, and bugfixes for Boundary Desktop ## Next +### New + +- Add prompt to close sessions when quitting client. ([PR](https://github.com/hashicorp/boundary-ui/pull/555)) + ### Bug Fixes - Close spawned boundary process on session cancel. ([PR](https://github.com/hashicorp/boundary-ui/pull/549)) diff --git a/ui/desktop/electron-app/src/index.js b/ui/desktop/electron-app/src/index.js index 93c9c3024..1f220eb85 100644 --- a/ui/desktop/electron-app/src/index.js +++ b/ui/desktop/electron-app/src/index.js @@ -7,9 +7,9 @@ const { const { session, app, + dialog, protocol, BrowserWindow, - ipcMain, Menu, MenuItem, } = require('electron'); @@ -17,6 +17,7 @@ require('./ipc/handlers.js'); const runtimeSettings = require('./services/runtime-settings.js'); const { generateCSPHeader } = require('./config/content-security-policy.js'); +const sessionManager = require('./services/session-manager.js'); const menu = require('./config/menu.js'); const appUpdater = require('./helpers/app-updater.js'); @@ -183,6 +184,30 @@ app.on('ready', async () => { }); }); +/** + * Prompt for closing spawned processes + */ +app.on('before-quit', (event) => { + if (sessionManager.hasRunningSessions) { + const dialogOpts = { + type: 'question', + buttons: ['Close', 'Cancel'], + detail: 'Close sessions before quitting?', + }; + + dialog.showMessageBox(dialogOpts).then((returnValue) => { + switch (returnValue.response) { + case 0: + sessionManager.stopAll(); + break; + default: + event.preventDefault(); + break; + } + }); + } +}); + // Handle an unhandled error in the main thread // // Note that 'uncaughtException' is a crude mechanism for exception handling intended to diff --git a/ui/desktop/electron-app/src/models/session-manager.js b/ui/desktop/electron-app/src/models/session-manager.js index dfcfd923a..311d86899 100644 --- a/ui/desktop/electron-app/src/models/session-manager.js +++ b/ui/desktop/electron-app/src/models/session-manager.js @@ -3,6 +3,14 @@ const Session = require('./session.js'); class SessionManager { #sessions = []; + /** + * Checks for running sessions + * @returns {boolean} + */ + get hasRunningSessions() { + return Boolean(this.#sessions.find((session) => session.isRunning)); + } + /** * Start a session and track it. * Returns session proxy details if successfully started. @@ -23,10 +31,11 @@ class SessionManager { */ stopById(session_id) { const session = this.#sessions.find((session) => session.id === session_id); - return session.stop(); + return session?.stop?.(); } stopAll() { + this.#sessions.forEach((session) => session.stop()); } } diff --git a/ui/desktop/electron-app/src/models/session.js b/ui/desktop/electron-app/src/models/session.js index 6ec9d3ce8..a59316f7f 100644 --- a/ui/desktop/electron-app/src/models/session.js +++ b/ui/desktop/electron-app/src/models/session.js @@ -36,7 +36,7 @@ class Session { /** * @return {boolean} */ - get isActive() { + get isRunning() { return !this.#process.killed; } @@ -60,12 +60,12 @@ class Session { */ stop() { return new Promise((resolve, reject) => { - if (this.isActive) { + if (this.isRunning) { this.#process.on('close', () => resolve()); this.#process.on('error', (e) => reject(e)); this.#process.kill(); } else { - // Do nothing when process isn't active + // Do nothing when process isn't running resolve(); } }); diff --git a/ui/desktop/electron-app/src/utils/sanitizer.js b/ui/desktop/electron-app/src/utils/sanitizer.js index 9e635b0d5..5dbc5efa9 100644 --- a/ui/desktop/electron-app/src/utils/sanitizer.js +++ b/ui/desktop/electron-app/src/utils/sanitizer.js @@ -21,7 +21,7 @@ module.exports = { urlValidate: (str) => { try { const url = new URL(str); - return `${url.protocol}://${url.host}`; + return `${url.protocol}//${url.host}`; } catch (e) { // Parse error throw new Error(`URL ${str} could not be validated.`);