From 755d84be56a8fbecdf7eba7217f0d331b2bbe46a Mon Sep 17 00:00:00 2001 From: sabonerune <102559104+sabonerune@users.noreply.github.com> Date: Tue, 8 Oct 2024 20:38:04 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20=E3=83=9D=E3=83=BC=E3=83=88?= =?UTF-8?q?=E5=A4=89=E6=9B=B4=E6=99=82=E3=81=AB`EngineInfo`=E3=81=AE?= =?UTF-8?q?=E6=83=85=E5=A0=B1=E3=82=92=E6=9B=B8=E3=81=8D=E6=8F=9B=E3=81=88?= =?UTF-8?q?=E3=81=AA=E3=81=84=E3=82=88=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=80=82=20(#2282)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: ポート変更時に`EngineInfo`の情報を書き換えないようにする。 * fix: URL組み立て方法の変更とコメント追加 * fix: コメント追加 Co-authored-by: Hiroshiba * fix: コメントさらに追記 --------- Co-authored-by: Hiroshiba --- src/backend/browser/contract.ts | 19 +++++--- .../electron/engineAndVvppController.ts | 6 ++- .../electron/manager/RuntimeInfoManager.ts | 19 ++++++-- .../electron/manager/engineInfoManager.ts | 26 ++++------- .../electron/manager/engineProcessManager.ts | 10 ++-- src/backend/electron/portHelper.ts | 8 ---- src/components/Menu/MenuBar/MenuBar.vue | 4 +- src/components/Talk/TalkEditor.vue | 3 +- src/store/proxy.ts | 10 +++- src/store/type.ts | 2 +- src/type/preload.ts | 5 +- .../unit/backend/electron/RuntimeInfo.spec.ts | 46 ++++++++++++++----- 12 files changed, 103 insertions(+), 55 deletions(-) diff --git a/src/backend/browser/contract.ts b/src/backend/browser/contract.ts index 089b07280c..2048441dd1 100644 --- a/src/backend/browser/contract.ts +++ b/src/backend/browser/contract.ts @@ -1,12 +1,19 @@ -import { EngineInfo, envEngineInfoSchema } from "@/type/preload"; +import { type EngineInfo, envEngineInfoSchema } from "@/type/preload"; const baseEngineInfo = envEngineInfoSchema .array() .parse(JSON.parse(import.meta.env.VITE_DEFAULT_ENGINE_INFOS))[0]; -export const defaultEngine: EngineInfo = { - ...baseEngineInfo, - type: "path", // FIXME: ダミーで"path"にしているので、エンジンAPIのURLを設定できるようにし、type: "URL"にする - isDefault: true, -}; +export const defaultEngine: EngineInfo = (() => { + const { protocol, hostname, port, pathname } = new URL(baseEngineInfo.host); + return { + ...baseEngineInfo, + protocol, + hostname, + defaultPort: port, + pathname: pathname === "/" ? "" : pathname, + type: "path", // FIXME: ダミーで"path"にしているので、エンジンAPIのURLを設定できるようにし、type: "URL"にする + isDefault: true, + }; +})(); export const directoryHandleStoreKey = "directoryHandle"; diff --git a/src/backend/electron/engineAndVvppController.ts b/src/backend/electron/engineAndVvppController.ts index 8b6d1108b3..e11e280e8d 100644 --- a/src/backend/electron/engineAndVvppController.ts +++ b/src/backend/electron/engineAndVvppController.ts @@ -147,7 +147,10 @@ export class EngineAndVvppController { this.configManager.set("engineSettings", engineSettings); await this.engineProcessManager.runEngineAll(); - this.runtimeInfoManager.setEngineInfos(engineInfos); + this.runtimeInfoManager.setEngineInfos( + engineInfos, + this.engineInfoManager.altPortInfos, + ); await this.runtimeInfoManager.exportFile(); } @@ -162,6 +165,7 @@ export class EngineAndVvppController { // TODO: setからexportの処理は排他処理にしたほうがより良い this.runtimeInfoManager.setEngineInfos( this.engineInfoManager.fetchEngineInfos(), + this.engineInfoManager.altPortInfos, ); await this.runtimeInfoManager.exportFile(); } diff --git a/src/backend/electron/manager/RuntimeInfoManager.ts b/src/backend/electron/manager/RuntimeInfoManager.ts index 0dc2c0fd2e..e3ec4340ca 100644 --- a/src/backend/electron/manager/RuntimeInfoManager.ts +++ b/src/backend/electron/manager/RuntimeInfoManager.ts @@ -6,6 +6,7 @@ import fs from "fs"; import AsyncLock from "async-lock"; import log from "electron-log/main"; +import type { AltPortInfos } from "@/store/type"; import { EngineId, EngineInfo } from "@/type/preload"; /** @@ -13,7 +14,7 @@ import { EngineId, EngineInfo } from "@/type/preload"; */ export type EngineInfoForRuntimeInfo = Pick< EngineInfo, - "uuid" | "host" | "name" + "uuid" | "protocol" | "hostname" | "defaultPort" | "pathname" | "name" >; /** @@ -58,12 +59,17 @@ export class RuntimeInfoManager { * エンジン情報(書き出し用に記憶) */ private engineInfos: EngineInfoForRuntimeInfo[] = []; + private altportInfos: AltPortInfos = {}; /** * エンジン情報を登録する */ - public setEngineInfos(engineInfos: EngineInfoForRuntimeInfo[]) { + public setEngineInfos( + engineInfos: EngineInfoForRuntimeInfo[], + altportInfos: AltPortInfos, + ) { this.engineInfos = engineInfos; + this.altportInfos = altportInfos; } /** @@ -76,9 +82,16 @@ export class RuntimeInfoManager { formatVersion: this.fileFormatVersion, appVersion: this.appVersion, engineInfos: this.engineInfos.map((engineInfo) => { + const altPort: string | undefined = + this.altportInfos[engineInfo.uuid]; + const port = altPort ?? engineInfo.defaultPort; + // NOTE: URLを正規化する + const url = new URL(`${engineInfo.protocol}//${engineInfo.hostname}`); + url.port = port; return { uuid: engineInfo.uuid, - url: engineInfo.host, // NOTE: 元のEngineInfo.hostにURLが入っている + // NOTE: URLインターフェースは"pathname"が空文字でも"/"を付けるので手動で結合する。 + url: `${url.origin}${engineInfo.pathname}`, name: engineInfo.name, }; }), diff --git a/src/backend/electron/manager/engineInfoManager.ts b/src/backend/electron/manager/engineInfoManager.ts index 51f8f557f9..5e440cbf8b 100644 --- a/src/backend/electron/manager/engineInfoManager.ts +++ b/src/backend/electron/manager/engineInfoManager.ts @@ -29,8 +29,13 @@ function fetchDefaultEngineInfos(defaultEngineDir: string): EngineInfo[] { const engines = envSchema.parse(JSON.parse(defaultEngineInfosEnv)); return engines.map((engineInfo) => { + const { protocol, hostname, port, pathname } = new URL(engineInfo.host); return { ...engineInfo, + protocol, + hostname, + defaultPort: port, + pathname: pathname === "/" ? "" : pathname, isDefault: true, type: "path", executionFilePath: path.resolve(engineInfo.executionFilePath), @@ -85,7 +90,10 @@ export class EngineInfoManager { engines.push({ uuid: manifest.uuid, - host: `http://127.0.0.1:${manifest.port}`, + protocol: "http:", + hostname: "127.0.0.1", + defaultPort: manifest.port.toString(), + pathname: "", name: manifest.name, path: engineDir, executionEnabled: true, @@ -140,15 +148,6 @@ export class EngineInfoManager { ...fetchDefaultEngineInfos(this.defaultEngineDir), ...this.fetchAdditionalEngineInfos(), ]; - // 代替ポートに置き換える - engineInfos.forEach((engineInfo) => { - const altPortInfo = this.altPortInfos[engineInfo.uuid]; - if (altPortInfo) { - const url = new URL(engineInfo.host); - url.port = altPortInfo.to.toString(); - engineInfo.host = url.origin; - } - }); return engineInfos; } @@ -191,12 +190,7 @@ export class EngineInfoManager { * エンジン起動時にポートが競合して代替ポートを使う場合に使用する。 */ updateAltPort(engineId: EngineId, port: number) { - const engineInfo = this.fetchEngineInfo(engineId); - const url = new URL(engineInfo.host); - this.altPortInfos[engineId] = { - from: Number(url.port), - to: port, - }; + this.altPortInfos[engineId] = port.toString(); } /** diff --git a/src/backend/electron/manager/engineProcessManager.ts b/src/backend/electron/manager/engineProcessManager.ts index 2e0294cef3..ba17e392c2 100644 --- a/src/backend/electron/manager/engineProcessManager.ts +++ b/src/backend/electron/manager/engineProcessManager.ts @@ -9,8 +9,8 @@ import { findAltPort, getPidFromPort, getProcessNameFromPid, + type HostInfo, isAssignablePort, - url2HostInfo, } from "../portHelper"; import { EngineInfo, EngineId, EngineSettings } from "@/type/preload"; @@ -86,7 +86,11 @@ export class EngineProcessManager { } // { hostname (localhost), port (50021) } <- url (http://localhost:50021) - const engineHostInfo = url2HostInfo(new URL(engineInfo.host)); + const engineHostInfo: HostInfo = { + protocol: engineInfo.protocol, + hostname: engineInfo.hostname, + port: Number(engineInfo.defaultPort), + }; // ポートが塞がっていれば代替ポートを探す let port = engineHostInfo.port; @@ -152,7 +156,7 @@ export class EngineProcessManager { const enginePath = engineInfo.executionFilePath; const args = engineInfo.executionArgs.concat(useGpu ? ["--use_gpu"] : [], [ "--host", - new URL(engineInfo.host).hostname, + engineHostInfo.hostname, "--port", port.toString(), ]); diff --git a/src/backend/electron/portHelper.ts b/src/backend/electron/portHelper.ts index 9630a9b803..cb68f9c15b 100644 --- a/src/backend/electron/portHelper.ts +++ b/src/backend/electron/portHelper.ts @@ -17,14 +17,6 @@ const portWarn = (port: number, message: string, isNested = false) => { log.warn(`${isNested ? "| " : ""}PORT ${port}: ${message}`); }; -export function url2HostInfo(url: URL): HostInfo { - return { - protocol: url.protocol, - hostname: url.hostname, - port: Number(url.port), - }; -} - /** * "netstat -ano" の stdout から, 指定したポートを LISTENING しているプロセスの id を取得します. * diff --git a/src/components/Menu/MenuBar/MenuBar.vue b/src/components/Menu/MenuBar/MenuBar.vue index 5bd26b70cf..b938b9045d 100644 --- a/src/components/Menu/MenuBar/MenuBar.vue +++ b/src/components/Menu/MenuBar/MenuBar.vue @@ -54,7 +54,7 @@ const currentVersion = ref(""); const audioKeys = computed(() => store.state.audioKeys); // デフォルトエンジンの代替先ポート -const defaultEngineAltPortTo = computed(() => { +const defaultEngineAltPortTo = computed(() => { const altPortInfos = store.state.altPortInfos; // ref: https://github.com/VOICEVOX/voicevox/blob/32940eab36f4f729dd0390dca98f18656240d60d/src/views/EditorHome.vue#L522-L528 @@ -65,7 +65,7 @@ const defaultEngineAltPortTo = computed(() => { // : { from: number, to: number } -> to (代替先ポート) if (defaultEngineInfo.uuid in altPortInfos) { - return altPortInfos[defaultEngineInfo.uuid].to; + return altPortInfos[defaultEngineInfo.uuid]; } else { return undefined; } diff --git a/src/components/Talk/TalkEditor.vue b/src/components/Talk/TalkEditor.vue index b31cf1bf71..2f8bea2fdc 100644 --- a/src/components/Talk/TalkEditor.vue +++ b/src/components/Talk/TalkEditor.vue @@ -554,11 +554,12 @@ watch( // 代替ポートをトースト通知する for (const engineId of store.state.engineIds) { const engineName = store.state.engineInfos[engineId].name; + const defaultPort = store.state.engineInfos[engineId].defaultPort; const altPort = store.state.altPortInfos[engineId]; if (!altPort) return; void store.actions.SHOW_NOTIFY_AND_NOT_SHOW_AGAIN_BUTTON({ - message: `${altPort.from}番ポートが使用中であるため ${engineName} は、${altPort.to}番ポートで起動しました`, + message: `${defaultPort}番ポートが使用中であるため ${engineName} は、${altPort}番ポートで起動しました`, icon: "compare_arrows", tipName: "engineStartedOnAltPort", }); diff --git a/src/store/proxy.ts b/src/store/proxy.ts index 309e760537..6cc82334d2 100644 --- a/src/store/proxy.ts +++ b/src/store/proxy.ts @@ -20,7 +20,15 @@ const proxyStoreCreator = (_engineFactory: IEngineConnectorFactory) => { new Error(`No such engineInfo registered: engineId == ${engineId}`), ); - const instance = _engineFactory.instance(engineInfo.host); + const altPort: string | undefined = state.altPortInfos[engineId]; + const port = altPort ?? engineInfo.defaultPort; + // NOTE: URLを正規化する + const url = new URL(`${engineInfo.protocol}//${engineInfo.hostname}`); + url.port = port; + // NOTE: URLインターフェースは"pathname"が空文字でも"/"を付けるので手動で結合する。 + const instance = _engineFactory.instance( + `${url.origin}${engineInfo.pathname}`, + ); return Promise.resolve({ invoke: (v) => (arg) => // FIXME: anyを使わないようにする diff --git a/src/store/type.ts b/src/store/type.ts index 4aab3297ca..66f0035b7e 100644 --- a/src/store/type.ts +++ b/src/store/type.ts @@ -105,7 +105,7 @@ export type Command = { export type EngineState = "STARTING" | "FAILED_STARTING" | "ERROR" | "READY"; // ポートが塞がれていたときの代替ポート情報 -export type AltPortInfos = Record; +export type AltPortInfos = Record; export type SaveResult = | "SUCCESS" diff --git a/src/type/preload.ts b/src/type/preload.ts index fac2c9b288..a4ab896177 100644 --- a/src/type/preload.ts +++ b/src/type/preload.ts @@ -402,7 +402,10 @@ export type MinimumEngineManifestType = z.infer< export type EngineInfo = { uuid: EngineId; - host: string; // NOTE: 実際はorigin(プロトコルとhostnameとport)が入る + protocol: string; // `http:`など + hostname: string; // `example.com`など + defaultPort: string; // `50021`など。空文字列もありえる。 + pathname: string; // `/engine`など。空文字列もありえる。 name: string; path?: string; // エンジンディレクトリのパス executionEnabled: boolean; diff --git a/tests/unit/backend/electron/RuntimeInfo.spec.ts b/tests/unit/backend/electron/RuntimeInfo.spec.ts index 2c4b9e3263..a0e1416ff2 100644 --- a/tests/unit/backend/electron/RuntimeInfo.spec.ts +++ b/tests/unit/backend/electron/RuntimeInfo.spec.ts @@ -14,18 +14,35 @@ test("想定通りのラインタイム情報が保存されている", async () const runtimeInfoManager = new RuntimeInfoManager(tempFilePath, appVersion); // エンジン情報 - runtimeInfoManager.setEngineInfos([ - { - uuid: EngineId("00000000-0000-0000-0000-000000000001"), - host: "https://example.com/engine1", - name: "engine1", - }, - { - uuid: EngineId("00000000-0000-0000-0000-000000000002"), - host: "https://example.com/engine2", - name: "engine2", - }, - ]); + runtimeInfoManager.setEngineInfos( + [ + { + uuid: EngineId("00000000-0000-0000-0000-000000000001"), + protocol: "https:", + hostname: "example.com", + defaultPort: "", + pathname: "/engine1", + name: "engine1", + }, + { + uuid: EngineId("00000000-0000-0000-0000-000000000002"), + protocol: "https:", + hostname: "example.com", + defaultPort: "", + pathname: "/engine2", + name: "engine2", + }, + { + uuid: EngineId("00000000-0000-0000-0000-000000000003"), + protocol: "http:", + hostname: "127.0.0.1", + defaultPort: "8080", + pathname: "", + name: "engine3", + }, + ], + { [EngineId("00000000-0000-0000-0000-000000000003")]: "8081" }, + ); // ファイル書き出し await runtimeInfoManager.exportFile(); @@ -49,6 +66,11 @@ test("想定通りのラインタイム情報が保存されている", async () "url": "https://example.com/engine2", "uuid": "00000000-0000-0000-0000-000000000002", }, + { + "name": "engine3", + "url": "http://127.0.0.1:8081", + "uuid": "00000000-0000-0000-0000-000000000003", + }, ], "formatVersion": 1, }