diff --git a/apps/backend/src/config/defaults.ts b/apps/backend/src/config/defaults.ts index 420eb8f7..aabc6eff 100644 --- a/apps/backend/src/config/defaults.ts +++ b/apps/backend/src/config/defaults.ts @@ -1,6 +1,6 @@ -import path from 'node:path' +import path from "node:path"; -import type { RuntimeConfig } from '../types/runtime-config.js' +import type { RuntimeConfig } from "../types/runtime-config.js"; export function createDefaultRuntimeConfig(projectRoot: string): RuntimeConfig { return { @@ -9,13 +9,13 @@ export function createDefaultRuntimeConfig(projectRoot: string): RuntimeConfig { }, browser: { - chromePath: '', + chromePath: "", headless: null, devtools: null, keepAlive: null, prewarm: null, slowMoMs: null, - vncResolution: '1600x900x24', + vncResolution: "1600x900x24", }, session: { @@ -23,99 +23,104 @@ export function createDefaultRuntimeConfig(projectRoot: string): RuntimeConfig { }, ocr: { - baseUrl: '', + baseUrl: "", }, data: { - root: path.resolve(projectRoot, 'data'), + root: path.resolve(projectRoot, "data"), }, logging: { - level: 'info', + level: "info", }, database: { - url: 'postgres://postgres:postgres@127.0.0.1:5432/order_site', + url: "postgres://postgres:postgres@127.0.0.1:5432/order_site", ssl: false, maxConnections: 10, }, orders: { - claimBaseUrl: 'http://127.0.0.1:5173/#/claim', + claimBaseUrl: "http://127.0.0.1:5173/#/claim", tokenTtlHours: 24, }, admin: { - sessionSecret: '', + sessionSecret: "", sessionTtlHours: 12, defaultUsers: [], }, platforms: { agiso: { - appSecret: '', + appSecret: "", tradeDetail: { - endpoint: 'https://gw-api.agiso.com/aldsIdle/Order/Detail', - apiVersion: '1', + endpoint: "https://gw-api.agiso.com/aldsIdle/Order/Detail", + apiVersion: "1", timeoutMs: 5000, }, autoDelivery: { enabled: true, - endpoint: 'https://gw-api.agiso.com/aldsIdle/Order/DummySend', - apiVersion: '1', + endpoint: "https://gw-api.agiso.com/aldsIdle/Order/DummySend", + apiVersion: "1", }, messaging: { enabled: false, - sendMessageEndpoint: 'https://gw-api.agiso.com/aldsIdle/ImMsg/SendMsg', - apiVersion: '1', - messageTemplate: '您的订单 {platformOrderId} 已创建领取链接,请在 {expiredAt} 前完成领取:{claimUrl}', - autoDeliveryMessageTemplate: '您的订单 {platformOrderId} 已完成自动发货,请注意查收。', + sendMessageEndpoint: + "https://gw-api.agiso.com/aldsIdle/ImMsg/SendMsg", + apiVersion: "1", + messageTemplate: + "您的订单 {platformOrderId} 已创建领取链接,请在 {expiredAt} 前完成领取:{claimUrl}", + autoDeliveryMessageTemplate: + "您的订单 {platformOrderId} 已完成自动发货,请注意查收。", shops: {}, }, }, ninetyone: { - userId: '', - secret: '', - version: '1.0', - shopId: '91kaquan', - shopName: '91卡券', + userId: "", + secret: "", + version: "1.0", + shopId: "91kaquan", + shopName: "91卡券", timestampToleranceSeconds: 600, - cardsEncoding: 'aes-256-ecb-base64', + cardsEncoding: "aes-256-ecb-base64", }, cloudtentacles: { - baseUrl: 'https://123.207.217.176', + baseUrl: "https://123.207.217.176", timeoutMs: 5000, - sendSmsPath: '/public/verif_code', - loginPath: '/public/login', - userInfoPath: '/user/info', - assetPath: '/user/get_asset', - permissionPath: '/user/get_permission', - categoriesPath: '/categories/get', - skuListPath: '/sku/list', - skuBuyPath: '/sku/buy', - skuUsePath: '/sku/use', - knapsackPath: '/user/get_knapsack', - vnListPath: '/vn/list', - vnAppointPath: '/vn/appoint', - vnGenerateLoginCodePath: '/vn/generate_login_code', - vnVerifCodePath: '/public/vn_verif_code', - vnVerifyLoginCodePath: '/vn/verify_login_code', - vnBindUrlPath: '/vn/bind_url', - vnBindInfoPath: '/vn/bind_info', - vnBackPath: '/vn/back', + sendSmsPath: "/public/verif_code", + loginPath: "/public/login", + userInfoPath: "/user/info", + assetPath: "/user/get_asset", + permissionPath: "/user/get_permission", + categoriesPath: "/categories/get", + skuListPath: "/sku/list", + skuBuyPath: "/sku/buy", + skuUsePath: "/sku/use", + knapsackPath: "/user/get_knapsack", + vnListPath: "/vn/list", + vnAppointPath: "/vn/appoint", + vnGenerateLoginCodePath: "/vn/generate_login_code", + vnVerifCodePath: "/public/vn_verif_code", + vnVerifyLoginCodePath: "/vn/verify_login_code", + vnBindUrlPath: "/vn/bind_url", + vnBindInfoPath: "/vn/bind_info", + vnBackPath: "/vn/back", bindUrlTtlSeconds: 600, bindUrlProbeIntervalSeconds: 30, bindUrlProbeTimeoutMs: 5000, - bindUrlProbeUserAgent: 'Mozilla/5.0 (Linux; Android 12; M2012K11AC Build/SKQ1.211006.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/98.0.4758.102 MQQBrowser/6.2 TBS/046141 Mobile Safari/537.36 V1_AND_SQ_8.8.85_2674_YYB_D QQ/8.8.85.7685 NetType/WIFI WebP/0.3.0 Pixel/1080 StatusBarHeight/108 SimpleUISwitch/0 QQTheme/1000', - bindUrlProbeEndpoint: 'https://comm.ams.game.qq.com/ide/', - bindUrlProbeChartId: '323794', - bindUrlProbeSubChartId: '323794', - bindUrlProbeIdeToken: 'z90Syo', - bindUrlProbeActivityUrl: 'http%3A%2F%2Fgp.qq.com%2Fcp%2Fa20240828cmcc%2F', - bindUrlProbeReferer: 'https://gp.qq.com/', - bindUrlProbeExtraCookie: '', - clientSource: 'ct-client', - deviceId: '-', + bindUrlProbeUserAgent: + "Mozilla/5.0 (Linux; Android 12; M2012K11AC Build/SKQ1.211006.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/98.0.4758.102 MQQBrowser/6.2 TBS/046141 Mobile Safari/537.36 V1_AND_SQ_8.8.85_2674_YYB_D QQ/8.8.85.7685 NetType/WIFI WebP/0.3.0 Pixel/1080 StatusBarHeight/108 SimpleUISwitch/0 QQTheme/1000", + bindUrlProbeEndpoint: "https://comm.ams.game.qq.com/ide/", + bindUrlProbeChartId: "323794", + bindUrlProbeSubChartId: "323794", + bindUrlProbeIdeToken: "z90Syo", + bindUrlProbeActivityUrl: + "http%3A%2F%2Fgp.qq.com%2Fcp%2Fa20240828cmcc%2F", + bindUrlProbeReferer: "https://gp.qq.com/", + bindUrlProbeExtraCookie: "", + clientSource: "ct-client", + deviceId: "-", deviceType: 0, publicKeyPem: `-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsSzjk+9st2f8IpYqYaM5 @@ -129,8 +134,12 @@ PfQTPNA++KsXtwRX9M6Re3vkTDRsutIIWKtj8jqhUbbYS3vzS8GJnAWavUFVkR15 }, }, - redeem: { - proofMode: 'full', + cors: { + allowedOrigins: ["*"], }, - } + + redeem: { + proofMode: "full", + }, + }; } diff --git a/apps/backend/src/config/env-overrides.ts b/apps/backend/src/config/env-overrides.ts index 3413a6ed..555b4e7a 100644 --- a/apps/backend/src/config/env-overrides.ts +++ b/apps/backend/src/config/env-overrides.ts @@ -1,135 +1,334 @@ -import path from 'node:path' -import process from 'node:process' +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' +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 +type RuntimeConfigPath = readonly [string, ...string[]]; +type RuntimeConfigValue = string | number | boolean | AdminDefaultUser[]; +type RuntimeEnv = Record; type EnvOverride = { - env: string - path: RuntimeConfigPath - read: (rawValue: unknown) => RuntimeConfigValue | null -} + 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']), -] + 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, {}) +export function applyEnvOverrides( + baseConfig: RuntimeConfig, + env: RuntimeEnv = process.env +): RuntimeConfig { + const nextConfig = deepMerge(baseConfig, {}); - applyClaimBaseUrlOverride(nextConfig, env) + applyClaimBaseUrlOverride(nextConfig, env); for (const override of ENV_OVERRIDES) { - const value = override.read(env[override.env]) + const value = override.read(env[override.env]); if (value !== null) { - setConfigValue(nextConfig, override.path, value) + setConfigValue(nextConfig, override.path, value); } } - return nextConfig + return nextConfig; } -function applyClaimBaseUrlOverride(config: RuntimeConfig, env: RuntimeEnv): void { - const claimBaseUrl = parseString(env.CLAIM_BASE_URL) +function applyClaimBaseUrlOverride( + config: RuntimeConfig, + env: RuntimeEnv +): void { + const claimBaseUrl = parseString(env.CLAIM_BASE_URL); if (claimBaseUrl !== null) { - config.orders.claimBaseUrl = claimBaseUrl - return + config.orders.claimBaseUrl = claimBaseUrl; + return; } - const appBaseUrl = parseString(env.APP_BASE_URL) + const appBaseUrl = parseString(env.APP_BASE_URL); if (appBaseUrl !== null) { - config.orders.claimBaseUrl = `${appBaseUrl.replace(/\/+$/, '')}/#/claim` + config.orders.claimBaseUrl = `${appBaseUrl.replace(/\/+$/, "")}/#/claim`; } } function stringEnv( env: string, configPath: RuntimeConfigPath, - transform: (value: string) => string = (value) => value, + transform: (value: string) => string = (value) => value ): EnvOverride { return { env, path: configPath, read(rawValue) { - const value = parseString(rawValue) - return value === null ? null : transform(value) + const value = parseString(rawValue); + return value === null ? null : transform(value); }, - } + }; } function integerEnv(env: string, configPath: RuntimeConfigPath): EnvOverride { @@ -137,7 +336,7 @@ function integerEnv(env: string, configPath: RuntimeConfigPath): EnvOverride { env, path: configPath, read: parseInteger, - } + }; } function booleanEnv(env: string, configPath: RuntimeConfigPath): EnvOverride { @@ -145,7 +344,7 @@ function booleanEnv(env: string, configPath: RuntimeConfigPath): EnvOverride { env, path: configPath, read: parseBoolean, - } + }; } function adminUsersJsonEnv(env: string, configPath: RuntimeConfigPath): EnvOverride { @@ -158,21 +357,49 @@ function adminUsersJsonEnv(env: string, configPath: RuntimeConfigPath): EnvOverr } } -function parseString(rawValue: unknown): string | null { - const value = String(rawValue || '').trim() - return value ? value : null +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 setConfigValue(config: RuntimeConfig, configPath: RuntimeConfigPath, value: RuntimeConfigValue): void { - let current: Record = config as unknown as Record +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 = 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] = {} + const nextValue = current[key]; + if ( + !nextValue || + typeof nextValue !== "object" || + Array.isArray(nextValue) + ) { + current[key] = {}; } - current = current[key] as Record + current = current[key] as Record; } - current[configPath[configPath.length - 1]] = value + current[configPath[configPath.length - 1]] = value; } diff --git a/apps/backend/src/types/runtime-config.ts b/apps/backend/src/types/runtime-config.ts index 4ab7097c..d14a32c4 100644 --- a/apps/backend/src/types/runtime-config.ts +++ b/apps/backend/src/types/runtime-config.ts @@ -1,132 +1,138 @@ export type AdminDefaultUser = { - username: string - password: string - role: string - status?: string -} + username: string; + password: string; + role: string; + status?: string; +}; export type AgisoMessagingShopConfig = { - enabled?: boolean - shopName?: string - accessToken?: string - messageTemplate?: string - autoDeliveryMessageTemplate?: string - appSecret?: string - apiVersion?: string - sendMessageEndpoint?: string -} + enabled?: boolean; + shopName?: string; + accessToken?: string; + messageTemplate?: string; + autoDeliveryMessageTemplate?: string; + appSecret?: string; + apiVersion?: string; + sendMessageEndpoint?: string; +}; -export type AgisoMessagingShopsConfig = Record +export type AgisoMessagingShopsConfig = Record< + string, + AgisoMessagingShopConfig +>; export type RuntimeConfig = { server: { - port: number - } + port: number; + }; browser: { - chromePath: string - headless: boolean | null - devtools: boolean | null - keepAlive: boolean | null - prewarm: boolean | null - slowMoMs: number | null - vncResolution: string - } + chromePath: string; + headless: boolean | null; + devtools: boolean | null; + keepAlive: boolean | null; + prewarm: boolean | null; + slowMoMs: number | null; + vncResolution: string; + }; session: { - debug: boolean - } + debug: boolean; + }; ocr: { - baseUrl: string - } + baseUrl: string; + }; data: { - root: string - } + root: string; + }; logging: { - level: string - } + level: string; + }; database: { - url: string - ssl: boolean - maxConnections: number - } + url: string; + ssl: boolean; + maxConnections: number; + }; orders: { - claimBaseUrl: string - tokenTtlHours: number - } + claimBaseUrl: string; + tokenTtlHours: number; + }; admin: { - sessionSecret: string - sessionTtlHours: number - defaultUsers: AdminDefaultUser[] - } + sessionSecret: string; + sessionTtlHours: number; + defaultUsers: AdminDefaultUser[]; + }; platforms: { agiso: { - appSecret: string + appSecret: string; tradeDetail: { - endpoint: string - apiVersion: string - timeoutMs: number - } + endpoint: string; + apiVersion: string; + timeoutMs: number; + }; autoDelivery: { - enabled: boolean - endpoint: string - apiVersion: string - } + enabled: boolean; + endpoint: string; + apiVersion: string; + }; messaging: { - enabled: boolean - apiVersion: string - sendMessageEndpoint: string - messageTemplate: string - autoDeliveryMessageTemplate: string - shops: AgisoMessagingShopsConfig - } - } + enabled: boolean; + apiVersion: string; + sendMessageEndpoint: string; + messageTemplate: string; + autoDeliveryMessageTemplate: string; + shops: AgisoMessagingShopsConfig; + }; + }; ninetyone: { - userId: string - secret: string - version: string - shopId: string - shopName: string - timestampToleranceSeconds: number - cardsEncoding: string - } + userId: string; + secret: string; + version: string; + shopId: string; + shopName: string; + timestampToleranceSeconds: number; + cardsEncoding: string; + }; cloudtentacles: { - baseUrl: string - timeoutMs: number - sendSmsPath: string - loginPath: string - userInfoPath: string - assetPath: string - permissionPath: string - categoriesPath: string - skuListPath: string - skuBuyPath: string - skuUsePath: string - knapsackPath: string - vnListPath: string - vnAppointPath: string - vnGenerateLoginCodePath: string - vnVerifCodePath: string - vnVerifyLoginCodePath: string - vnBindUrlPath: string - vnBindInfoPath: string - vnBackPath: string - bindUrlTtlSeconds: number - bindUrlProbeIntervalSeconds: number - bindUrlProbeTimeoutMs: number - bindUrlProbeUserAgent: string - bindUrlProbeEndpoint: string - bindUrlProbeChartId: string - bindUrlProbeSubChartId: string - bindUrlProbeIdeToken: string - bindUrlProbeActivityUrl: string - bindUrlProbeReferer: string - bindUrlProbeExtraCookie: string - publicKeyPem: string - clientSource: string - deviceId: string - deviceType: number - } - } + baseUrl: string; + timeoutMs: number; + sendSmsPath: string; + loginPath: string; + userInfoPath: string; + assetPath: string; + permissionPath: string; + categoriesPath: string; + skuListPath: string; + skuBuyPath: string; + skuUsePath: string; + knapsackPath: string; + vnListPath: string; + vnAppointPath: string; + vnGenerateLoginCodePath: string; + vnVerifCodePath: string; + vnVerifyLoginCodePath: string; + vnBindUrlPath: string; + vnBindInfoPath: string; + vnBackPath: string; + bindUrlTtlSeconds: number; + bindUrlProbeIntervalSeconds: number; + bindUrlProbeTimeoutMs: number; + bindUrlProbeUserAgent: string; + bindUrlProbeEndpoint: string; + bindUrlProbeChartId: string; + bindUrlProbeSubChartId: string; + bindUrlProbeIdeToken: string; + bindUrlProbeActivityUrl: string; + bindUrlProbeReferer: string; + bindUrlProbeExtraCookie: string; + publicKeyPem: string; + clientSource: string; + deviceId: string; + deviceType: number; + }; + }; + cors: { + allowedOrigins: string[]; + }; redeem: { - proofMode: string - } -} + proofMode: string; + }; +};