From 2098720baff5b1d57ff596306ccb32c61037b458 Mon Sep 17 00:00:00 2001 From: Mohanad Ahmed Date: Tue, 9 Sep 2025 23:06:08 +0300 Subject: [PATCH 1/6] add HTTPS config options - Add useHttps, certPath, and keyPath fields to APIServerConfig interface and default config. - These fields allow toggling HTTPS and specifying certificate and key file paths for the API server. --- src/plugins/api-server/config.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/plugins/api-server/config.ts b/src/plugins/api-server/config.ts index 22d8e661fe..2a390decf1 100644 --- a/src/plugins/api-server/config.ts +++ b/src/plugins/api-server/config.ts @@ -11,6 +11,9 @@ export interface APIServerConfig { secret: string; authorizedClients: string[]; + useHttps: boolean; + certPath: string; + keyPath: string; } export const defaultAPIServerConfig: APIServerConfig = { @@ -21,4 +24,7 @@ export const defaultAPIServerConfig: APIServerConfig = { secret: Date.now().toString(36), authorizedClients: [], + useHttps: false, + certPath: '', + keyPath: '', }; From d47efcc84b291e6b331c26389f6ada5107bc249f Mon Sep 17 00:00:00 2001 From: Mohanad Ahmed Date: Tue, 9 Sep 2025 23:06:48 +0300 Subject: [PATCH 2/6] add HTTPS and certificate selection to menu - Add a submenu for HTTPS settings in the API server plugin menu. - Allow users to enable HTTPS and select certificate and key files via file dialogs. - Fetch latest config before updating cert/key paths to avoid overwriting values. --- src/plugins/api-server/menu.ts | 39 ++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/plugins/api-server/menu.ts b/src/plugins/api-server/menu.ts index 4a28ad9a39..0ab55274a0 100644 --- a/src/plugins/api-server/menu.ts +++ b/src/plugins/api-server/menu.ts @@ -1,3 +1,4 @@ +import { dialog } from 'electron'; import prompt from 'custom-electron-prompt'; import { t } from '@/i18n'; @@ -93,5 +94,43 @@ export const onMenu = async ({ }, ], }, + { + label: t('plugins.api-server.menu.https.label'), + type: 'checkbox', + checked: config.useHttps, + click(menuItem) { + setConfig({ ...config, useHttps: menuItem.checked }); + }, + }, + { + label: t('plugins.api-server.menu.cert.label'), + type: 'normal', + async click() { + const config = await getConfig(); + const result = await dialog.showOpenDialog(window, { + title: t('plugins.api-server.menu.cert.dialogTitle'), + filters: [{ name: 'Certificate', extensions: ['crt', 'pem'] }], + properties: ['openFile'], + }); + if (!result.canceled && result.filePaths.length > 0) { + setConfig({ ...config, certPath: result.filePaths[0] }); + } + }, + }, + { + label: t('plugins.api-server.menu.key.label'), + type: 'normal', + async click() { + const config = await getConfig(); + const result = await dialog.showOpenDialog(window, { + title: t('plugins.api-server.menu.key.dialogTitle'), + filters: [{ name: 'Private Key', extensions: ['key', 'pem'] }], + properties: ['openFile'], + }); + if (!result.canceled && result.filePaths.length > 0) { + setConfig({ ...config, keyPath: result.filePaths[0] }); + } + }, + }, ]; }; From a77bdb5ccc8f09f96c5b12b24983fe7ed50c3107 Mon Sep 17 00:00:00 2001 From: Mohanad Ahmed Date: Tue, 9 Sep 2025 23:07:22 +0300 Subject: [PATCH 3/6] add English strings for API server HTTPS menu - Add labels and dialog titles for HTTPS submenu, certificate, and key selection in the API server plugin. --- src/i18n/resources/en.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/i18n/resources/en.json b/src/i18n/resources/en.json index 85638a0f6d..b398258f57 100644 --- a/src/i18n/resources/en.json +++ b/src/i18n/resources/en.json @@ -322,6 +322,20 @@ }, "port": { "label": "Port" + }, + "https-submenu": { + "label": "HTTPS & Certificates" + }, + "https": { + "label": "Enable HTTPS" + }, + "cert": { + "label": "Certificate file (.crt/.pem)", + "dialogTitle": "Select HTTPS certificate file" + }, + "key": { + "label": "Private key file (.key/.pem)", + "dialogTitle": "Select HTTPS private key file" } }, "name": "API Server [Beta]", From bbf655a7abdf5160f3883276db605cbf7e157aa9 Mon Sep 17 00:00:00 2001 From: Mohanad Ahmed Date: Tue, 9 Sep 2025 23:07:47 +0300 Subject: [PATCH 4/6] update BackendType for HTTPS config - Update BackendType to include new HTTPS config fields. - Change run method to accept full APIServerConfig object. --- src/plugins/api-server/backend/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/api-server/backend/types.ts b/src/plugins/api-server/backend/types.ts index 2f20dbe93d..a49e71f93a 100644 --- a/src/plugins/api-server/backend/types.ts +++ b/src/plugins/api-server/backend/types.ts @@ -17,6 +17,6 @@ export type BackendType = { injectWebSocket?: (server: ReturnType) => void; init: (ctx: BackendContext) => void; - run: (hostname: string, port: number) => void; + run: (config: APIServerConfig) => void; end: () => void; }; From c7fe9efcfd4e7d5709a28c8e2c62a52a45e7eb85 Mon Sep 17 00:00:00 2001 From: Mohanad Ahmed Date: Tue, 9 Sep 2025 23:08:26 +0300 Subject: [PATCH 5/6] implement HTTPS support in backend - Update backend to start an HTTPS server when enabled in config, using provided cert and key paths. - Ensure server restarts on any relevant config change (hostname, port, useHttps, certPath, keyPath). --- src/plugins/api-server/backend/main.ts | 43 ++++++++++++++++++++------ 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/src/plugins/api-server/backend/main.ts b/src/plugins/api-server/backend/main.ts index ae05d9247c..02a99e344d 100644 --- a/src/plugins/api-server/backend/main.ts +++ b/src/plugins/api-server/backend/main.ts @@ -1,3 +1,7 @@ +import { createServer as createHttpServer } from 'node:http'; +import { createServer as createHttpsServer } from 'node:https'; +import { readFileSync } from 'node:fs'; + import { jwt } from 'hono/jwt'; import { OpenAPIHono as Hono } from '@hono/zod-openapi'; import { cors } from 'hono/cors'; @@ -48,22 +52,26 @@ export const backend = createBackend({ (newVolumeState: VolumeState) => (this.volumeState = newVolumeState), ); - this.run(config.hostname, config.port); + this.run(config); }, stop() { this.end(); }, onConfigChange(config) { + const old = this.oldConfig; if ( - this.oldConfig?.hostname === config.hostname && - this.oldConfig?.port === config.port + old?.hostname === config.hostname && + old?.port === config.port && + old?.useHttps === config.useHttps && + old?.certPath === config.certPath && + old?.keyPath === config.keyPath ) { this.oldConfig = config; return; } this.end(); - this.run(config.hostname, config.port); + this.run(config); this.oldConfig = config; }, @@ -153,15 +161,30 @@ export const backend = createBackend({ this.injectWebSocket = ws.injectWebSocket.bind(this); }, - run(hostname, port) { + run(config) { if (!this.app) return; try { - this.server = serve({ - fetch: this.app.fetch.bind(this.app), - port, - hostname, - }); + const serveOptions = + config.useHttps && config.certPath && config.keyPath + ? { + fetch: this.app.fetch.bind(this.app), + port: config.port, + hostname: config.hostname, + createServer: createHttpsServer, + serverOptions: { + key: readFileSync(config.keyPath), + cert: readFileSync(config.certPath), + }, + } + : { + fetch: this.app.fetch.bind(this.app), + port: config.port, + hostname: config.hostname, + createServer: createHttpServer, + }; + + this.server = serve(serveOptions); if (this.injectWebSocket && this.server) { this.injectWebSocket(this.server); From bfb65c2be4584cb47a6f39061dabe61a0352867a Mon Sep 17 00:00:00 2001 From: Mohanad Ahmed Date: Thu, 11 Sep 2025 14:10:02 +0300 Subject: [PATCH 6/6] refactor HTTPS menu structure and labels for improved and consistent organization --- src/i18n/resources/en.json | 26 ++++++------ src/plugins/api-server/menu.ts | 78 +++++++++++++++++++--------------- 2 files changed, 57 insertions(+), 47 deletions(-) diff --git a/src/i18n/resources/en.json b/src/i18n/resources/en.json index b398258f57..041c7f13eb 100644 --- a/src/i18n/resources/en.json +++ b/src/i18n/resources/en.json @@ -323,19 +323,21 @@ "port": { "label": "Port" }, - "https-submenu": { - "label": "HTTPS & Certificates" - }, "https": { - "label": "Enable HTTPS" - }, - "cert": { - "label": "Certificate file (.crt/.pem)", - "dialogTitle": "Select HTTPS certificate file" - }, - "key": { - "label": "Private key file (.key/.pem)", - "dialogTitle": "Select HTTPS private key file" + "label": "HTTPS & Certificates", + "submenu": { + "enable-https": { + "label": "Enable HTTPS" + }, + "cert": { + "label": "Certificate file (.crt/.pem)", + "dialogTitle": "Select HTTPS certificate file" + }, + "key": { + "label": "Private key file (.key/.pem)", + "dialogTitle": "Select HTTPS private key file" + } + } } }, "name": "API Server [Beta]", diff --git a/src/plugins/api-server/menu.ts b/src/plugins/api-server/menu.ts index 0ab55274a0..08800e06ac 100644 --- a/src/plugins/api-server/menu.ts +++ b/src/plugins/api-server/menu.ts @@ -96,41 +96,49 @@ export const onMenu = async ({ }, { label: t('plugins.api-server.menu.https.label'), - type: 'checkbox', - checked: config.useHttps, - click(menuItem) { - setConfig({ ...config, useHttps: menuItem.checked }); - }, - }, - { - label: t('plugins.api-server.menu.cert.label'), - type: 'normal', - async click() { - const config = await getConfig(); - const result = await dialog.showOpenDialog(window, { - title: t('plugins.api-server.menu.cert.dialogTitle'), - filters: [{ name: 'Certificate', extensions: ['crt', 'pem'] }], - properties: ['openFile'], - }); - if (!result.canceled && result.filePaths.length > 0) { - setConfig({ ...config, certPath: result.filePaths[0] }); - } - }, - }, - { - label: t('plugins.api-server.menu.key.label'), - type: 'normal', - async click() { - const config = await getConfig(); - const result = await dialog.showOpenDialog(window, { - title: t('plugins.api-server.menu.key.dialogTitle'), - filters: [{ name: 'Private Key', extensions: ['key', 'pem'] }], - properties: ['openFile'], - }); - if (!result.canceled && result.filePaths.length > 0) { - setConfig({ ...config, keyPath: result.filePaths[0] }); - } - }, + type: 'submenu', + submenu: [ + { + label: t('plugins.api-server.menu.https.submenu.enable-https.label'), + type: 'checkbox', + checked: config.useHttps, + click(menuItem) { + setConfig({ ...config, useHttps: menuItem.checked }); + }, + }, + { + label: t('plugins.api-server.menu.https.submenu.cert.label'), + type: 'normal', + async click() { + const config = await getConfig(); + const result = await dialog.showOpenDialog(window, { + title: t( + 'plugins.api-server.menu.https.submenu.cert.dialogTitle', + ), + filters: [{ name: 'Certificate', extensions: ['crt', 'pem'] }], + properties: ['openFile'], + }); + if (!result.canceled && result.filePaths.length > 0) { + setConfig({ ...config, certPath: result.filePaths[0] }); + } + }, + }, + { + label: t('plugins.api-server.menu.https.submenu.key.label'), + type: 'normal', + async click() { + const config = await getConfig(); + const result = await dialog.showOpenDialog(window, { + title: t('plugins.api-server.menu.https.submenu.key.dialogTitle'), + filters: [{ name: 'Private Key', extensions: ['key', 'pem'] }], + properties: ['openFile'], + }); + if (!result.canceled && result.filePaths.length > 0) { + setConfig({ ...config, keyPath: result.filePaths[0] }); + } + }, + }, + ], }, ]; };