Files
order_site/apps/backend/src/config/env-overrides.ts
T

406 lines
10 KiB
TypeScript

import path from "node:path";
import process from "node:process";
import type {
AdminDefaultUser,
RuntimeConfig,
} from "../types/runtime-config.js";
import { parseBoolean, parseInteger, parseJsonArray } from "./runtime-env.js";
import { deepMerge } from "./runtime-merge.js";
type RuntimeConfigPath = readonly [string, ...string[]];
type RuntimeConfigValue = string | number | boolean | AdminDefaultUser[];
type RuntimeEnv = Record<string, string | undefined>;
type EnvOverride = {
env: string;
path: RuntimeConfigPath;
read: (rawValue: unknown) => RuntimeConfigValue | null;
};
export const ENV_OVERRIDES: readonly EnvOverride[] = [
integerEnv("PORT", ["server", "port"]),
stringEnv("CHROME_PATH", ["browser", "chromePath"]),
booleanEnv("TENCENT_BROWSER_HEADLESS", ["browser", "headless"]),
booleanEnv("TENCENT_BROWSER_DEVTOOLS", ["browser", "devtools"]),
booleanEnv("TENCENT_BROWSER_KEEP_ALIVE", ["browser", "keepAlive"]),
booleanEnv("TENCENT_BROWSER_PREWARM", ["browser", "prewarm"]),
integerEnv("TENCENT_BROWSER_SLOW_MO", ["browser", "slowMoMs"]),
stringEnv("TENCENT_BROWSER_VNC_RESOLUTION", ["browser", "vncResolution"]),
booleanEnv("TENCENT_SESSION_DEBUG", ["session", "debug"]),
stringEnv("OCR_BASE_URL", ["ocr", "baseUrl"]),
stringEnv("DATA_ROOT", ["data", "root"], path.resolve),
stringEnv("LOG_LEVEL", ["logging", "level"]),
stringEnv("DATABASE_URL", ["database", "url"]),
booleanEnv("DATABASE_SSL", ["database", "ssl"]),
integerEnv("DATABASE_MAX_CONNECTIONS", ["database", "maxConnections"]),
integerEnv("CLAIM_TOKEN_TTL_HOURS", ["orders", "tokenTtlHours"]),
stringEnv("ADMIN_SESSION_SECRET", ["admin", "sessionSecret"]),
integerEnv("ADMIN_SESSION_TTL_HOURS", ["admin", "sessionTtlHours"]),
adminUsersJsonEnv("ADMIN_DEFAULT_USERS_JSON", ["admin", "defaultUsers"]),
stringEnv("AGISO_APP_SECRET", ["platforms", "agiso", "appSecret"]),
stringEnv("AGISO_TRADE_DETAIL_ENDPOINT", [
"platforms",
"agiso",
"tradeDetail",
"endpoint",
]),
stringEnv("AGISO_TRADE_DETAIL_API_VERSION", [
"platforms",
"agiso",
"tradeDetail",
"apiVersion",
]),
integerEnv("AGISO_TRADE_DETAIL_TIMEOUT_MS", [
"platforms",
"agiso",
"tradeDetail",
"timeoutMs",
]),
booleanEnv("AGISO_AUTO_DELIVERY_ENABLED", [
"platforms",
"agiso",
"autoDelivery",
"enabled",
]),
stringEnv("AGISO_AUTO_DELIVERY_ENDPOINT", [
"platforms",
"agiso",
"autoDelivery",
"endpoint",
]),
stringEnv("AGISO_AUTO_DELIVERY_API_VERSION", [
"platforms",
"agiso",
"autoDelivery",
"apiVersion",
]),
stringEnv("AGISO_MESSAGE_API_VERSION", [
"platforms",
"agiso",
"messaging",
"apiVersion",
]),
stringEnv("AGISO_SEND_MESSAGE_ENDPOINT", [
"platforms",
"agiso",
"messaging",
"sendMessageEndpoint",
]),
booleanEnv("AGISO_MESSAGING_ENABLED", [
"platforms",
"agiso",
"messaging",
"enabled",
]),
stringEnv("NINETYONE_USER_ID", ["platforms", "ninetyone", "userId"]),
stringEnv("NINETYONE_SECRET", ["platforms", "ninetyone", "secret"]),
stringEnv("NINETYONE_VERSION", ["platforms", "ninetyone", "version"]),
stringEnv("NINETYONE_SHOP_ID", ["platforms", "ninetyone", "shopId"]),
stringEnv("NINETYONE_SHOP_NAME", ["platforms", "ninetyone", "shopName"]),
integerEnv("NINETYONE_TIMESTAMP_TOLERANCE_SECONDS", [
"platforms",
"ninetyone",
"timestampToleranceSeconds",
]),
stringEnv("NINETYONE_CARDS_ENCODING", [
"platforms",
"ninetyone",
"cardsEncoding",
]),
stringEnv("CLOUDTENTACLES_BASE_URL", [
"platforms",
"cloudtentacles",
"baseUrl",
]),
integerEnv("CLOUDTENTACLES_TIMEOUT_MS", [
"platforms",
"cloudtentacles",
"timeoutMs",
]),
stringEnv("CLOUDTENTACLES_SEND_SMS_PATH", [
"platforms",
"cloudtentacles",
"sendSmsPath",
]),
stringEnv("CLOUDTENTACLES_LOGIN_PATH", [
"platforms",
"cloudtentacles",
"loginPath",
]),
stringEnv("CLOUDTENTACLES_USER_INFO_PATH", [
"platforms",
"cloudtentacles",
"userInfoPath",
]),
stringEnv("CLOUDTENTACLES_ASSET_PATH", [
"platforms",
"cloudtentacles",
"assetPath",
]),
stringEnv("CLOUDTENTACLES_PERMISSION_PATH", [
"platforms",
"cloudtentacles",
"permissionPath",
]),
stringEnv("CLOUDTENTACLES_CATEGORIES_PATH", [
"platforms",
"cloudtentacles",
"categoriesPath",
]),
stringEnv("CLOUDTENTACLES_SKU_LIST_PATH", [
"platforms",
"cloudtentacles",
"skuListPath",
]),
stringEnv("CLOUDTENTACLES_SKU_BUY_PATH", [
"platforms",
"cloudtentacles",
"skuBuyPath",
]),
stringEnv("CLOUDTENTACLES_KNAPSACK_PATH", [
"platforms",
"cloudtentacles",
"knapsackPath",
]),
stringEnv("CLOUDTENTACLES_VN_LIST_PATH", [
"platforms",
"cloudtentacles",
"vnListPath",
]),
stringEnv("CLOUDTENTACLES_VN_APPOINT_PATH", [
"platforms",
"cloudtentacles",
"vnAppointPath",
]),
stringEnv("CLOUDTENTACLES_VN_GENERATE_LOGIN_CODE_PATH", [
"platforms",
"cloudtentacles",
"vnGenerateLoginCodePath",
]),
stringEnv("CLOUDTENTACLES_VN_VERIF_CODE_PATH", [
"platforms",
"cloudtentacles",
"vnVerifCodePath",
]),
stringEnv("CLOUDTENTACLES_VN_VERIFY_LOGIN_CODE_PATH", [
"platforms",
"cloudtentacles",
"vnVerifyLoginCodePath",
]),
stringEnv("CLOUDTENTACLES_VN_BIND_URL_PATH", [
"platforms",
"cloudtentacles",
"vnBindUrlPath",
]),
stringEnv("CLOUDTENTACLES_VN_BIND_INFO_PATH", [
"platforms",
"cloudtentacles",
"vnBindInfoPath",
]),
stringEnv("CLOUDTENTACLES_VN_BACK_PATH", [
"platforms",
"cloudtentacles",
"vnBackPath",
]),
integerEnv("CLOUDTENTACLES_BIND_URL_TTL_SECONDS", [
"platforms",
"cloudtentacles",
"bindUrlTtlSeconds",
]),
integerEnv("CLOUDTENTACLES_BIND_URL_PROBE_INTERVAL_SECONDS", [
"platforms",
"cloudtentacles",
"bindUrlProbeIntervalSeconds",
]),
integerEnv("CLOUDTENTACLES_BIND_URL_PROBE_TIMEOUT_MS", [
"platforms",
"cloudtentacles",
"bindUrlProbeTimeoutMs",
]),
stringEnv("CLOUDTENTACLES_BIND_URL_PROBE_USER_AGENT", [
"platforms",
"cloudtentacles",
"bindUrlProbeUserAgent",
]),
stringEnv("CLOUDTENTACLES_BIND_URL_PROBE_ENDPOINT", [
"platforms",
"cloudtentacles",
"bindUrlProbeEndpoint",
]),
stringEnv("CLOUDTENTACLES_BIND_URL_PROBE_CHART_ID", [
"platforms",
"cloudtentacles",
"bindUrlProbeChartId",
]),
stringEnv("CLOUDTENTACLES_BIND_URL_PROBE_SUB_CHART_ID", [
"platforms",
"cloudtentacles",
"bindUrlProbeSubChartId",
]),
stringEnv("CLOUDTENTACLES_BIND_URL_PROBE_IDE_TOKEN", [
"platforms",
"cloudtentacles",
"bindUrlProbeIdeToken",
]),
stringEnv("CLOUDTENTACLES_BIND_URL_PROBE_ACTIVITY_URL", [
"platforms",
"cloudtentacles",
"bindUrlProbeActivityUrl",
]),
stringEnv("CLOUDTENTACLES_BIND_URL_PROBE_REFERER", [
"platforms",
"cloudtentacles",
"bindUrlProbeReferer",
]),
stringEnv("CLOUDTENTACLES_BIND_URL_PROBE_EXTRA_COOKIE", [
"platforms",
"cloudtentacles",
"bindUrlProbeExtraCookie",
]),
stringEnv("CLOUDTENTACLES_PUBLIC_KEY_PEM", [
"platforms",
"cloudtentacles",
"publicKeyPem",
]),
stringEnv("CLOUDTENTACLES_CLIENT_SOURCE", [
"platforms",
"cloudtentacles",
"clientSource",
]),
stringEnv("CLOUDTENTACLES_DEVICE_ID", [
"platforms",
"cloudtentacles",
"deviceId",
]),
integerEnv("CLOUDTENTACLES_DEVICE_TYPE", [
"platforms",
"cloudtentacles",
"deviceType",
]),
stringEnv("TENCENT_REDEEM_PROOF_MODE", ["redeem", "proofMode"]),
corsOriginsEnv("CORS_ALLOWED_ORIGINS", ["cors", "allowedOrigins"]),
];
export function applyEnvOverrides(
baseConfig: RuntimeConfig,
env: RuntimeEnv = process.env
): RuntimeConfig {
const nextConfig = deepMerge(baseConfig, {});
applyClaimBaseUrlOverride(nextConfig, env);
for (const override of ENV_OVERRIDES) {
const value = override.read(env[override.env]);
if (value !== null) {
setConfigValue(nextConfig, override.path, value);
}
}
return nextConfig;
}
function applyClaimBaseUrlOverride(
config: RuntimeConfig,
env: RuntimeEnv
): void {
const claimBaseUrl = parseString(env.CLAIM_BASE_URL);
if (claimBaseUrl !== null) {
config.orders.claimBaseUrl = claimBaseUrl;
return;
}
const appBaseUrl = parseString(env.APP_BASE_URL);
if (appBaseUrl !== null) {
config.orders.claimBaseUrl = `${appBaseUrl.replace(/\/+$/, "")}/#/claim`;
}
}
function stringEnv(
env: string,
configPath: RuntimeConfigPath,
transform: (value: string) => string = (value) => value
): EnvOverride {
return {
env,
path: configPath,
read(rawValue) {
const value = parseString(rawValue);
return value === null ? null : transform(value);
},
};
}
function integerEnv(env: string, configPath: RuntimeConfigPath): EnvOverride {
return {
env,
path: configPath,
read: parseInteger,
};
}
function booleanEnv(env: string, configPath: RuntimeConfigPath): EnvOverride {
return {
env,
path: configPath,
read: parseBoolean,
};
}
function adminUsersJsonEnv(env: string, configPath: RuntimeConfigPath): EnvOverride {
return {
env,
path: configPath,
read(rawValue) {
return parseJsonArray<AdminDefaultUser>(rawValue)
},
}
}
function corsOriginsEnv(env: string, configPath: RuntimeConfigPath): EnvOverride {
return {
env,
path: configPath,
read(rawValue) {
const value = parseString(rawValue)
if (value === null) {
return null
}
if (value.trim() === '*') {
return ['*']
}
return value.split(',').map((s) => s.trim()).filter(Boolean)
},
}
}
function parseString(rawValue: unknown): string | null {
const value = String(rawValue || "").trim();
return value ? value : null;
}
function setConfigValue(
config: RuntimeConfig,
configPath: RuntimeConfigPath,
value: RuntimeConfigValue
): void {
let current: Record<string, unknown> = config as unknown as Record<
string,
unknown
>;
for (const key of configPath.slice(0, -1)) {
const nextValue = current[key];
if (
!nextValue ||
typeof nextValue !== "object" ||
Array.isArray(nextValue)
) {
current[key] = {};
}
current = current[key] as Record<string, unknown>;
}
current[configPath[configPath.length - 1]] = value;
}