mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-10 10:11:46 +00:00
feat(api-server): Add HTTPS support and custom certificate configuration (#3874)
This commit is contained in:
@ -323,6 +323,22 @@
|
||||
},
|
||||
"port": {
|
||||
"label": "Port"
|
||||
},
|
||||
"https": {
|
||||
"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]",
|
||||
|
||||
@ -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<BackendType, APIServerConfig>({
|
||||
(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<BackendType, APIServerConfig>({
|
||||
|
||||
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);
|
||||
|
||||
@ -17,6 +17,6 @@ export type BackendType = {
|
||||
injectWebSocket?: (server: ReturnType<typeof serve>) => void;
|
||||
|
||||
init: (ctx: BackendContext<APIServerConfig>) => void;
|
||||
run: (hostname: string, port: number) => void;
|
||||
run: (config: APIServerConfig) => void;
|
||||
end: () => void;
|
||||
};
|
||||
|
||||
@ -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: '',
|
||||
};
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { dialog } from 'electron';
|
||||
import prompt from 'custom-electron-prompt';
|
||||
|
||||
import { t } from '@/i18n';
|
||||
@ -93,5 +94,51 @@ export const onMenu = async ({
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: t('plugins.api-server.menu.https.label'),
|
||||
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] });
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user