prep: commit previous optimization changes before team merge

This commit is contained in:
yml
2026-05-21 23:17:28 +08:00
parent 0e6bf88beb
commit 0567da414c
3 changed files with 520 additions and 278 deletions
+66 -57
View File
@@ -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 { export function createDefaultRuntimeConfig(projectRoot: string): RuntimeConfig {
return { return {
@@ -9,13 +9,13 @@ export function createDefaultRuntimeConfig(projectRoot: string): RuntimeConfig {
}, },
browser: { browser: {
chromePath: '', chromePath: "",
headless: null, headless: null,
devtools: null, devtools: null,
keepAlive: null, keepAlive: null,
prewarm: null, prewarm: null,
slowMoMs: null, slowMoMs: null,
vncResolution: '1600x900x24', vncResolution: "1600x900x24",
}, },
session: { session: {
@@ -23,99 +23,104 @@ export function createDefaultRuntimeConfig(projectRoot: string): RuntimeConfig {
}, },
ocr: { ocr: {
baseUrl: '', baseUrl: "",
}, },
data: { data: {
root: path.resolve(projectRoot, 'data'), root: path.resolve(projectRoot, "data"),
}, },
logging: { logging: {
level: 'info', level: "info",
}, },
database: { 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, ssl: false,
maxConnections: 10, maxConnections: 10,
}, },
orders: { orders: {
claimBaseUrl: 'http://127.0.0.1:5173/#/claim', claimBaseUrl: "http://127.0.0.1:5173/#/claim",
tokenTtlHours: 24, tokenTtlHours: 24,
}, },
admin: { admin: {
sessionSecret: '', sessionSecret: "",
sessionTtlHours: 12, sessionTtlHours: 12,
defaultUsers: [], defaultUsers: [],
}, },
platforms: { platforms: {
agiso: { agiso: {
appSecret: '', appSecret: "",
tradeDetail: { tradeDetail: {
endpoint: 'https://gw-api.agiso.com/aldsIdle/Order/Detail', endpoint: "https://gw-api.agiso.com/aldsIdle/Order/Detail",
apiVersion: '1', apiVersion: "1",
timeoutMs: 5000, timeoutMs: 5000,
}, },
autoDelivery: { autoDelivery: {
enabled: true, enabled: true,
endpoint: 'https://gw-api.agiso.com/aldsIdle/Order/DummySend', endpoint: "https://gw-api.agiso.com/aldsIdle/Order/DummySend",
apiVersion: '1', apiVersion: "1",
}, },
messaging: { messaging: {
enabled: false, enabled: false,
sendMessageEndpoint: 'https://gw-api.agiso.com/aldsIdle/ImMsg/SendMsg', sendMessageEndpoint:
apiVersion: '1', "https://gw-api.agiso.com/aldsIdle/ImMsg/SendMsg",
messageTemplate: '您的订单 {platformOrderId} 已创建领取链接,请在 {expiredAt} 前完成领取:{claimUrl}', apiVersion: "1",
autoDeliveryMessageTemplate: '您的订单 {platformOrderId} 已完成自动发货,请注意查收。', messageTemplate:
"您的订单 {platformOrderId} 已创建领取链接,请在 {expiredAt} 前完成领取:{claimUrl}",
autoDeliveryMessageTemplate:
"您的订单 {platformOrderId} 已完成自动发货,请注意查收。",
shops: {}, shops: {},
}, },
}, },
ninetyone: { ninetyone: {
userId: '', userId: "",
secret: '', secret: "",
version: '1.0', version: "1.0",
shopId: '91kaquan', shopId: "91kaquan",
shopName: '91卡券', shopName: "91卡券",
timestampToleranceSeconds: 600, timestampToleranceSeconds: 600,
cardsEncoding: 'aes-256-ecb-base64', cardsEncoding: "aes-256-ecb-base64",
}, },
cloudtentacles: { cloudtentacles: {
baseUrl: 'https://123.207.217.176', baseUrl: "https://123.207.217.176",
timeoutMs: 5000, timeoutMs: 5000,
sendSmsPath: '/public/verif_code', sendSmsPath: "/public/verif_code",
loginPath: '/public/login', loginPath: "/public/login",
userInfoPath: '/user/info', userInfoPath: "/user/info",
assetPath: '/user/get_asset', assetPath: "/user/get_asset",
permissionPath: '/user/get_permission', permissionPath: "/user/get_permission",
categoriesPath: '/categories/get', categoriesPath: "/categories/get",
skuListPath: '/sku/list', skuListPath: "/sku/list",
skuBuyPath: '/sku/buy', skuBuyPath: "/sku/buy",
skuUsePath: '/sku/use', skuUsePath: "/sku/use",
knapsackPath: '/user/get_knapsack', knapsackPath: "/user/get_knapsack",
vnListPath: '/vn/list', vnListPath: "/vn/list",
vnAppointPath: '/vn/appoint', vnAppointPath: "/vn/appoint",
vnGenerateLoginCodePath: '/vn/generate_login_code', vnGenerateLoginCodePath: "/vn/generate_login_code",
vnVerifCodePath: '/public/vn_verif_code', vnVerifCodePath: "/public/vn_verif_code",
vnVerifyLoginCodePath: '/vn/verify_login_code', vnVerifyLoginCodePath: "/vn/verify_login_code",
vnBindUrlPath: '/vn/bind_url', vnBindUrlPath: "/vn/bind_url",
vnBindInfoPath: '/vn/bind_info', vnBindInfoPath: "/vn/bind_info",
vnBackPath: '/vn/back', vnBackPath: "/vn/back",
bindUrlTtlSeconds: 600, bindUrlTtlSeconds: 600,
bindUrlProbeIntervalSeconds: 30, bindUrlProbeIntervalSeconds: 30,
bindUrlProbeTimeoutMs: 5000, 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', bindUrlProbeUserAgent:
bindUrlProbeEndpoint: 'https://comm.ams.game.qq.com/ide/', "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",
bindUrlProbeChartId: '323794', bindUrlProbeEndpoint: "https://comm.ams.game.qq.com/ide/",
bindUrlProbeSubChartId: '323794', bindUrlProbeChartId: "323794",
bindUrlProbeIdeToken: 'z90Syo', bindUrlProbeSubChartId: "323794",
bindUrlProbeActivityUrl: 'http%3A%2F%2Fgp.qq.com%2Fcp%2Fa20240828cmcc%2F', bindUrlProbeIdeToken: "z90Syo",
bindUrlProbeReferer: 'https://gp.qq.com/', bindUrlProbeActivityUrl:
bindUrlProbeExtraCookie: '', "http%3A%2F%2Fgp.qq.com%2Fcp%2Fa20240828cmcc%2F",
clientSource: 'ct-client', bindUrlProbeReferer: "https://gp.qq.com/",
deviceId: '-', bindUrlProbeExtraCookie: "",
clientSource: "ct-client",
deviceId: "-",
deviceType: 0, deviceType: 0,
publicKeyPem: `-----BEGIN PUBLIC KEY----- publicKeyPem: `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsSzjk+9st2f8IpYqYaM5 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsSzjk+9st2f8IpYqYaM5
@@ -129,8 +134,12 @@ PfQTPNA++KsXtwRX9M6Re3vkTDRsutIIWKtj8jqhUbbYS3vzS8GJnAWavUFVkR15
}, },
}, },
redeem: { cors: {
proofMode: 'full', allowedOrigins: ["*"],
}, },
}
redeem: {
proofMode: "full",
},
};
} }
+339 -112
View File
@@ -1,135 +1,334 @@
import path from 'node:path' import path from "node:path";
import process from 'node:process' import process from "node:process";
import type { AdminDefaultUser, RuntimeConfig } from '../types/runtime-config.js' import type {
import { parseBoolean, parseInteger, parseJsonArray } from './runtime-env.js' AdminDefaultUser,
import { deepMerge } from './runtime-merge.js' 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 RuntimeConfigPath = readonly [string, ...string[]];
type RuntimeConfigValue = string | number | boolean | AdminDefaultUser[] type RuntimeConfigValue = string | number | boolean | AdminDefaultUser[];
type RuntimeEnv = Record<string, string | undefined> type RuntimeEnv = Record<string, string | undefined>;
type EnvOverride = { type EnvOverride = {
env: string env: string;
path: RuntimeConfigPath path: RuntimeConfigPath;
read: (rawValue: unknown) => RuntimeConfigValue | null read: (rawValue: unknown) => RuntimeConfigValue | null;
} };
export const ENV_OVERRIDES: readonly EnvOverride[] = [ export const ENV_OVERRIDES: readonly EnvOverride[] = [
integerEnv('PORT', ['server', 'port']), integerEnv("PORT", ["server", "port"]),
stringEnv('CHROME_PATH', ['browser', 'chromePath']), stringEnv("CHROME_PATH", ["browser", "chromePath"]),
booleanEnv('TENCENT_BROWSER_HEADLESS', ['browser', 'headless']), booleanEnv("TENCENT_BROWSER_HEADLESS", ["browser", "headless"]),
booleanEnv('TENCENT_BROWSER_DEVTOOLS', ['browser', 'devtools']), booleanEnv("TENCENT_BROWSER_DEVTOOLS", ["browser", "devtools"]),
booleanEnv('TENCENT_BROWSER_KEEP_ALIVE', ['browser', 'keepAlive']), booleanEnv("TENCENT_BROWSER_KEEP_ALIVE", ["browser", "keepAlive"]),
booleanEnv('TENCENT_BROWSER_PREWARM', ['browser', 'prewarm']), booleanEnv("TENCENT_BROWSER_PREWARM", ["browser", "prewarm"]),
integerEnv('TENCENT_BROWSER_SLOW_MO', ['browser', 'slowMoMs']), integerEnv("TENCENT_BROWSER_SLOW_MO", ["browser", "slowMoMs"]),
stringEnv('TENCENT_BROWSER_VNC_RESOLUTION', ['browser', 'vncResolution']), stringEnv("TENCENT_BROWSER_VNC_RESOLUTION", ["browser", "vncResolution"]),
booleanEnv('TENCENT_SESSION_DEBUG', ['session', 'debug']), booleanEnv("TENCENT_SESSION_DEBUG", ["session", "debug"]),
stringEnv('OCR_BASE_URL', ['ocr', 'baseUrl']), stringEnv("OCR_BASE_URL", ["ocr", "baseUrl"]),
stringEnv('DATA_ROOT', ['data', 'root'], path.resolve), stringEnv("DATA_ROOT", ["data", "root"], path.resolve),
stringEnv('LOG_LEVEL', ['logging', 'level']), stringEnv("LOG_LEVEL", ["logging", "level"]),
stringEnv('DATABASE_URL', ['database', 'url']), stringEnv("DATABASE_URL", ["database", "url"]),
booleanEnv('DATABASE_SSL', ['database', 'ssl']), booleanEnv("DATABASE_SSL", ["database", "ssl"]),
integerEnv('DATABASE_MAX_CONNECTIONS', ['database', 'maxConnections']), integerEnv("DATABASE_MAX_CONNECTIONS", ["database", "maxConnections"]),
integerEnv('CLAIM_TOKEN_TTL_HOURS', ['orders', 'tokenTtlHours']), integerEnv("CLAIM_TOKEN_TTL_HOURS", ["orders", "tokenTtlHours"]),
stringEnv('ADMIN_SESSION_SECRET', ['admin', 'sessionSecret']), stringEnv("ADMIN_SESSION_SECRET", ["admin", "sessionSecret"]),
integerEnv('ADMIN_SESSION_TTL_HOURS', ['admin', 'sessionTtlHours']), integerEnv("ADMIN_SESSION_TTL_HOURS", ["admin", "sessionTtlHours"]),
adminUsersJsonEnv('ADMIN_DEFAULT_USERS_JSON', ['admin', 'defaultUsers']), adminUsersJsonEnv("ADMIN_DEFAULT_USERS_JSON", ["admin", "defaultUsers"]),
stringEnv('AGISO_APP_SECRET', ['platforms', 'agiso', 'appSecret']), stringEnv("AGISO_APP_SECRET", ["platforms", "agiso", "appSecret"]),
stringEnv('AGISO_TRADE_DETAIL_ENDPOINT', ['platforms', 'agiso', 'tradeDetail', 'endpoint']), stringEnv("AGISO_TRADE_DETAIL_ENDPOINT", [
stringEnv('AGISO_TRADE_DETAIL_API_VERSION', ['platforms', 'agiso', 'tradeDetail', 'apiVersion']), "platforms",
integerEnv('AGISO_TRADE_DETAIL_TIMEOUT_MS', ['platforms', 'agiso', 'tradeDetail', 'timeoutMs']), "agiso",
booleanEnv('AGISO_AUTO_DELIVERY_ENABLED', ['platforms', 'agiso', 'autoDelivery', 'enabled']), "tradeDetail",
stringEnv('AGISO_AUTO_DELIVERY_ENDPOINT', ['platforms', 'agiso', 'autoDelivery', 'endpoint']), "endpoint",
stringEnv('AGISO_AUTO_DELIVERY_API_VERSION', ['platforms', 'agiso', 'autoDelivery', 'apiVersion']), ]),
stringEnv('AGISO_MESSAGE_API_VERSION', ['platforms', 'agiso', 'messaging', 'apiVersion']), stringEnv("AGISO_TRADE_DETAIL_API_VERSION", [
stringEnv('AGISO_SEND_MESSAGE_ENDPOINT', ['platforms', 'agiso', 'messaging', 'sendMessageEndpoint']), "platforms",
booleanEnv('AGISO_MESSAGING_ENABLED', ['platforms', 'agiso', 'messaging', 'enabled']), "agiso",
stringEnv('NINETYONE_USER_ID', ['platforms', 'ninetyone', 'userId']), "tradeDetail",
stringEnv('NINETYONE_SECRET', ['platforms', 'ninetyone', 'secret']), "apiVersion",
stringEnv('NINETYONE_VERSION', ['platforms', 'ninetyone', 'version']), ]),
stringEnv('NINETYONE_SHOP_ID', ['platforms', 'ninetyone', 'shopId']), integerEnv("AGISO_TRADE_DETAIL_TIMEOUT_MS", [
stringEnv('NINETYONE_SHOP_NAME', ['platforms', 'ninetyone', 'shopName']), "platforms",
integerEnv('NINETYONE_TIMESTAMP_TOLERANCE_SECONDS', ['platforms', 'ninetyone', 'timestampToleranceSeconds']), "agiso",
stringEnv('NINETYONE_CARDS_ENCODING', ['platforms', 'ninetyone', 'cardsEncoding']), "tradeDetail",
stringEnv('CLOUDTENTACLES_BASE_URL', ['platforms', 'cloudtentacles', 'baseUrl']), "timeoutMs",
integerEnv('CLOUDTENTACLES_TIMEOUT_MS', ['platforms', 'cloudtentacles', 'timeoutMs']), ]),
stringEnv('CLOUDTENTACLES_SEND_SMS_PATH', ['platforms', 'cloudtentacles', 'sendSmsPath']), booleanEnv("AGISO_AUTO_DELIVERY_ENABLED", [
stringEnv('CLOUDTENTACLES_LOGIN_PATH', ['platforms', 'cloudtentacles', 'loginPath']), "platforms",
stringEnv('CLOUDTENTACLES_USER_INFO_PATH', ['platforms', 'cloudtentacles', 'userInfoPath']), "agiso",
stringEnv('CLOUDTENTACLES_ASSET_PATH', ['platforms', 'cloudtentacles', 'assetPath']), "autoDelivery",
stringEnv('CLOUDTENTACLES_PERMISSION_PATH', ['platforms', 'cloudtentacles', 'permissionPath']), "enabled",
stringEnv('CLOUDTENTACLES_CATEGORIES_PATH', ['platforms', 'cloudtentacles', 'categoriesPath']), ]),
stringEnv('CLOUDTENTACLES_SKU_LIST_PATH', ['platforms', 'cloudtentacles', 'skuListPath']), stringEnv("AGISO_AUTO_DELIVERY_ENDPOINT", [
stringEnv('CLOUDTENTACLES_SKU_BUY_PATH', ['platforms', 'cloudtentacles', 'skuBuyPath']), "platforms",
stringEnv('CLOUDTENTACLES_KNAPSACK_PATH', ['platforms', 'cloudtentacles', 'knapsackPath']), "agiso",
stringEnv('CLOUDTENTACLES_VN_LIST_PATH', ['platforms', 'cloudtentacles', 'vnListPath']), "autoDelivery",
stringEnv('CLOUDTENTACLES_VN_APPOINT_PATH', ['platforms', 'cloudtentacles', 'vnAppointPath']), "endpoint",
stringEnv('CLOUDTENTACLES_VN_GENERATE_LOGIN_CODE_PATH', ['platforms', 'cloudtentacles', 'vnGenerateLoginCodePath']), ]),
stringEnv('CLOUDTENTACLES_VN_VERIF_CODE_PATH', ['platforms', 'cloudtentacles', 'vnVerifCodePath']), stringEnv("AGISO_AUTO_DELIVERY_API_VERSION", [
stringEnv('CLOUDTENTACLES_VN_VERIFY_LOGIN_CODE_PATH', ['platforms', 'cloudtentacles', 'vnVerifyLoginCodePath']), "platforms",
stringEnv('CLOUDTENTACLES_VN_BIND_URL_PATH', ['platforms', 'cloudtentacles', 'vnBindUrlPath']), "agiso",
stringEnv('CLOUDTENTACLES_VN_BIND_INFO_PATH', ['platforms', 'cloudtentacles', 'vnBindInfoPath']), "autoDelivery",
stringEnv('CLOUDTENTACLES_VN_BACK_PATH', ['platforms', 'cloudtentacles', 'vnBackPath']), "apiVersion",
integerEnv('CLOUDTENTACLES_BIND_URL_TTL_SECONDS', ['platforms', 'cloudtentacles', 'bindUrlTtlSeconds']), ]),
integerEnv('CLOUDTENTACLES_BIND_URL_PROBE_INTERVAL_SECONDS', ['platforms', 'cloudtentacles', 'bindUrlProbeIntervalSeconds']), stringEnv("AGISO_MESSAGE_API_VERSION", [
integerEnv('CLOUDTENTACLES_BIND_URL_PROBE_TIMEOUT_MS', ['platforms', 'cloudtentacles', 'bindUrlProbeTimeoutMs']), "platforms",
stringEnv('CLOUDTENTACLES_BIND_URL_PROBE_USER_AGENT', ['platforms', 'cloudtentacles', 'bindUrlProbeUserAgent']), "agiso",
stringEnv('CLOUDTENTACLES_BIND_URL_PROBE_ENDPOINT', ['platforms', 'cloudtentacles', 'bindUrlProbeEndpoint']), "messaging",
stringEnv('CLOUDTENTACLES_BIND_URL_PROBE_CHART_ID', ['platforms', 'cloudtentacles', 'bindUrlProbeChartId']), "apiVersion",
stringEnv('CLOUDTENTACLES_BIND_URL_PROBE_SUB_CHART_ID', ['platforms', 'cloudtentacles', 'bindUrlProbeSubChartId']), ]),
stringEnv('CLOUDTENTACLES_BIND_URL_PROBE_IDE_TOKEN', ['platforms', 'cloudtentacles', 'bindUrlProbeIdeToken']), stringEnv("AGISO_SEND_MESSAGE_ENDPOINT", [
stringEnv('CLOUDTENTACLES_BIND_URL_PROBE_ACTIVITY_URL', ['platforms', 'cloudtentacles', 'bindUrlProbeActivityUrl']), "platforms",
stringEnv('CLOUDTENTACLES_BIND_URL_PROBE_REFERER', ['platforms', 'cloudtentacles', 'bindUrlProbeReferer']), "agiso",
stringEnv('CLOUDTENTACLES_BIND_URL_PROBE_EXTRA_COOKIE', ['platforms', 'cloudtentacles', 'bindUrlProbeExtraCookie']), "messaging",
stringEnv('CLOUDTENTACLES_PUBLIC_KEY_PEM', ['platforms', 'cloudtentacles', 'publicKeyPem']), "sendMessageEndpoint",
stringEnv('CLOUDTENTACLES_CLIENT_SOURCE', ['platforms', 'cloudtentacles', 'clientSource']), ]),
stringEnv('CLOUDTENTACLES_DEVICE_ID', ['platforms', 'cloudtentacles', 'deviceId']), booleanEnv("AGISO_MESSAGING_ENABLED", [
integerEnv('CLOUDTENTACLES_DEVICE_TYPE', ['platforms', 'cloudtentacles', 'deviceType']), "platforms",
stringEnv('TENCENT_REDEEM_PROOF_MODE', ['redeem', 'proofMode']), "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 { export function applyEnvOverrides(
const nextConfig = deepMerge(baseConfig, {}) baseConfig: RuntimeConfig,
env: RuntimeEnv = process.env
): RuntimeConfig {
const nextConfig = deepMerge(baseConfig, {});
applyClaimBaseUrlOverride(nextConfig, env) applyClaimBaseUrlOverride(nextConfig, env);
for (const override of ENV_OVERRIDES) { for (const override of ENV_OVERRIDES) {
const value = override.read(env[override.env]) const value = override.read(env[override.env]);
if (value !== null) { if (value !== null) {
setConfigValue(nextConfig, override.path, value) setConfigValue(nextConfig, override.path, value);
} }
} }
return nextConfig return nextConfig;
} }
function applyClaimBaseUrlOverride(config: RuntimeConfig, env: RuntimeEnv): void { function applyClaimBaseUrlOverride(
const claimBaseUrl = parseString(env.CLAIM_BASE_URL) config: RuntimeConfig,
env: RuntimeEnv
): void {
const claimBaseUrl = parseString(env.CLAIM_BASE_URL);
if (claimBaseUrl !== null) { if (claimBaseUrl !== null) {
config.orders.claimBaseUrl = claimBaseUrl config.orders.claimBaseUrl = claimBaseUrl;
return return;
} }
const appBaseUrl = parseString(env.APP_BASE_URL) const appBaseUrl = parseString(env.APP_BASE_URL);
if (appBaseUrl !== null) { if (appBaseUrl !== null) {
config.orders.claimBaseUrl = `${appBaseUrl.replace(/\/+$/, '')}/#/claim` config.orders.claimBaseUrl = `${appBaseUrl.replace(/\/+$/, "")}/#/claim`;
} }
} }
function stringEnv( function stringEnv(
env: string, env: string,
configPath: RuntimeConfigPath, configPath: RuntimeConfigPath,
transform: (value: string) => string = (value) => value, transform: (value: string) => string = (value) => value
): EnvOverride { ): EnvOverride {
return { return {
env, env,
path: configPath, path: configPath,
read(rawValue) { read(rawValue) {
const value = parseString(rawValue) const value = parseString(rawValue);
return value === null ? null : transform(value) return value === null ? null : transform(value);
}, },
} };
} }
function integerEnv(env: string, configPath: RuntimeConfigPath): EnvOverride { function integerEnv(env: string, configPath: RuntimeConfigPath): EnvOverride {
@@ -137,7 +336,7 @@ function integerEnv(env: string, configPath: RuntimeConfigPath): EnvOverride {
env, env,
path: configPath, path: configPath,
read: parseInteger, read: parseInteger,
} };
} }
function booleanEnv(env: string, configPath: RuntimeConfigPath): EnvOverride { function booleanEnv(env: string, configPath: RuntimeConfigPath): EnvOverride {
@@ -145,7 +344,7 @@ function booleanEnv(env: string, configPath: RuntimeConfigPath): EnvOverride {
env, env,
path: configPath, path: configPath,
read: parseBoolean, read: parseBoolean,
} };
} }
function adminUsersJsonEnv(env: string, configPath: RuntimeConfigPath): EnvOverride { function adminUsersJsonEnv(env: string, configPath: RuntimeConfigPath): EnvOverride {
@@ -158,21 +357,49 @@ function adminUsersJsonEnv(env: string, configPath: RuntimeConfigPath): EnvOverr
} }
} }
function parseString(rawValue: unknown): string | null { function corsOriginsEnv(env: string, configPath: RuntimeConfigPath): EnvOverride {
const value = String(rawValue || '').trim() return {
return value ? value : null 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 { function parseString(rawValue: unknown): string | null {
let current: Record<string, unknown> = config as unknown as Record<string, unknown> 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)) { for (const key of configPath.slice(0, -1)) {
const nextValue = current[key] const nextValue = current[key];
if (!nextValue || typeof nextValue !== 'object' || Array.isArray(nextValue)) { if (
current[key] = {} !nextValue ||
typeof nextValue !== "object" ||
Array.isArray(nextValue)
) {
current[key] = {};
} }
current = current[key] as Record<string, unknown> current = current[key] as Record<string, unknown>;
} }
current[configPath[configPath.length - 1]] = value current[configPath[configPath.length - 1]] = value;
} }
+115 -109
View File
@@ -1,132 +1,138 @@
export type AdminDefaultUser = { export type AdminDefaultUser = {
username: string username: string;
password: string password: string;
role: string role: string;
status?: string status?: string;
} };
export type AgisoMessagingShopConfig = { export type AgisoMessagingShopConfig = {
enabled?: boolean enabled?: boolean;
shopName?: string shopName?: string;
accessToken?: string accessToken?: string;
messageTemplate?: string messageTemplate?: string;
autoDeliveryMessageTemplate?: string autoDeliveryMessageTemplate?: string;
appSecret?: string appSecret?: string;
apiVersion?: string apiVersion?: string;
sendMessageEndpoint?: string sendMessageEndpoint?: string;
} };
export type AgisoMessagingShopsConfig = Record<string, AgisoMessagingShopConfig> export type AgisoMessagingShopsConfig = Record<
string,
AgisoMessagingShopConfig
>;
export type RuntimeConfig = { export type RuntimeConfig = {
server: { server: {
port: number port: number;
} };
browser: { browser: {
chromePath: string chromePath: string;
headless: boolean | null headless: boolean | null;
devtools: boolean | null devtools: boolean | null;
keepAlive: boolean | null keepAlive: boolean | null;
prewarm: boolean | null prewarm: boolean | null;
slowMoMs: number | null slowMoMs: number | null;
vncResolution: string vncResolution: string;
} };
session: { session: {
debug: boolean debug: boolean;
} };
ocr: { ocr: {
baseUrl: string baseUrl: string;
} };
data: { data: {
root: string root: string;
} };
logging: { logging: {
level: string level: string;
} };
database: { database: {
url: string url: string;
ssl: boolean ssl: boolean;
maxConnections: number maxConnections: number;
} };
orders: { orders: {
claimBaseUrl: string claimBaseUrl: string;
tokenTtlHours: number tokenTtlHours: number;
} };
admin: { admin: {
sessionSecret: string sessionSecret: string;
sessionTtlHours: number sessionTtlHours: number;
defaultUsers: AdminDefaultUser[] defaultUsers: AdminDefaultUser[];
} };
platforms: { platforms: {
agiso: { agiso: {
appSecret: string appSecret: string;
tradeDetail: { tradeDetail: {
endpoint: string endpoint: string;
apiVersion: string apiVersion: string;
timeoutMs: number timeoutMs: number;
} };
autoDelivery: { autoDelivery: {
enabled: boolean enabled: boolean;
endpoint: string endpoint: string;
apiVersion: string apiVersion: string;
} };
messaging: { messaging: {
enabled: boolean enabled: boolean;
apiVersion: string apiVersion: string;
sendMessageEndpoint: string sendMessageEndpoint: string;
messageTemplate: string messageTemplate: string;
autoDeliveryMessageTemplate: string autoDeliveryMessageTemplate: string;
shops: AgisoMessagingShopsConfig shops: AgisoMessagingShopsConfig;
} };
} };
ninetyone: { ninetyone: {
userId: string userId: string;
secret: string secret: string;
version: string version: string;
shopId: string shopId: string;
shopName: string shopName: string;
timestampToleranceSeconds: number timestampToleranceSeconds: number;
cardsEncoding: string cardsEncoding: string;
} };
cloudtentacles: { cloudtentacles: {
baseUrl: string baseUrl: string;
timeoutMs: number timeoutMs: number;
sendSmsPath: string sendSmsPath: string;
loginPath: string loginPath: string;
userInfoPath: string userInfoPath: string;
assetPath: string assetPath: string;
permissionPath: string permissionPath: string;
categoriesPath: string categoriesPath: string;
skuListPath: string skuListPath: string;
skuBuyPath: string skuBuyPath: string;
skuUsePath: string skuUsePath: string;
knapsackPath: string knapsackPath: string;
vnListPath: string vnListPath: string;
vnAppointPath: string vnAppointPath: string;
vnGenerateLoginCodePath: string vnGenerateLoginCodePath: string;
vnVerifCodePath: string vnVerifCodePath: string;
vnVerifyLoginCodePath: string vnVerifyLoginCodePath: string;
vnBindUrlPath: string vnBindUrlPath: string;
vnBindInfoPath: string vnBindInfoPath: string;
vnBackPath: string vnBackPath: string;
bindUrlTtlSeconds: number bindUrlTtlSeconds: number;
bindUrlProbeIntervalSeconds: number bindUrlProbeIntervalSeconds: number;
bindUrlProbeTimeoutMs: number bindUrlProbeTimeoutMs: number;
bindUrlProbeUserAgent: string bindUrlProbeUserAgent: string;
bindUrlProbeEndpoint: string bindUrlProbeEndpoint: string;
bindUrlProbeChartId: string bindUrlProbeChartId: string;
bindUrlProbeSubChartId: string bindUrlProbeSubChartId: string;
bindUrlProbeIdeToken: string bindUrlProbeIdeToken: string;
bindUrlProbeActivityUrl: string bindUrlProbeActivityUrl: string;
bindUrlProbeReferer: string bindUrlProbeReferer: string;
bindUrlProbeExtraCookie: string bindUrlProbeExtraCookie: string;
publicKeyPem: string publicKeyPem: string;
clientSource: string clientSource: string;
deviceId: string deviceId: string;
deviceType: number deviceType: number;
} };
} };
cors: {
allowedOrigins: string[];
};
redeem: { redeem: {
proofMode: string proofMode: string;
} };
} };