470 lines
14 KiB
JavaScript
470 lines
14 KiB
JavaScript
// @ts-check
|
|
|
|
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
import process from 'node:process'
|
|
import { createRequire } from 'node:module'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
/** @typedef {import('../types/runtime-config.js').RuntimeConfig} RuntimeConfig */
|
|
|
|
const require = createRequire(import.meta.url)
|
|
const CURRENT_DIR = path.dirname(fileURLToPath(import.meta.url))
|
|
export const PROJECT_ROOT = path.resolve(CURRENT_DIR, '../..')
|
|
const WORKSPACE_ROOT = path.resolve(PROJECT_ROOT, '../..')
|
|
const CONFIG_ROOT = path.join(PROJECT_ROOT, 'config')
|
|
|
|
loadEnvFiles([
|
|
path.join(WORKSPACE_ROOT, '.env'),
|
|
path.join(PROJECT_ROOT, '.env'),
|
|
])
|
|
|
|
const defaultConfig = /** @type {RuntimeConfig} */ (loadConfig(path.join(CONFIG_ROOT, 'default.cjs')))
|
|
|
|
export const runtimeConfig = /** @type {RuntimeConfig} */ (applyEnvOverrides(defaultConfig))
|
|
|
|
function loadConfig(configPath) {
|
|
if (!fs.existsSync(configPath)) {
|
|
return {}
|
|
}
|
|
|
|
const loaded = require(configPath)
|
|
return isPlainObject(loaded) ? loaded : {}
|
|
}
|
|
|
|
function loadEnvFiles(filePaths) {
|
|
for (const filePath of filePaths) {
|
|
loadEnvFile(filePath)
|
|
}
|
|
}
|
|
|
|
function loadEnvFile(filePath) {
|
|
if (!fs.existsSync(filePath)) {
|
|
return
|
|
}
|
|
|
|
const rawText = fs.readFileSync(filePath, 'utf8')
|
|
const lines = rawText.split(/\r?\n/)
|
|
|
|
for (const rawLine of lines) {
|
|
const line = rawLine.trim()
|
|
|
|
if (!line || line.startsWith('#')) {
|
|
continue
|
|
}
|
|
|
|
const separatorIndex = line.indexOf('=')
|
|
if (separatorIndex <= 0) {
|
|
continue
|
|
}
|
|
|
|
const key = line.slice(0, separatorIndex).trim()
|
|
if (!key || key in process.env) {
|
|
continue
|
|
}
|
|
|
|
process.env[key] = parseEnvValue(line.slice(separatorIndex + 1))
|
|
}
|
|
}
|
|
|
|
function parseEnvValue(rawValue) {
|
|
const value = String(rawValue || '').trim()
|
|
|
|
if (!value) {
|
|
return ''
|
|
}
|
|
|
|
const quote = value[0]
|
|
if ((quote === '"' || quote === "'") && value.endsWith(quote)) {
|
|
return value.slice(1, -1)
|
|
}
|
|
|
|
return value
|
|
}
|
|
|
|
function applyEnvOverrides(baseConfig) {
|
|
const nextConfig = deepMerge(baseConfig, {})
|
|
|
|
const port = parseInteger(process.env.PORT)
|
|
if (port !== null) {
|
|
nextConfig.server.port = port
|
|
}
|
|
|
|
const chromePath = String(process.env.CHROME_PATH || '').trim()
|
|
if (chromePath) {
|
|
nextConfig.browser.chromePath = chromePath
|
|
}
|
|
|
|
const browserHeadless = parseBoolean(process.env.TENCENT_BROWSER_HEADLESS)
|
|
if (browserHeadless !== null) {
|
|
nextConfig.browser.headless = browserHeadless
|
|
}
|
|
|
|
const browserDevtools = parseBoolean(process.env.TENCENT_BROWSER_DEVTOOLS)
|
|
if (browserDevtools !== null) {
|
|
nextConfig.browser.devtools = browserDevtools
|
|
}
|
|
|
|
const browserKeepAlive = parseBoolean(process.env.TENCENT_BROWSER_KEEP_ALIVE)
|
|
if (browserKeepAlive !== null) {
|
|
nextConfig.browser.keepAlive = browserKeepAlive
|
|
}
|
|
|
|
const browserPrewarm = parseBoolean(process.env.TENCENT_BROWSER_PREWARM)
|
|
if (browserPrewarm !== null) {
|
|
nextConfig.browser.prewarm = browserPrewarm
|
|
}
|
|
|
|
const browserSlowMoMs = parseInteger(process.env.TENCENT_BROWSER_SLOW_MO)
|
|
if (browserSlowMoMs !== null) {
|
|
nextConfig.browser.slowMoMs = browserSlowMoMs
|
|
}
|
|
|
|
const sessionDebug = parseBoolean(process.env.TENCENT_SESSION_DEBUG)
|
|
if (sessionDebug !== null) {
|
|
nextConfig.session.debug = sessionDebug
|
|
}
|
|
|
|
const ocrProjectRoot = String(process.env.OCR_PROJECT_ROOT || '').trim()
|
|
if (ocrProjectRoot) {
|
|
nextConfig.ocr.projectRoot = ocrProjectRoot
|
|
}
|
|
|
|
const dataRoot = String(process.env.DATA_ROOT || '').trim()
|
|
if (dataRoot) {
|
|
nextConfig.data.root = path.resolve(dataRoot)
|
|
}
|
|
|
|
const logLevel = String(process.env.LOG_LEVEL || '').trim()
|
|
if (logLevel) {
|
|
nextConfig.logging.level = logLevel
|
|
}
|
|
|
|
const databaseUrl = String(process.env.DATABASE_URL || '').trim()
|
|
if (databaseUrl) {
|
|
nextConfig.database.url = databaseUrl
|
|
}
|
|
|
|
const databaseSsl = parseBoolean(process.env.DATABASE_SSL)
|
|
if (databaseSsl !== null) {
|
|
nextConfig.database.ssl = databaseSsl
|
|
}
|
|
|
|
const databaseMaxConnections = parseInteger(process.env.DATABASE_MAX_CONNECTIONS)
|
|
if (databaseMaxConnections !== null) {
|
|
nextConfig.database.maxConnections = databaseMaxConnections
|
|
}
|
|
|
|
const claimBaseUrl = String(process.env.CLAIM_BASE_URL || '').trim()
|
|
if (claimBaseUrl) {
|
|
nextConfig.orders.claimBaseUrl = claimBaseUrl
|
|
} else {
|
|
const appBaseUrl = String(process.env.APP_BASE_URL || '').trim()
|
|
if (appBaseUrl) {
|
|
nextConfig.orders.claimBaseUrl = `${appBaseUrl.replace(/\/+$/, '')}/#/claim`
|
|
}
|
|
}
|
|
|
|
const tokenTtlHours = parseInteger(process.env.CLAIM_TOKEN_TTL_HOURS)
|
|
if (tokenTtlHours !== null) {
|
|
nextConfig.orders.tokenTtlHours = tokenTtlHours
|
|
}
|
|
|
|
const adminSessionSecret = String(process.env.ADMIN_SESSION_SECRET || '').trim()
|
|
if (adminSessionSecret) {
|
|
nextConfig.admin.sessionSecret = adminSessionSecret
|
|
}
|
|
|
|
const adminSessionTtlHours = parseInteger(process.env.ADMIN_SESSION_TTL_HOURS)
|
|
if (adminSessionTtlHours !== null) {
|
|
nextConfig.admin.sessionTtlHours = adminSessionTtlHours
|
|
}
|
|
|
|
const adminDefaultUsers = parseJsonArray(process.env.ADMIN_DEFAULT_USERS_JSON)
|
|
if (adminDefaultUsers) {
|
|
nextConfig.admin.defaultUsers = adminDefaultUsers
|
|
}
|
|
|
|
const agisoAppSecret = String(process.env.AGISO_APP_SECRET || '').trim()
|
|
if (agisoAppSecret) {
|
|
nextConfig.platforms.agiso.appSecret = agisoAppSecret
|
|
}
|
|
|
|
const agisoTradeDetailEndpoint = String(process.env.AGISO_TRADE_DETAIL_ENDPOINT || '').trim()
|
|
if (agisoTradeDetailEndpoint) {
|
|
nextConfig.platforms.agiso.tradeDetail.endpoint = agisoTradeDetailEndpoint
|
|
}
|
|
|
|
const agisoTradeDetailApiVersion = String(process.env.AGISO_TRADE_DETAIL_API_VERSION || '').trim()
|
|
if (agisoTradeDetailApiVersion) {
|
|
nextConfig.platforms.agiso.tradeDetail.apiVersion = agisoTradeDetailApiVersion
|
|
}
|
|
|
|
const agisoTradeDetailTimeoutMs = parseInteger(process.env.AGISO_TRADE_DETAIL_TIMEOUT_MS)
|
|
if (agisoTradeDetailTimeoutMs !== null) {
|
|
nextConfig.platforms.agiso.tradeDetail.timeoutMs = agisoTradeDetailTimeoutMs
|
|
}
|
|
|
|
const agisoAutoDeliveryEnabled = parseBoolean(process.env.AGISO_AUTO_DELIVERY_ENABLED)
|
|
if (agisoAutoDeliveryEnabled !== null) {
|
|
nextConfig.platforms.agiso.autoDelivery.enabled = agisoAutoDeliveryEnabled
|
|
}
|
|
|
|
const agisoAutoDeliveryEndpoint = String(process.env.AGISO_AUTO_DELIVERY_ENDPOINT || '').trim()
|
|
if (agisoAutoDeliveryEndpoint) {
|
|
nextConfig.platforms.agiso.autoDelivery.endpoint = agisoAutoDeliveryEndpoint
|
|
}
|
|
|
|
const agisoAutoDeliveryApiVersion = String(process.env.AGISO_AUTO_DELIVERY_API_VERSION || '').trim()
|
|
if (agisoAutoDeliveryApiVersion) {
|
|
nextConfig.platforms.agiso.autoDelivery.apiVersion = agisoAutoDeliveryApiVersion
|
|
}
|
|
|
|
const agisoMessageApiVersion = String(process.env.AGISO_MESSAGE_API_VERSION || '').trim()
|
|
if (agisoMessageApiVersion) {
|
|
nextConfig.platforms.agiso.messaging.apiVersion = agisoMessageApiVersion
|
|
}
|
|
|
|
const agisoSendMessageEndpoint = String(process.env.AGISO_SEND_MESSAGE_ENDPOINT || '').trim()
|
|
if (agisoSendMessageEndpoint) {
|
|
nextConfig.platforms.agiso.messaging.sendMessageEndpoint = agisoSendMessageEndpoint
|
|
}
|
|
|
|
const agisoMessagingEnabled = parseBoolean(process.env.AGISO_MESSAGING_ENABLED)
|
|
if (agisoMessagingEnabled !== null) {
|
|
nextConfig.platforms.agiso.messaging.enabled = agisoMessagingEnabled
|
|
}
|
|
|
|
const khhaoBaseUrl = String(process.env.KHHAO_BASE_URL || '').trim()
|
|
if (khhaoBaseUrl) {
|
|
nextConfig.platforms.khhao.baseUrl = khhaoBaseUrl
|
|
}
|
|
|
|
const khhaoTimeoutMs = parseInteger(process.env.KHHAO_TIMEOUT_MS)
|
|
if (khhaoTimeoutMs !== null) {
|
|
nextConfig.platforms.khhao.timeoutMs = khhaoTimeoutMs
|
|
}
|
|
|
|
const khhaoLoginPath = String(process.env.KHHAO_LOGIN_PATH || '').trim()
|
|
if (khhaoLoginPath) {
|
|
nextConfig.platforms.khhao.loginPath = khhaoLoginPath
|
|
}
|
|
|
|
const khhaoCaptchaPath = String(process.env.KHHAO_CAPTCHA_PATH || '').trim()
|
|
if (khhaoCaptchaPath) {
|
|
nextConfig.platforms.khhao.captchaPath = khhaoCaptchaPath
|
|
}
|
|
|
|
const khhaoOrderListPath = String(process.env.KHHAO_ORDER_LIST_PATH || '').trim()
|
|
if (khhaoOrderListPath) {
|
|
nextConfig.platforms.khhao.orderListPath = khhaoOrderListPath
|
|
}
|
|
|
|
const cloudtentaclesBaseUrl = String(process.env.CLOUDTENTACLES_BASE_URL || '').trim()
|
|
if (cloudtentaclesBaseUrl) {
|
|
nextConfig.platforms.cloudtentacles.baseUrl = cloudtentaclesBaseUrl
|
|
}
|
|
|
|
const cloudtentaclesTimeoutMs = parseInteger(process.env.CLOUDTENTACLES_TIMEOUT_MS)
|
|
if (cloudtentaclesTimeoutMs !== null) {
|
|
nextConfig.platforms.cloudtentacles.timeoutMs = cloudtentaclesTimeoutMs
|
|
}
|
|
|
|
const cloudtentaclesSendSmsPath = String(process.env.CLOUDTENTACLES_SEND_SMS_PATH || '').trim()
|
|
if (cloudtentaclesSendSmsPath) {
|
|
nextConfig.platforms.cloudtentacles.sendSmsPath = cloudtentaclesSendSmsPath
|
|
}
|
|
|
|
const cloudtentaclesLoginPath = String(process.env.CLOUDTENTACLES_LOGIN_PATH || '').trim()
|
|
if (cloudtentaclesLoginPath) {
|
|
nextConfig.platforms.cloudtentacles.loginPath = cloudtentaclesLoginPath
|
|
}
|
|
|
|
const cloudtentaclesUserInfoPath = String(process.env.CLOUDTENTACLES_USER_INFO_PATH || '').trim()
|
|
if (cloudtentaclesUserInfoPath) {
|
|
nextConfig.platforms.cloudtentacles.userInfoPath = cloudtentaclesUserInfoPath
|
|
}
|
|
|
|
const cloudtentaclesAssetPath = String(process.env.CLOUDTENTACLES_ASSET_PATH || '').trim()
|
|
if (cloudtentaclesAssetPath) {
|
|
nextConfig.platforms.cloudtentacles.assetPath = cloudtentaclesAssetPath
|
|
}
|
|
|
|
const cloudtentaclesPermissionPath = String(process.env.CLOUDTENTACLES_PERMISSION_PATH || '').trim()
|
|
if (cloudtentaclesPermissionPath) {
|
|
nextConfig.platforms.cloudtentacles.permissionPath = cloudtentaclesPermissionPath
|
|
}
|
|
|
|
const cloudtentaclesCategoriesPath = String(process.env.CLOUDTENTACLES_CATEGORIES_PATH || '').trim()
|
|
if (cloudtentaclesCategoriesPath) {
|
|
nextConfig.platforms.cloudtentacles.categoriesPath = cloudtentaclesCategoriesPath
|
|
}
|
|
|
|
const cloudtentaclesSkuListPath = String(process.env.CLOUDTENTACLES_SKU_LIST_PATH || '').trim()
|
|
if (cloudtentaclesSkuListPath) {
|
|
nextConfig.platforms.cloudtentacles.skuListPath = cloudtentaclesSkuListPath
|
|
}
|
|
|
|
const cloudtentaclesSkuBuyPath = String(process.env.CLOUDTENTACLES_SKU_BUY_PATH || '').trim()
|
|
if (cloudtentaclesSkuBuyPath) {
|
|
nextConfig.platforms.cloudtentacles.skuBuyPath = cloudtentaclesSkuBuyPath
|
|
}
|
|
|
|
const cloudtentaclesKnapsackPath = String(process.env.CLOUDTENTACLES_KNAPSACK_PATH || '').trim()
|
|
if (cloudtentaclesKnapsackPath) {
|
|
nextConfig.platforms.cloudtentacles.knapsackPath = cloudtentaclesKnapsackPath
|
|
}
|
|
|
|
const cloudtentaclesVnListPath = String(process.env.CLOUDTENTACLES_VN_LIST_PATH || '').trim()
|
|
if (cloudtentaclesVnListPath) {
|
|
nextConfig.platforms.cloudtentacles.vnListPath = cloudtentaclesVnListPath
|
|
}
|
|
|
|
const cloudtentaclesVnAppointPath = String(process.env.CLOUDTENTACLES_VN_APPOINT_PATH || '').trim()
|
|
if (cloudtentaclesVnAppointPath) {
|
|
nextConfig.platforms.cloudtentacles.vnAppointPath = cloudtentaclesVnAppointPath
|
|
}
|
|
|
|
const cloudtentaclesVnGenerateLoginCodePath = String(process.env.CLOUDTENTACLES_VN_GENERATE_LOGIN_CODE_PATH || '').trim()
|
|
if (cloudtentaclesVnGenerateLoginCodePath) {
|
|
nextConfig.platforms.cloudtentacles.vnGenerateLoginCodePath = cloudtentaclesVnGenerateLoginCodePath
|
|
}
|
|
|
|
const cloudtentaclesVnVerifCodePath = String(process.env.CLOUDTENTACLES_VN_VERIF_CODE_PATH || '').trim()
|
|
if (cloudtentaclesVnVerifCodePath) {
|
|
nextConfig.platforms.cloudtentacles.vnVerifCodePath = cloudtentaclesVnVerifCodePath
|
|
}
|
|
|
|
const cloudtentaclesVnVerifyLoginCodePath = String(process.env.CLOUDTENTACLES_VN_VERIFY_LOGIN_CODE_PATH || '').trim()
|
|
if (cloudtentaclesVnVerifyLoginCodePath) {
|
|
nextConfig.platforms.cloudtentacles.vnVerifyLoginCodePath = cloudtentaclesVnVerifyLoginCodePath
|
|
}
|
|
|
|
const cloudtentaclesVnBindUrlPath = String(process.env.CLOUDTENTACLES_VN_BIND_URL_PATH || '').trim()
|
|
if (cloudtentaclesVnBindUrlPath) {
|
|
nextConfig.platforms.cloudtentacles.vnBindUrlPath = cloudtentaclesVnBindUrlPath
|
|
}
|
|
|
|
const cloudtentaclesPublicKeyPem = String(process.env.CLOUDTENTACLES_PUBLIC_KEY_PEM || '').trim()
|
|
if (cloudtentaclesPublicKeyPem) {
|
|
nextConfig.platforms.cloudtentacles.publicKeyPem = cloudtentaclesPublicKeyPem
|
|
}
|
|
|
|
const cloudtentaclesClientSource = String(process.env.CLOUDTENTACLES_CLIENT_SOURCE || '').trim()
|
|
if (cloudtentaclesClientSource) {
|
|
nextConfig.platforms.cloudtentacles.clientSource = cloudtentaclesClientSource
|
|
}
|
|
|
|
const cloudtentaclesDeviceId = String(process.env.CLOUDTENTACLES_DEVICE_ID || '').trim()
|
|
if (cloudtentaclesDeviceId) {
|
|
nextConfig.platforms.cloudtentacles.deviceId = cloudtentaclesDeviceId
|
|
}
|
|
|
|
const cloudtentaclesDeviceType = parseInteger(process.env.CLOUDTENTACLES_DEVICE_TYPE)
|
|
if (cloudtentaclesDeviceType !== null) {
|
|
nextConfig.platforms.cloudtentacles.deviceType = cloudtentaclesDeviceType
|
|
}
|
|
|
|
const proofMode = String(process.env.TENCENT_REDEEM_PROOF_MODE || '').trim()
|
|
if (proofMode) {
|
|
nextConfig.redeem.proofMode = proofMode
|
|
}
|
|
|
|
return nextConfig
|
|
}
|
|
|
|
function deepMerge(baseValue, overrideValue) {
|
|
if (!isPlainObject(baseValue)) {
|
|
return cloneValue(overrideValue)
|
|
}
|
|
|
|
const result = cloneValue(baseValue)
|
|
|
|
if (!isPlainObject(overrideValue)) {
|
|
return result
|
|
}
|
|
|
|
for (const [key, value] of Object.entries(overrideValue)) {
|
|
if (isPlainObject(value) && isPlainObject(result[key])) {
|
|
result[key] = deepMerge(result[key], value)
|
|
continue
|
|
}
|
|
|
|
result[key] = cloneValue(value)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
function cloneValue(value) {
|
|
if (Array.isArray(value)) {
|
|
return value.map((item) => cloneValue(item))
|
|
}
|
|
|
|
if (isPlainObject(value)) {
|
|
const output = {}
|
|
for (const [key, item] of Object.entries(value)) {
|
|
output[key] = cloneValue(item)
|
|
}
|
|
return output
|
|
}
|
|
|
|
return value
|
|
}
|
|
|
|
function isPlainObject(value) {
|
|
return Object.prototype.toString.call(value) === '[object Object]'
|
|
}
|
|
|
|
function parseBoolean(rawValue) {
|
|
const normalized = String(rawValue || '').trim().toLowerCase()
|
|
|
|
if (!normalized) {
|
|
return null
|
|
}
|
|
|
|
if (['1', 'true', 'yes', 'on'].includes(normalized)) {
|
|
return true
|
|
}
|
|
|
|
if (['0', 'false', 'no', 'off'].includes(normalized)) {
|
|
return false
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
function parseInteger(rawValue) {
|
|
const normalized = String(rawValue || '').trim()
|
|
|
|
if (!normalized) {
|
|
return null
|
|
}
|
|
|
|
const parsed = Number(normalized)
|
|
return Number.isFinite(parsed) ? parsed : null
|
|
}
|
|
|
|
function parseJsonArray(rawValue) {
|
|
const normalized = String(rawValue || '').trim()
|
|
|
|
if (!normalized) {
|
|
return null
|
|
}
|
|
|
|
try {
|
|
const parsed = JSON.parse(normalized)
|
|
return Array.isArray(parsed) ? parsed : null
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
function normalizeBooleanLike(value) {
|
|
if (typeof value === 'boolean') {
|
|
return value
|
|
}
|
|
|
|
return parseBoolean(value)
|
|
}
|