mirror of
https://github.com/Pandipipas/scoreko-electron-dev.git
synced 2026-06-06 05:32:06 +00:00
env config
This commit is contained in:
@@ -1,24 +1,40 @@
|
||||
import { app, BrowserWindow } from "electron";
|
||||
import path from "node:path";
|
||||
|
||||
import { getRuntimeConfig } from "../config/runtime-config";
|
||||
import { getRuntimeConfig, loadEnvFile, AppRuntimeConfig } from "../config/runtime-config";
|
||||
import { showFatalError, log } from "../errors/error-presenter";
|
||||
import { createNodecgProcessManager } from "../nodecg/process-manager";
|
||||
import { prepareUserNodecgRuntime } from "../nodecg/runtime-provisioner";
|
||||
import { scheduleUpdateCheck } from "../updates/update-service";
|
||||
import { createLoadingWindow, createMainWindow } from "../windows/window-service";
|
||||
import { createApplicationController } from "./application-controller";
|
||||
import { getApplicationPaths } from "./paths";
|
||||
import { getApplicationPaths, getRootPath } from "./paths";
|
||||
|
||||
export function bootstrap(): void {
|
||||
const appConfig = getRuntimeConfig();
|
||||
const isDev = !app.isPackaged;
|
||||
const compiledMainDir = path.resolve(__dirname, "..");
|
||||
const resourcesPath = process.resourcesPath;
|
||||
const rootPath = getRootPath(isDev, compiledMainDir, resourcesPath);
|
||||
const envFilePath = path.join(rootPath, ".env");
|
||||
|
||||
let appConfig: AppRuntimeConfig;
|
||||
try {
|
||||
loadEnvFile(envFilePath);
|
||||
appConfig = getRuntimeConfig();
|
||||
} catch (error: unknown) {
|
||||
app.on("ready", () => {
|
||||
showFatalError("No se pudo cargar la configuración de la aplicación.", error);
|
||||
app.exit(1);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const paths = getApplicationPaths({
|
||||
appConfig,
|
||||
appDataPath: app.getPath("appData"),
|
||||
compiledMainDir: path.resolve(__dirname, ".."),
|
||||
compiledMainDir,
|
||||
isDev,
|
||||
resourcesPath: process.resourcesPath,
|
||||
resourcesPath,
|
||||
});
|
||||
|
||||
app.setName(appConfig.title);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import fs from "node:fs";
|
||||
|
||||
export type AppRuntimeConfig = {
|
||||
title: string;
|
||||
userModelId: string;
|
||||
@@ -21,29 +23,51 @@ export type AppRuntimeConfig = {
|
||||
const MIN_TCP_PORT = 1;
|
||||
const MAX_TCP_PORT = 65535;
|
||||
|
||||
export function loadEnvFile(envFilePath: string): void {
|
||||
if (!fs.existsSync(envFilePath)) {
|
||||
throw new Error(
|
||||
`Archivo de configuración obligatorio no encontrado: ${envFilePath}\n\nPor favor, crea un archivo .env basado en .env.example en la raíz de la aplicación.`,
|
||||
);
|
||||
}
|
||||
try {
|
||||
process.loadEnvFile(envFilePath);
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Error al leer el archivo de configuración .env: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function getRuntimeConfig(): AppRuntimeConfig {
|
||||
// Centralized defaults keep local development and packaged builds consistent.
|
||||
return {
|
||||
title: getEnv("SCOREKO_APP_TITLE", "Scoreko"),
|
||||
userModelId: getEnv("SCOREKO_APP_USER_MODEL_ID", "com.scoreko.desktop"),
|
||||
userDataDirectoryName: getEnv("SCOREKO_APP_USER_DATA_DIRECTORY", "scoreko"),
|
||||
title: getRequiredEnv("SCOREKO_APP_TITLE"),
|
||||
userModelId: getRequiredEnv("SCOREKO_APP_USER_MODEL_ID"),
|
||||
userDataDirectoryName: getRequiredEnv("SCOREKO_APP_USER_DATA_DIRECTORY"),
|
||||
iconPathOverride: getOptionalEnv("SCOREKO_APP_ICON_PATH"),
|
||||
nodecgPort: parseEnvPort("NODECG_PORT", "9090"),
|
||||
bundleName: getEnv("NODECG_BUNDLE_NAME", "scoreko-dev"),
|
||||
mainDashboardRoute: getEnv("SCOREKO_DASHBOARD_ROUTE", "dashboard/scoreko-dev/main.html?standalone=true"),
|
||||
loadingDashboardRoute: getEnv("SCOREKO_LOADING_ROUTE", "dashboard/loading/main.html?standalone=true"),
|
||||
loadDelayMs: parseEnvIntInRange("ELECTRON_LOAD_DELAY_MS", 10000, 0, 600000),
|
||||
startupTimeoutMs: parseEnvIntInRange("NODECG_STARTUP_TIMEOUT_MS", 120000, 1000, 600000),
|
||||
nodecgKillTimeoutMs: parseEnvIntInRange("NODECG_KILL_TIMEOUT_MS", 2500, 0, 120000),
|
||||
updatesEnabled: parseEnvBool("SCOREKO_UPDATES_ENABLED", true),
|
||||
nodecgPort: parseRequiredEnvPort("NODECG_PORT"),
|
||||
bundleName: getRequiredEnv("NODECG_BUNDLE_NAME"),
|
||||
mainDashboardRoute: getRequiredEnv("SCOREKO_DASHBOARD_ROUTE"),
|
||||
loadingDashboardRoute: getRequiredEnv("SCOREKO_LOADING_ROUTE"),
|
||||
loadDelayMs: parseRequiredEnvIntInRange("ELECTRON_LOAD_DELAY_MS", 0, 600000),
|
||||
startupTimeoutMs: parseRequiredEnvIntInRange("NODECG_STARTUP_TIMEOUT_MS", 1000, 600000),
|
||||
nodecgKillTimeoutMs: parseRequiredEnvIntInRange("NODECG_KILL_TIMEOUT_MS", 0, 120000),
|
||||
updatesEnabled: parseRequiredEnvBool("SCOREKO_UPDATES_ENABLED"),
|
||||
updateApiUrl: parseOptionalHttpUrl("SCOREKO_UPDATE_API_URL"),
|
||||
updateReleasePageUrl: parseOptionalHttpUrl("SCOREKO_UPDATE_RELEASE_PAGE_URL"),
|
||||
updateAssetPattern: getOptionalEnv("SCOREKO_UPDATE_ASSET_PATTERN"),
|
||||
updateConfigPathOverride: getOptionalEnv("SCOREKO_UPDATE_CONFIG_PATH"),
|
||||
updateCheckDelayMs: parseEnvIntInRange("SCOREKO_UPDATE_CHECK_DELAY_MS", 5000, 0, 600000),
|
||||
updateCheckDelayMs: parseRequiredEnvIntInRange("SCOREKO_UPDATE_CHECK_DELAY_MS", 0, 600000),
|
||||
};
|
||||
}
|
||||
|
||||
export function getRequiredEnv(name: string): string {
|
||||
const value = process.env[name]?.trim();
|
||||
if (!value || value.length === 0) {
|
||||
throw new Error(`La variable de entorno requerida '${name}' no está definida en el archivo .env.`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export function getOptionalEnv(name: string): string | undefined {
|
||||
const value = process.env[name]?.trim();
|
||||
return value && value.length > 0 ? value : undefined;
|
||||
@@ -53,8 +77,18 @@ export function getEnv(name: string, fallback: string): string {
|
||||
return getOptionalEnv(name) ?? fallback;
|
||||
}
|
||||
|
||||
export function parseRequiredEnvIntInRange(name: string, min: number, max: number): number {
|
||||
const rawValue = getRequiredEnv(name);
|
||||
const parsedValue = Number.parseInt(rawValue, 10);
|
||||
if (!Number.isFinite(parsedValue) || parsedValue < min || parsedValue > max) {
|
||||
throw new Error(
|
||||
`The ${name} variable must be an integer between ${min} and ${max}. Received value: '${rawValue}'.`,
|
||||
);
|
||||
}
|
||||
return parsedValue;
|
||||
}
|
||||
|
||||
export function parseEnvIntInRange(name: string, fallback: number, min: number, max: number): number {
|
||||
// We throw here instead of silently coercing to avoid hidden misconfiguration in production.
|
||||
const rawValue = process.env[name];
|
||||
if (!rawValue) {
|
||||
return fallback;
|
||||
@@ -70,6 +104,19 @@ export function parseEnvIntInRange(name: string, fallback: number, min: number,
|
||||
return parsedValue;
|
||||
}
|
||||
|
||||
export function parseRequiredEnvBool(name: string): boolean {
|
||||
const rawValue = getRequiredEnv(name).toLowerCase();
|
||||
if (["1", "true", "yes", "on"].includes(rawValue)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (["0", "false", "no", "off"].includes(rawValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new Error(`The ${name} variable must be a boolean. Received value: '${rawValue}'.`);
|
||||
}
|
||||
|
||||
export function parseEnvBool(name: string, fallback: boolean): boolean {
|
||||
const rawValue = process.env[name]?.trim().toLowerCase();
|
||||
if (!rawValue) {
|
||||
@@ -105,6 +152,19 @@ export function parseOptionalHttpUrl(name: string): string | undefined {
|
||||
}
|
||||
}
|
||||
|
||||
export function parseRequiredEnvPort(name: string): string {
|
||||
const rawValue = getRequiredEnv(name);
|
||||
const parsedValue = Number.parseInt(rawValue, 10);
|
||||
|
||||
if (!Number.isFinite(parsedValue) || parsedValue < MIN_TCP_PORT || parsedValue > MAX_TCP_PORT) {
|
||||
throw new Error(
|
||||
`The ${name} variable must be a valid TCP port (${MIN_TCP_PORT}-${MAX_TCP_PORT}). Received value: '${rawValue}'.`,
|
||||
);
|
||||
}
|
||||
|
||||
return String(parsedValue);
|
||||
}
|
||||
|
||||
export function parseEnvPort(name: string, fallback: string): string {
|
||||
const rawValue = getEnv(name, fallback);
|
||||
const parsedValue = Number.parseInt(rawValue, 10);
|
||||
|
||||
Reference in New Issue
Block a user