mirror of
https://github.com/Pandipipas/scoreko-electron-dev.git
synced 2026-06-05 21:22:07 +00:00
refactor(cleanup): completar fase 5 con renombrados semánticos
This commit is contained in:
@@ -4,8 +4,8 @@ export type AppRuntimeConfig = {
|
|||||||
iconPathOverride?: string;
|
iconPathOverride?: string;
|
||||||
nodecgPort: string;
|
nodecgPort: string;
|
||||||
bundleName: string;
|
bundleName: string;
|
||||||
dashboardRoute: string;
|
mainDashboardRoute: string;
|
||||||
loadingRoute: string;
|
loadingDashboardRoute: string;
|
||||||
loadDelayMs: number;
|
loadDelayMs: number;
|
||||||
startupTimeoutMs: number;
|
startupTimeoutMs: number;
|
||||||
nodecgKillTimeoutMs: number;
|
nodecgKillTimeoutMs: number;
|
||||||
@@ -21,8 +21,8 @@ export function getRuntimeConfig(): AppRuntimeConfig {
|
|||||||
iconPathOverride: getOptionalEnv("SCOREKO_APP_ICON_PATH"),
|
iconPathOverride: getOptionalEnv("SCOREKO_APP_ICON_PATH"),
|
||||||
nodecgPort: parseEnvPort("NODECG_PORT", "9090"),
|
nodecgPort: parseEnvPort("NODECG_PORT", "9090"),
|
||||||
bundleName: getEnv("NODECG_BUNDLE_NAME", "scoreko-dev"),
|
bundleName: getEnv("NODECG_BUNDLE_NAME", "scoreko-dev"),
|
||||||
dashboardRoute: getEnv("SCOREKO_DASHBOARD_ROUTE", "dashboard/scoreko-dev/main.html?standalone=true"),
|
mainDashboardRoute: getEnv("SCOREKO_DASHBOARD_ROUTE", "dashboard/scoreko-dev/main.html?standalone=true"),
|
||||||
loadingRoute: getEnv("SCOREKO_LOADING_ROUTE", "dashboard/loading/main.html?standalone=true"),
|
loadingDashboardRoute: getEnv("SCOREKO_LOADING_ROUTE", "dashboard/loading/main.html?standalone=true"),
|
||||||
loadDelayMs: parseEnvIntInRange("ELECTRON_LOAD_DELAY_MS", 10000, 0, 600000),
|
loadDelayMs: parseEnvIntInRange("ELECTRON_LOAD_DELAY_MS", 10000, 0, 600000),
|
||||||
startupTimeoutMs: parseEnvIntInRange("NODECG_STARTUP_TIMEOUT_MS", 30000, 1000, 600000),
|
startupTimeoutMs: parseEnvIntInRange("NODECG_STARTUP_TIMEOUT_MS", 30000, 1000, 600000),
|
||||||
nodecgKillTimeoutMs: parseEnvIntInRange("NODECG_KILL_TIMEOUT_MS", 2500, 0, 120000),
|
nodecgKillTimeoutMs: parseEnvIntInRange("NODECG_KILL_TIMEOUT_MS", 2500, 0, 120000),
|
||||||
|
|||||||
+23
-23
@@ -7,20 +7,20 @@ import { createNodecgProcessManager } from "./nodecg/process-manager";
|
|||||||
import { getRemainingDelayMs } from "./utils/timing";
|
import { getRemainingDelayMs } from "./utils/timing";
|
||||||
import { createLoadingWindow, createMainWindow } from "./windows/window-factory";
|
import { createLoadingWindow, createMainWindow } from "./windows/window-factory";
|
||||||
|
|
||||||
const runtimeConfig = getRuntimeConfig();
|
const appConfig = getRuntimeConfig();
|
||||||
|
|
||||||
const isDev = !app.isPackaged;
|
const isDev = !app.isPackaged;
|
||||||
const rootPath = isDev ? path.resolve(__dirname, "../..") : process.resourcesPath;
|
const rootPath = isDev ? path.resolve(__dirname, "../..") : process.resourcesPath;
|
||||||
const nodecgPath = path.resolve(rootPath, "lib", "nodecg");
|
const nodecgRootPath = path.resolve(rootPath, "lib", "nodecg");
|
||||||
const dashboardUrl = `http://localhost:${runtimeConfig.nodecgPort}/bundles/${runtimeConfig.bundleName}/${runtimeConfig.dashboardRoute}`;
|
const mainDashboardUrl = `http://localhost:${appConfig.nodecgPort}/bundles/${appConfig.bundleName}/${appConfig.mainDashboardRoute}`;
|
||||||
const loadingUrl = `http://localhost:${runtimeConfig.nodecgPort}/bundles/${runtimeConfig.bundleName}/${runtimeConfig.loadingRoute}`;
|
const loadingDashboardUrl = `http://localhost:${appConfig.nodecgPort}/bundles/${appConfig.bundleName}/${appConfig.loadingDashboardRoute}`;
|
||||||
const baseUrl = `http://127.0.0.1:${runtimeConfig.nodecgPort}`;
|
const nodecgBaseUrl = `http://127.0.0.1:${appConfig.nodecgPort}`;
|
||||||
|
|
||||||
const nodecgManager = createNodecgProcessManager({
|
const nodecgManager = createNodecgProcessManager({
|
||||||
isDev,
|
isDev,
|
||||||
nodecgPath,
|
nodecgRootPath,
|
||||||
baseUrl,
|
nodecgBaseUrl,
|
||||||
runtimeConfig,
|
appConfig,
|
||||||
log,
|
log,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -30,19 +30,19 @@ let mainWindow: BrowserWindow | null = null;
|
|||||||
let loadingWindow: BrowserWindow | null = null;
|
let loadingWindow: BrowserWindow | null = null;
|
||||||
let shutdownState: AppShutdownState = "running";
|
let shutdownState: AppShutdownState = "running";
|
||||||
|
|
||||||
async function launch(): Promise<void> {
|
async function launchApplication(): Promise<void> {
|
||||||
mainWindow = createMainWindow({ runtimeConfig, rootPath, dashboardUrl });
|
mainWindow = createMainWindow({ appConfig, rootPath, mainDashboardUrl });
|
||||||
loadingWindow = createLoadingWindow({ runtimeConfig, rootPath });
|
loadingWindow = createLoadingWindow({ appConfig, rootPath });
|
||||||
|
|
||||||
nodecgManager.startNodeCG();
|
nodecgManager.startNodecgProcess();
|
||||||
|
|
||||||
await nodecgManager.waitForNodeCGReady(Date.now());
|
await nodecgManager.waitForNodecgReady(Date.now());
|
||||||
|
|
||||||
if (!loadingWindow || loadingWindow.isDestroyed()) {
|
if (!loadingWindow || loadingWindow.isDestroyed()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await loadingWindow.loadURL(loadingUrl);
|
await loadingWindow.loadURL(loadingDashboardUrl);
|
||||||
loadingWindow.show();
|
loadingWindow.show();
|
||||||
|
|
||||||
const loadingShownAt = Date.now();
|
const loadingShownAt = Date.now();
|
||||||
@@ -51,9 +51,9 @@ async function launch(): Promise<void> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await mainWindow.loadURL(dashboardUrl);
|
await mainWindow.loadURL(mainDashboardUrl);
|
||||||
|
|
||||||
const remainingLoadingDelay = getRemainingDelayMs(runtimeConfig.loadDelayMs, loadingShownAt);
|
const remainingLoadingDelay = getRemainingDelayMs(appConfig.loadDelayMs, loadingShownAt);
|
||||||
if (remainingLoadingDelay > 0) {
|
if (remainingLoadingDelay > 0) {
|
||||||
await sleep(remainingLoadingDelay);
|
await sleep(remainingLoadingDelay);
|
||||||
}
|
}
|
||||||
@@ -83,24 +83,24 @@ function stopNodecgGracefully(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (shutdownState === "stopping") {
|
if (shutdownState === "stopping") {
|
||||||
return nodecgManager.stopNodeCG();
|
return nodecgManager.stopNodecgProcessGracefully();
|
||||||
}
|
}
|
||||||
|
|
||||||
shutdownState = "stopping";
|
shutdownState = "stopping";
|
||||||
|
|
||||||
return nodecgManager.stopNodeCG().finally(() => {
|
return nodecgManager.stopNodecgProcessGracefully().finally(() => {
|
||||||
shutdownState = "stopped";
|
shutdownState = "stopped";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
app.on("ready", () => {
|
app.on("ready", () => {
|
||||||
app.setName(runtimeConfig.title);
|
app.setName(appConfig.title);
|
||||||
|
|
||||||
if (process.platform === "win32") {
|
if (process.platform === "win32") {
|
||||||
app.setAppUserModelId(runtimeConfig.userModelId);
|
app.setAppUserModelId(appConfig.userModelId);
|
||||||
}
|
}
|
||||||
|
|
||||||
launch().catch((error: unknown) => {
|
launchApplication().catch((error: unknown) => {
|
||||||
showFatalError("No se pudo iniciar Scoreko.", error);
|
showFatalError("No se pudo iniciar Scoreko.", error);
|
||||||
closeLoadingWindow();
|
closeLoadingWindow();
|
||||||
app.exit(1);
|
app.exit(1);
|
||||||
@@ -109,8 +109,8 @@ app.on("ready", () => {
|
|||||||
|
|
||||||
app.on("activate", async () => {
|
app.on("activate", async () => {
|
||||||
if (BrowserWindow.getAllWindows().length === 0) {
|
if (BrowserWindow.getAllWindows().length === 0) {
|
||||||
mainWindow = createMainWindow({ runtimeConfig, rootPath, dashboardUrl });
|
mainWindow = createMainWindow({ appConfig, rootPath, mainDashboardUrl });
|
||||||
await mainWindow.loadURL(dashboardUrl);
|
await mainWindow.loadURL(mainDashboardUrl);
|
||||||
mainWindow.show();
|
mainWindow.show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import { NODE_RUNTIME_NAME } from "../constants";
|
|||||||
|
|
||||||
type NodecgProcessManagerConfig = {
|
type NodecgProcessManagerConfig = {
|
||||||
isDev: boolean;
|
isDev: boolean;
|
||||||
nodecgPath: string;
|
nodecgRootPath: string;
|
||||||
baseUrl: string;
|
nodecgBaseUrl: string;
|
||||||
runtimeConfig: AppRuntimeConfig;
|
appConfig: AppRuntimeConfig;
|
||||||
log: (...args: unknown[]) => void;
|
log: (...args: unknown[]) => void;
|
||||||
deps?: Partial<NodecgProcessManagerDeps>;
|
deps?: Partial<NodecgProcessManagerDeps>;
|
||||||
};
|
};
|
||||||
@@ -28,35 +28,35 @@ type NodecgProcessManagerDeps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type NodecgProcessManager = {
|
export type NodecgProcessManager = {
|
||||||
startNodeCG: () => ChildProcess;
|
startNodecgProcess: () => ChildProcess;
|
||||||
waitForNodeCGReady: (startTime: number) => Promise<void>;
|
waitForNodecgReady: (startTime: number) => Promise<void>;
|
||||||
stopNodeCG: () => Promise<void>;
|
stopNodecgProcessGracefully: () => Promise<void>;
|
||||||
getProcess: () => ChildProcess | null;
|
getProcess: () => ChildProcess | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function createNodecgProcessManager({
|
export function createNodecgProcessManager({
|
||||||
isDev,
|
isDev,
|
||||||
nodecgPath,
|
nodecgRootPath,
|
||||||
baseUrl,
|
nodecgBaseUrl,
|
||||||
runtimeConfig,
|
appConfig,
|
||||||
log,
|
log,
|
||||||
deps,
|
deps,
|
||||||
}: NodecgProcessManagerConfig): NodecgProcessManager {
|
}: NodecgProcessManagerConfig): NodecgProcessManager {
|
||||||
const resolvedDeps = resolveDeps(deps);
|
const resolvedDeps = resolveDeps(deps);
|
||||||
|
|
||||||
let nodecgProcess: ChildProcess | null = null;
|
let nodecgProcess: ChildProcess | null = null;
|
||||||
let stopNodeCGPromise: Promise<void> | null = null;
|
let stopNodecgPromise: Promise<void> | null = null;
|
||||||
|
|
||||||
const startNodeCG = (): ChildProcess => {
|
const startNodecgProcess = (): ChildProcess => {
|
||||||
validateNodeCGInstall(nodecgPath, runtimeConfig.bundleName, resolvedDeps.pathExists);
|
validateNodecgInstall(nodecgRootPath, appConfig.bundleName, resolvedDeps.pathExists);
|
||||||
|
|
||||||
const indexPath = path.join(nodecgPath, "index.js");
|
const indexPath = path.join(nodecgRootPath, "index.js");
|
||||||
const child = resolvedDeps.spawnProcess(resolvedDeps.execPath, [indexPath], {
|
const child = resolvedDeps.spawnProcess(resolvedDeps.execPath, [indexPath], {
|
||||||
cwd: nodecgPath,
|
cwd: nodecgRootPath,
|
||||||
env: {
|
env: {
|
||||||
...resolvedDeps.env,
|
...resolvedDeps.env,
|
||||||
NODE_ENV: isDev ? "development" : "production",
|
NODE_ENV: isDev ? "development" : "production",
|
||||||
NODECG_PORT: runtimeConfig.nodecgPort,
|
NODECG_PORT: appConfig.nodecgPort,
|
||||||
ELECTRON_RUN_AS_NODE: "1",
|
ELECTRON_RUN_AS_NODE: "1",
|
||||||
},
|
},
|
||||||
stdio: ["ignore", "pipe", "pipe"],
|
stdio: ["ignore", "pipe", "pipe"],
|
||||||
@@ -83,14 +83,14 @@ export function createNodecgProcessManager({
|
|||||||
return child;
|
return child;
|
||||||
};
|
};
|
||||||
|
|
||||||
const waitForNodeCGReady = async (startTime: number): Promise<void> => {
|
const waitForNodecgReady = async (startTime: number): Promise<void> => {
|
||||||
while (Date.now() - startTime < runtimeConfig.startupTimeoutMs) {
|
while (Date.now() - startTime < appConfig.startupTimeoutMs) {
|
||||||
if (!nodecgProcess) {
|
if (!nodecgProcess) {
|
||||||
throw new Error("NodeCG terminó antes de estar listo.");
|
throw new Error("NodeCG terminó antes de estar listo.");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await resolvedDeps.fetchUrl(baseUrl, { method: "GET" });
|
const response = await resolvedDeps.fetchUrl(nodecgBaseUrl, { method: "GET" });
|
||||||
if (response.ok || response.status === 404) {
|
if (response.ok || response.status === 404) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -101,12 +101,12 @@ export function createNodecgProcessManager({
|
|||||||
await sleep(500, resolvedDeps.setTimer);
|
await sleep(500, resolvedDeps.setTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(`Timeout esperando NodeCG en ${baseUrl} (${runtimeConfig.startupTimeoutMs}ms).`);
|
throw new Error(`Timeout esperando NodeCG en ${nodecgBaseUrl} (${appConfig.startupTimeoutMs}ms).`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const stopNodeCG = (): Promise<void> => {
|
const stopNodecgProcessGracefully = (): Promise<void> => {
|
||||||
if (stopNodeCGPromise) {
|
if (stopNodecgPromise) {
|
||||||
return stopNodeCGPromise;
|
return stopNodecgPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nodecgProcess || nodecgProcess.killed) {
|
if (!nodecgProcess || nodecgProcess.killed) {
|
||||||
@@ -122,14 +122,15 @@ export function createNodecgProcessManager({
|
|||||||
}
|
}
|
||||||
|
|
||||||
log(`Stopping NodeCG pid=${pid}`);
|
log(`Stopping NodeCG pid=${pid}`);
|
||||||
killNodeCGProcessTree(pid, "SIGTERM", log, resolvedDeps);
|
killNodecgProcessTree(pid, "SIGTERM", log, resolvedDeps);
|
||||||
|
|
||||||
stopNodeCGPromise = new Promise((resolve) => {
|
stopNodecgPromise = new Promise((resolve) => {
|
||||||
const complete = () => {
|
const complete = () => {
|
||||||
if (nodecgProcess === processToStop) {
|
if (nodecgProcess === processToStop) {
|
||||||
nodecgProcess = null;
|
nodecgProcess = null;
|
||||||
}
|
}
|
||||||
stopNodeCGPromise = null;
|
|
||||||
|
stopNodecgPromise = null;
|
||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -140,18 +141,18 @@ export function createNodecgProcessManager({
|
|||||||
resolvedDeps.setTimer(() => {
|
resolvedDeps.setTimer(() => {
|
||||||
if (processToStop.exitCode === null && processToStop.signalCode === null) {
|
if (processToStop.exitCode === null && processToStop.signalCode === null) {
|
||||||
log(`NodeCG did not exit after SIGTERM, forcing SIGKILL pid=${pid}`);
|
log(`NodeCG did not exit after SIGTERM, forcing SIGKILL pid=${pid}`);
|
||||||
killNodeCGProcessTree(pid, "SIGKILL", log, resolvedDeps);
|
killNodecgProcessTree(pid, "SIGKILL", log, resolvedDeps);
|
||||||
}
|
}
|
||||||
}, Math.max(0, runtimeConfig.nodecgKillTimeoutMs));
|
}, Math.max(0, appConfig.nodecgKillTimeoutMs));
|
||||||
});
|
});
|
||||||
|
|
||||||
return stopNodeCGPromise;
|
return stopNodecgPromise;
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
startNodeCG,
|
startNodecgProcess,
|
||||||
waitForNodeCGReady,
|
waitForNodecgReady,
|
||||||
stopNodeCG,
|
stopNodecgProcessGracefully,
|
||||||
getProcess: () => nodecgProcess,
|
getProcess: () => nodecgProcess,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -171,13 +172,13 @@ function resolveDeps(deps?: Partial<NodecgProcessManagerDeps>): NodecgProcessMan
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateNodeCGInstall(nodecgPath: string, bundleName: string, pathExists: (candidatePath: string) => boolean): void {
|
function validateNodecgInstall(nodecgRootPath: string, bundleName: string, pathExists: (candidatePath: string) => boolean): void {
|
||||||
const indexPath = path.join(nodecgPath, "index.js");
|
const indexPath = path.join(nodecgRootPath, "index.js");
|
||||||
const nodecgBootstrapPath = path.join(nodecgPath, "node_modules", "nodecg", "dist", "server", "bootstrap.js");
|
const nodecgBootstrapPath = path.join(nodecgRootPath, "node_modules", "nodecg", "dist", "server", "bootstrap.js");
|
||||||
const bundlePath = path.join(nodecgPath, "bundles", bundleName);
|
const bundlePath = path.join(nodecgRootPath, "bundles", bundleName);
|
||||||
|
|
||||||
if (!pathExists(nodecgPath)) {
|
if (!pathExists(nodecgRootPath)) {
|
||||||
throw new Error(`No existe la carpeta NodeCG: ${nodecgPath}`);
|
throw new Error(`No existe la carpeta NodeCG: ${nodecgRootPath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pathExists(indexPath)) {
|
if (!pathExists(indexPath)) {
|
||||||
@@ -206,7 +207,7 @@ function validateNodeCGInstall(nodecgPath: string, bundleName: string, pathExist
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function killNodeCGProcessTree(
|
function killNodecgProcessTree(
|
||||||
pid: number,
|
pid: number,
|
||||||
signal: NodeJS.Signals,
|
signal: NodeJS.Signals,
|
||||||
log: (...args: unknown[]) => void,
|
log: (...args: unknown[]) => void,
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import path from "node:path";
|
|||||||
import { AppRuntimeConfig } from "../config/runtime-config";
|
import { AppRuntimeConfig } from "../config/runtime-config";
|
||||||
|
|
||||||
export function resolveAppIconPath(
|
export function resolveAppIconPath(
|
||||||
runtimeConfig: AppRuntimeConfig,
|
appConfig: AppRuntimeConfig,
|
||||||
rootPath: string,
|
rootPath: string,
|
||||||
pathExists: (candidatePath: string) => boolean = fs.existsSync,
|
pathExists: (candidatePath: string) => boolean = fs.existsSync,
|
||||||
): string | undefined {
|
): string | undefined {
|
||||||
const iconCandidates = [
|
const iconCandidates = [
|
||||||
runtimeConfig.iconPathOverride,
|
appConfig.iconPathOverride,
|
||||||
path.join(rootPath, "static", "icons", "icon.ico"),
|
path.join(rootPath, "static", "icons", "icon.ico"),
|
||||||
path.join(rootPath, "static", "icons", "icon.png"),
|
path.join(rootPath, "static", "icons", "icon.png"),
|
||||||
path.join(rootPath, "static", "icon.ico"),
|
path.join(rootPath, "static", "icon.ico"),
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ import { resolveAppIconPath } from "./icon-path";
|
|||||||
import { shouldAllowInternalNavigation, shouldOpenExternalNavigation } from "./navigation-security";
|
import { shouldAllowInternalNavigation, shouldOpenExternalNavigation } from "./navigation-security";
|
||||||
|
|
||||||
type WindowFactoryDependencies = {
|
type WindowFactoryDependencies = {
|
||||||
runtimeConfig: AppRuntimeConfig;
|
appConfig: AppRuntimeConfig;
|
||||||
rootPath: string;
|
rootPath: string;
|
||||||
dashboardUrl: string;
|
mainDashboardUrl: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function createMainWindow({ runtimeConfig, rootPath, dashboardUrl }: WindowFactoryDependencies): BrowserWindow {
|
export function createMainWindow({ appConfig, rootPath, mainDashboardUrl }: WindowFactoryDependencies): BrowserWindow {
|
||||||
const windowOptions = createWindowOptions({ runtimeConfig, rootPath, isLoadingWindow: false });
|
const windowOptions = createWindowOptions({ appConfig, rootPath, isLoadingWindow: false });
|
||||||
const window = new BrowserWindow(windowOptions);
|
const window = new BrowserWindow(windowOptions);
|
||||||
|
|
||||||
window.setMenuBarVisibility(false);
|
window.setMenuBarVisibility(false);
|
||||||
@@ -25,7 +25,7 @@ export function createMainWindow({ runtimeConfig, rootPath, dashboardUrl }: Wind
|
|||||||
});
|
});
|
||||||
|
|
||||||
window.webContents.on("will-navigate", (event, url) => {
|
window.webContents.on("will-navigate", (event, url) => {
|
||||||
if (shouldAllowInternalNavigation(url, dashboardUrl)) {
|
if (shouldAllowInternalNavigation(url, mainDashboardUrl)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,8 +43,8 @@ export function createMainWindow({ runtimeConfig, rootPath, dashboardUrl }: Wind
|
|||||||
return window;
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createLoadingWindow({ runtimeConfig, rootPath }: Omit<WindowFactoryDependencies, "dashboardUrl">): BrowserWindow {
|
export function createLoadingWindow({ appConfig, rootPath }: Omit<WindowFactoryDependencies, "mainDashboardUrl">): BrowserWindow {
|
||||||
const window = new BrowserWindow(createWindowOptions({ runtimeConfig, rootPath, isLoadingWindow: true }));
|
const window = new BrowserWindow(createWindowOptions({ appConfig, rootPath, isLoadingWindow: true }));
|
||||||
|
|
||||||
window.on("page-title-updated", (event) => {
|
window.on("page-title-updated", (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -54,19 +54,19 @@ export function createLoadingWindow({ runtimeConfig, rootPath }: Omit<WindowFact
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createWindowOptions({
|
function createWindowOptions({
|
||||||
runtimeConfig,
|
appConfig,
|
||||||
rootPath,
|
rootPath,
|
||||||
isLoadingWindow,
|
isLoadingWindow,
|
||||||
}: {
|
}: {
|
||||||
runtimeConfig: AppRuntimeConfig;
|
appConfig: AppRuntimeConfig;
|
||||||
rootPath: string;
|
rootPath: string;
|
||||||
isLoadingWindow: boolean;
|
isLoadingWindow: boolean;
|
||||||
}): BrowserWindowConstructorOptions {
|
}): BrowserWindowConstructorOptions {
|
||||||
const iconPath = resolveAppIconPath(runtimeConfig, rootPath);
|
const iconPath = resolveAppIconPath(appConfig, rootPath);
|
||||||
|
|
||||||
const baseOptions: BrowserWindowConstructorOptions = {
|
const baseOptions: BrowserWindowConstructorOptions = {
|
||||||
show: false,
|
show: false,
|
||||||
title: runtimeConfig.title,
|
title: appConfig.title,
|
||||||
...(iconPath ? { icon: iconPath } : {}),
|
...(iconPath ? { icon: iconPath } : {}),
|
||||||
backgroundColor: DEFAULT_WINDOW_BACKGROUND,
|
backgroundColor: DEFAULT_WINDOW_BACKGROUND,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ function getBaseConfig(): AppRuntimeConfig {
|
|||||||
userModelId: "com.scoreko.desktop",
|
userModelId: "com.scoreko.desktop",
|
||||||
nodecgPort: "9090",
|
nodecgPort: "9090",
|
||||||
bundleName: "scoreko-dev",
|
bundleName: "scoreko-dev",
|
||||||
dashboardRoute: "dashboard/scoreko-dev/main.html?standalone=true",
|
mainDashboardRoute: "dashboard/scoreko-dev/main.html?standalone=true",
|
||||||
loadingRoute: "dashboard/loading/main.html?standalone=true",
|
loadingDashboardRoute: "dashboard/loading/main.html?standalone=true",
|
||||||
loadDelayMs: 10000,
|
loadDelayMs: 10000,
|
||||||
startupTimeoutMs: 30000,
|
startupTimeoutMs: 30000,
|
||||||
nodecgKillTimeoutMs: 2500,
|
nodecgKillTimeoutMs: 2500,
|
||||||
@@ -19,28 +19,28 @@ function getBaseConfig(): AppRuntimeConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("resolveAppIconPath prioriza iconPathOverride cuando existe", () => {
|
test("resolveAppIconPath prioriza iconPathOverride cuando existe", () => {
|
||||||
const runtimeConfig: AppRuntimeConfig = {
|
const appConfig: AppRuntimeConfig = {
|
||||||
...getBaseConfig(),
|
...getBaseConfig(),
|
||||||
iconPathOverride: "/custom/icon.ico",
|
iconPathOverride: "/custom/icon.ico",
|
||||||
};
|
};
|
||||||
|
|
||||||
const iconPath = resolveAppIconPath(runtimeConfig, "/app", (candidate) => candidate === "/custom/icon.ico");
|
const iconPath = resolveAppIconPath(appConfig, "/app", (candidate) => candidate === "/custom/icon.ico");
|
||||||
|
|
||||||
assert.equal(iconPath, "/custom/icon.ico");
|
assert.equal(iconPath, "/custom/icon.ico");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("resolveAppIconPath cae al primer icono por defecto existente", () => {
|
test("resolveAppIconPath cae al primer icono por defecto existente", () => {
|
||||||
const runtimeConfig = getBaseConfig();
|
const appConfig = getBaseConfig();
|
||||||
|
|
||||||
const iconPath = resolveAppIconPath(runtimeConfig, "/app", (candidate) => candidate === "/app/static/icons/icon.png");
|
const iconPath = resolveAppIconPath(appConfig, "/app", (candidate) => candidate === "/app/static/icons/icon.png");
|
||||||
|
|
||||||
assert.equal(iconPath, "/app/static/icons/icon.png");
|
assert.equal(iconPath, "/app/static/icons/icon.png");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("resolveAppIconPath devuelve undefined cuando no hay iconos", () => {
|
test("resolveAppIconPath devuelve undefined cuando no hay iconos", () => {
|
||||||
const runtimeConfig = getBaseConfig();
|
const appConfig = getBaseConfig();
|
||||||
|
|
||||||
const iconPath = resolveAppIconPath(runtimeConfig, "/app", () => false);
|
const iconPath = resolveAppIconPath(appConfig, "/app", () => false);
|
||||||
|
|
||||||
assert.equal(iconPath, undefined);
|
assert.equal(iconPath, undefined);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ function getBaseConfig(): AppRuntimeConfig {
|
|||||||
userModelId: "com.scoreko.desktop",
|
userModelId: "com.scoreko.desktop",
|
||||||
nodecgPort: "9090",
|
nodecgPort: "9090",
|
||||||
bundleName: "scoreko-dev",
|
bundleName: "scoreko-dev",
|
||||||
dashboardRoute: "dashboard/scoreko-dev/main.html?standalone=true",
|
mainDashboardRoute: "dashboard/scoreko-dev/main.html?standalone=true",
|
||||||
loadingRoute: "dashboard/loading/main.html?standalone=true",
|
loadingDashboardRoute: "dashboard/loading/main.html?standalone=true",
|
||||||
loadDelayMs: 10000,
|
loadDelayMs: 10000,
|
||||||
startupTimeoutMs: 100,
|
startupTimeoutMs: 100,
|
||||||
nodecgKillTimeoutMs: 10,
|
nodecgKillTimeoutMs: 10,
|
||||||
@@ -34,9 +34,9 @@ function getBaseConfig(): AppRuntimeConfig {
|
|||||||
test("startNodeCG valida instalación de NodeCG antes de arrancar", () => {
|
test("startNodeCG valida instalación de NodeCG antes de arrancar", () => {
|
||||||
const manager = createNodecgProcessManager({
|
const manager = createNodecgProcessManager({
|
||||||
isDev: true,
|
isDev: true,
|
||||||
nodecgPath: "/fake/nodecg",
|
nodecgRootPath: "/fake/nodecg",
|
||||||
baseUrl: "http://127.0.0.1:9090",
|
nodecgBaseUrl: "http://127.0.0.1:9090",
|
||||||
runtimeConfig: getBaseConfig(),
|
appConfig: getBaseConfig(),
|
||||||
log: () => undefined,
|
log: () => undefined,
|
||||||
deps: {
|
deps: {
|
||||||
pathExists: () => false,
|
pathExists: () => false,
|
||||||
@@ -47,7 +47,7 @@ test("startNodeCG valida instalación de NodeCG antes de arrancar", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
manager.startNodeCG();
|
manager.startNodecgProcess();
|
||||||
}, /No existe la carpeta NodeCG/);
|
}, /No existe la carpeta NodeCG/);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -55,9 +55,9 @@ test("waitForNodeCGReady resuelve cuando el endpoint responde 404", async () =>
|
|||||||
const child = new MockChildProcess(4321);
|
const child = new MockChildProcess(4321);
|
||||||
const manager = createNodecgProcessManager({
|
const manager = createNodecgProcessManager({
|
||||||
isDev: true,
|
isDev: true,
|
||||||
nodecgPath: "/fake/nodecg",
|
nodecgRootPath: "/fake/nodecg",
|
||||||
baseUrl: "http://127.0.0.1:9090",
|
nodecgBaseUrl: "http://127.0.0.1:9090",
|
||||||
runtimeConfig: getBaseConfig(),
|
appConfig: getBaseConfig(),
|
||||||
log: () => undefined,
|
log: () => undefined,
|
||||||
deps: {
|
deps: {
|
||||||
pathExists: () => true,
|
pathExists: () => true,
|
||||||
@@ -72,9 +72,9 @@ test("waitForNodeCGReady resuelve cuando el endpoint responde 404", async () =>
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
manager.startNodeCG();
|
manager.startNodecgProcess();
|
||||||
await assert.doesNotReject(async () => {
|
await assert.doesNotReject(async () => {
|
||||||
await manager.waitForNodeCGReady(Date.now());
|
await manager.waitForNodecgReady(Date.now());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -85,9 +85,9 @@ test("stopNodeCG envía SIGTERM y luego SIGKILL si el proceso no sale", async ()
|
|||||||
|
|
||||||
const manager = createNodecgProcessManager({
|
const manager = createNodecgProcessManager({
|
||||||
isDev: true,
|
isDev: true,
|
||||||
nodecgPath: "/fake/nodecg",
|
nodecgRootPath: "/fake/nodecg",
|
||||||
baseUrl: "http://127.0.0.1:9090",
|
nodecgBaseUrl: "http://127.0.0.1:9090",
|
||||||
runtimeConfig: getBaseConfig(),
|
appConfig: getBaseConfig(),
|
||||||
log: () => undefined,
|
log: () => undefined,
|
||||||
deps: {
|
deps: {
|
||||||
pathExists: () => true,
|
pathExists: () => true,
|
||||||
@@ -105,8 +105,8 @@ test("stopNodeCG envía SIGTERM y luego SIGKILL si el proceso no sale", async ()
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
manager.startNodeCG();
|
manager.startNodecgProcess();
|
||||||
const stopPromise = manager.stopNodeCG();
|
const stopPromise = manager.stopNodecgProcessGracefully();
|
||||||
|
|
||||||
assert.deepEqual(killSignals, [{ pid: -9999, signal: "SIGTERM" }]);
|
assert.deepEqual(killSignals, [{ pid: -9999, signal: "SIGTERM" }]);
|
||||||
|
|
||||||
@@ -129,9 +129,9 @@ test("stopNodeCG reutiliza la misma promesa cuando se invoca en paralelo", async
|
|||||||
|
|
||||||
const manager = createNodecgProcessManager({
|
const manager = createNodecgProcessManager({
|
||||||
isDev: true,
|
isDev: true,
|
||||||
nodecgPath: "/fake/nodecg",
|
nodecgRootPath: "/fake/nodecg",
|
||||||
baseUrl: "http://127.0.0.1:9090",
|
nodecgBaseUrl: "http://127.0.0.1:9090",
|
||||||
runtimeConfig: getBaseConfig(),
|
appConfig: getBaseConfig(),
|
||||||
log: () => undefined,
|
log: () => undefined,
|
||||||
deps: {
|
deps: {
|
||||||
pathExists: () => true,
|
pathExists: () => true,
|
||||||
@@ -144,9 +144,9 @@ test("stopNodeCG reutiliza la misma promesa cuando se invoca en paralelo", async
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
manager.startNodeCG();
|
manager.startNodecgProcess();
|
||||||
const firstStop = manager.stopNodeCG();
|
const firstStop = manager.stopNodecgProcessGracefully();
|
||||||
const secondStop = manager.stopNodeCG();
|
const secondStop = manager.stopNodecgProcessGracefully();
|
||||||
|
|
||||||
assert.equal(firstStop, secondStop);
|
assert.equal(firstStop, secondStop);
|
||||||
|
|
||||||
@@ -160,9 +160,9 @@ test("stopNodeCG normaliza timeout negativo a cero", async () => {
|
|||||||
|
|
||||||
const manager = createNodecgProcessManager({
|
const manager = createNodecgProcessManager({
|
||||||
isDev: true,
|
isDev: true,
|
||||||
nodecgPath: "/fake/nodecg",
|
nodecgRootPath: "/fake/nodecg",
|
||||||
baseUrl: "http://127.0.0.1:9090",
|
nodecgBaseUrl: "http://127.0.0.1:9090",
|
||||||
runtimeConfig: {
|
appConfig: {
|
||||||
...getBaseConfig(),
|
...getBaseConfig(),
|
||||||
nodecgKillTimeoutMs: -10,
|
nodecgKillTimeoutMs: -10,
|
||||||
},
|
},
|
||||||
@@ -182,8 +182,8 @@ test("stopNodeCG normaliza timeout negativo a cero", async () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
manager.startNodeCG();
|
manager.startNodecgProcess();
|
||||||
const stopPromise = manager.stopNodeCG();
|
const stopPromise = manager.stopNodecgProcessGracefully();
|
||||||
|
|
||||||
assert.ok(timeouts.includes(0));
|
assert.ok(timeouts.includes(0));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user