425 lines
12 KiB
JavaScript
425 lines
12 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').AgisoMessagingShopsConfig} AgisoMessagingShopsConfig */
|
|
/** @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 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 agisoAutoDeliveryAldsType = parseInteger(process.env.AGISO_AUTO_DELIVERY_ALDS_TYPE)
|
|
if (agisoAutoDeliveryAldsType !== null) {
|
|
nextConfig.platforms.agiso.autoDelivery.aldsType = agisoAutoDeliveryAldsType
|
|
}
|
|
|
|
for (const [envKey, configKey] of [
|
|
['AGISO_AUTO_DELIVERY_IGNORE_ALDS_LOG', 'ignoreAldsLog'],
|
|
['AGISO_AUTO_DELIVERY_IGNORE_BLACK_LIST', 'ignoreBlackList'],
|
|
['AGISO_AUTO_DELIVERY_IGNORE_ON_OFF', 'ignoreOnOff'],
|
|
['AGISO_AUTO_DELIVERY_IGNORE_REFUND_CHECK', 'ignoreRefundCheck'],
|
|
['AGISO_AUTO_DELIVERY_IGNORE_RESTRICTED', 'ignoreRestricted'],
|
|
['AGISO_AUTO_DELIVERY_IGNORE_TRADE_STATUS_CHECK', 'ignoreTradeStatusCheck'],
|
|
]) {
|
|
const parsed = parseBoolean(process.env[envKey])
|
|
if (parsed !== null) {
|
|
nextConfig.platforms.agiso.autoDelivery[configKey] = parsed
|
|
}
|
|
}
|
|
|
|
const agisoAppId = String(process.env.AGISO_APP_ID || '').trim()
|
|
if (agisoAppId) {
|
|
nextConfig.platforms.agiso.messaging.appId = agisoAppId
|
|
}
|
|
|
|
const agisoAccessToken = String(process.env.AGISO_ACCESS_TOKEN || '').trim()
|
|
if (agisoAccessToken) {
|
|
nextConfig.platforms.agiso.messaging.accessToken = agisoAccessToken
|
|
}
|
|
|
|
const agisoMessageAppSecret = String(process.env.AGISO_MESSAGE_APP_SECRET || '').trim()
|
|
if (agisoMessageAppSecret) {
|
|
nextConfig.platforms.agiso.messaging.appSecret = agisoMessageAppSecret
|
|
}
|
|
|
|
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 agisoMessageTemplate = String(process.env.AGISO_MESSAGE_TEMPLATE || '').trim()
|
|
if (agisoMessageTemplate) {
|
|
nextConfig.platforms.agiso.messaging.messageTemplate = agisoMessageTemplate
|
|
}
|
|
|
|
const agisoShops = parseJsonObject(process.env.AGISO_SHOPS_JSON)
|
|
if (agisoShops) {
|
|
nextConfig.platforms.agiso.messaging.shops = normalizeAgisoMessagingShops(agisoShops)
|
|
}
|
|
|
|
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 parseJsonObject(rawValue) {
|
|
const normalized = String(rawValue || '').trim()
|
|
|
|
if (!normalized) {
|
|
return null
|
|
}
|
|
|
|
try {
|
|
const parsed = JSON.parse(normalized)
|
|
return isPlainObject(parsed) ? parsed : null
|
|
} catch {
|
|
return 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 normalizeAgisoMessagingShops(rawValue) {
|
|
/** @type {AgisoMessagingShopsConfig} */
|
|
const output = {}
|
|
|
|
for (const [shopId, config] of Object.entries(rawValue || {})) {
|
|
const normalizedShopId = String(shopId || '').trim()
|
|
if (!normalizedShopId || !isPlainObject(config)) {
|
|
continue
|
|
}
|
|
|
|
/** @type {AgisoMessagingShopsConfig[string]} */
|
|
const next = {}
|
|
const enabled = normalizeBooleanLike(config.enabled)
|
|
if (enabled !== null) {
|
|
next.enabled = enabled
|
|
}
|
|
|
|
for (const key of ['shopName', 'accessToken', 'messageTemplate', 'appSecret', 'apiVersion', 'sendMessageEndpoint']) {
|
|
const value = String(config[key] || '').trim()
|
|
if (value) {
|
|
next[key] = value
|
|
}
|
|
}
|
|
|
|
output[normalizedShopId] = next
|
|
}
|
|
|
|
return output
|
|
}
|
|
|
|
function normalizeBooleanLike(value) {
|
|
if (typeof value === 'boolean') {
|
|
return value
|
|
}
|
|
|
|
return parseBoolean(value)
|
|
}
|