wo-base-envar/envar-tool.js
2024-02-05 11:36:01 +08:00

240 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const fs = require('fs')
const path = require('path')
const deepmerge = require('deepmerge')
const my = { envar: {} }
module.exports = {
/** 合并 envar files 和 commander parameters 中的环境变量。
* @param envarFiles:
* - 字符串: 导入文件,内容应当是字符串数组,或者对象。
* - 字符串数组: 按顺序导入导入每个文件,后面文件里的变量覆盖前面的。
* - 对象: 直接添加到 global.envar 上。
*/
merge_envar ({
rawEnvar = {},
envarFiles = [
'./envar-base-basic.js',
'./envar-base-basic.gitignore.js',
'./envar-base-dynamic.js',
'./envar-base-dynamic.gitignore.js',
'./envar-base-secret.js',
'./envar-base-secret.gitignore.js',
],
} = {}) {
if (!global.envar) {
global.envar = rawEnvar // 不知为何必须定义成全局变量才能保证多次require只执行一次。
console.info({ _at: new Date().toJSON(), _from: 'merge_envar', about: `<<<<<<<< Configuring [${process.env.NODE_ENV}] Environment <<<<<<<<` }, '\n,')
console.info({ _at: new Date().toJSON(), _from: 'merge_envar', about: '- Loading Configuration Files (读取配置文件)' }, '\n,')
if (typeof envarFiles === 'string') {
// 例如当输入参数为 envarFiles = 'envar-base.js' 里面应当 module.exports 一个数组
if (fs.existsSync(path.resolve(envarFiles))) {
envarFiles = require(path.resolve(envarFiles))
} else {
console.warn({ _at: new Date().toJSON(), _from: 'merge_envar', about: ` - ${envarFiles} is missing.` }, '\n,')
envarFiles = undefined
}
}
if (Array.isArray(envarFiles)) {
for (let configFile of envarFiles) {
if (fs.existsSync(path.resolve(configFile))) {
global.envar = deepmerge(global.envar, require(path.resolve(configFile)))
console.info({ _at: new Date().toJSON(), _from: 'merge_envar', about: ` - ${configFile} is loaded.` }, '\n,')
} else {
console.warn({ _at: new Date().toJSON(), _from: 'merge_envar', about: ` - ${configFile} is missing.` }, '\n,')
}
}
} else if (typeof envarFiles === 'object') {
global.envar = deepmerge(global.envar, envarFiles)
} else {
console.warn({ _at: new Date().toJSON(), _from: 'merge_envar', about: ` - unrecognized envarFiles!` }, '\n,')
}
if (process.argv.length > 2 && Array.isArray(global.envar.commanderOptions)) {
console.info({ _at: new Date().toJSON(), _from: 'merge_envar', about: '- Loading Command Line Parameters (载入命令行参数)' }, '\n,')
const commander = require('commander')
commander.version(global.envar.Base_Version || '0.0.1', '-v, --version') // 默认是 -V。如果要 -v就要加 '-v --version'
for (let [key, param, desc] of global.envar.commanderOptions || []) {
commander.option(param, `${desc} Default = "${global.envar[key]}"`)
}
commander.parse(process.argv)
// console.log({_at:new Date().toJSON(),_from:'merge_envar', about: '- Merging Command Line Parameters into Configuration (把命令行参数值合并入配置)' },'\n,')
for (let key in commander) {
if (!/^_/.test(key) && typeof commander[key] === 'string') {
// commander 自带了一批 _开头的属性过滤掉
global.envar[key] = commander[key]
}
}
delete global.envar.commanderOptions
}
console.log({ _at: new Date().toJSON(), _from: 'merge_envar', about: `>>>>>>>> Configured [${process.env.NODE_ENV}] Environment >>>>>>>>` }, '\n,')
}
return global.envar
},
/* 读取动态配置文件中的环境变量。
*/
get_dynamic_envar ({ dynamicEnvarFiles = ['./envar-base-dynamic.js', './envar-base-dynamic.gitignore.js'] } = {}) {
// config file should be absolute or relative to the node process's dir.
let dynamicEnvar = {}
if (typeof dynamicEnvarFiles === 'string') {
// a file containing more files
if (fs.existsSync(path.resolve(dynamicEnvarFiles))) {
dynamicEnvarFiles = require(path.resolve(dynamicEnvarFiles))
} else {
console.warn({ _at: new Date().toJSON(), _from: 'get_dynamic_envar', about: ` - ${dynamicEnvarFiles} is missing.` }, '\n,')
dynamicEnvarFiles = undefined
}
}
if (Array.isArray(dynamicEnvarFiles)) {
for (let dynamicFile of dynamicEnvarFiles) {
if (fs.existsSync(path.resolve(dynamicFile))) {
delete require.cache[require.resolve(path.resolve(dynamicFile))] // delete require.cache[fullpath] 不起作用,必须要加 require.resolve
dynamicEnvar = deepmerge(dynamicEnvar, require(path.resolve(dynamicFile))) // 在这里其实不需要 deepmerge
console.info({ _at: new Date().toJSON(), _from: 'get_dynamic_envar', about: ` - ${dynamicFile} is parsed.` }, '\n,')
} else {
console.warn({ _at: new Date().toJSON(), _from: 'get_dynamic_envar', about: ` - ${dynamicFile} is missing.` }, '\n,')
}
}
} else if (typeof dynamicEnvarFiles === 'object') {
dynamicEnvar = dynamicEnvarFiles
} else {
console.warn({ _at: new Date().toJSON(), _from: 'get_dynamic_envar', about: ` - unrecognized dynamicEnvarFiles!` }, '\n,')
}
return dynamicEnvar
},
/* 隐藏机密配置文件中的环境变量。
* 需要输出当前环境变量时,必须调用本函数,避免机密信息被输出。
*/
mask_secret_envar ({ rawEnvar, secretEnvarFiles = ['./envar-base-secret.js', './envar-base-secret.gitignore.js'] } = {}) {
let envar = JSON.parse(JSON.stringify(rawEnvar || global.envar)) // 复制一份,避免污染
let secretEnvar = {}
if (typeof secretEnvarFiles === 'string') {
if (fs.existsSync(path.resolve(secretEnvarFiles))) {
secretEnvarFiles = require(path.resolve(secretEnvarFiles))
} else {
console.warn({ _at: new Date().toJSON(), _from: 'mask_secret_envar', about: ` - ${secretEnvarFiles} is missing.` }, '\n,')
secretEnvarFiles = undefined
}
}
console.info({ _at: new Date().toJSON(), _from: 'mask_secret_envar', about: ` - Parsing secretEnvarFiles...` }, '\n,')
if (Array.isArray(secretEnvarFiles)) {
for (let secretFile of secretEnvarFiles) {
if (fs.existsSync(path.resolve(secretFile))) {
secretEnvar = deepmerge(secretEnvar, require(path.resolve(secretFile))) // 在这里其实不需要 deepmerge
console.info({ _at: new Date().toJSON(), _from: 'mask_secret_envar', about: ` - ${secretFile} is parsed.` }, '\n,')
} else {
console.warn({ _at: new Date().toJSON(), _from: 'mask_secret_envar', about: ` - ${secretFile} is missing.` }, '\n,')
}
}
} else if (typeof secretEnvarFiles === 'object') {
secretEnvar = secretEnvarFiles
} else {
console.warn({ _at: new Date().toJSON(), _from: 'mask_secret_envar', about: ` - unrecognized secretEnvarFiles!` }, '\n,')
}
for (let key in secretEnvar) {
envar[key] = '****** confidential ******'
}
return envar
},
// 预制方法
envar_all ({
files = [
'./envar-base-basic.js',
'./envar-base-basic.gitignore.js',
'./envar-base-dynamic.js',
'./envar-base-dynamic.gitignore.js',
'./envar-base-secret.js',
'./envar-base-secret.gitignore.js',
],
} = {}) {
let envar = this.get_envar({ inProcess: false, refresh: true, files })
if (process.argv.length > 2 && Array.isArray(envar.commanderOptions)) {
console.info({ _at: new Date().toJSON(), _from: 'envar_all', about: '- Loading Command Line Parameters (载入命令行参数)' }, '\n,')
const commander = require('commander')
commander.version(envar.Base_Version || '0.0.1', '-v, --version') // 默认是 -V。如果要 -v就要加 '-v --version'
for (let [key, param, desc] of envar.commanderOptions || []) {
commander.option(param, `${desc} Default = "${envar[key]}"`)
}
commander.parse(process.argv)
// console.log({_at:new Date().toJSON(),_from:'envar_all', about: '- Merging Command Line Parameters into Configuration (把命令行参数值合并入配置)' },'\n,')
for (let key in commander) {
if (!/^_/.test(key) && typeof commander[key] === 'string') {
// commander 自带了一批 _开头的属性过滤掉
envar[key] = commander[key]
}
}
delete envar.commanderOptions
}
return envar
},
envar_basic ({ envarKey, files = ['./envar-base-basic.js', './envar-base-basic.gitignore.js'] } = {}) {
return (my['basic'] = this.get_envar({ envarKey, inProcess: false, inCache: true, cachename: 'basic', refresh: false, files }))
},
envar_dynamic ({ envarKey, files = ['./envar-base-dynamic.js', './envar-base-dynamic.gitignore.js'] } = {}) {
return this.get_envar({ envarKey, inProcess: true, inCache: false, refresh: true, files })
},
envar_sesame ({ envarKey, files = ['./envar-base-secret.js', './envar-base-secret.gitignore.js'] } = {}) {
return (my['sesame'] = this.get_envar({ envarKey, inProcess: true, inCache: true, cachename: 'sesame', refresh: false, files }))
},
// 可定制的通用方法
get_envar ({ envarKey, inProcess, inCache, cachename, files, refresh } = {}) {
if (envarKey)
return (
(inProcess && process.env[envarKey]) ||
(inCache && my[cachename || 'envar']?.[envarKey]) ||
(files && this.merge_envar_files({ refresh, files })[envarKey])
)
else return (inCache && my[cachename || 'envar']) || (files && this.merge_envar_files({ refresh, files }))
},
merge_envar_files ({ refresh = false, files } = {}) {
let envar = {}
if (typeof files === 'string') {
// a file containing more files
if (fs.existsSync(path.resolve(files))) {
files = require(path.resolve(files))
} else {
console.warn({ _at: new Date().toJSON(), _from: 'merge_envar_files', about: ` - ${files} is missing.` }, '\n,')
files = undefined
}
}
if (Array.isArray(files)) {
for (let envarFile of files) {
if (fs.existsSync(path.resolve(envarFile))) {
if (refresh) {
delete require.cache[require.resolve(path.resolve(envarFile))] // delete require.cache[fullpath] 不起作用,必须要加 require.resolve
}
envar = deepmerge(envar, require(path.resolve(envarFile)))
console.info({ _at: new Date().toJSON(), _from: 'merge_envar_files', about: ` - ${envarFile} is parsed.` }, '\n,')
} else {
console.warn({ _at: new Date().toJSON(), _from: 'merge_envar_files', about: ` - ${envarFile} is missing.` }, '\n,')
}
}
} else if (typeof files === 'object') {
envar = files
} else {
console.warn({ _at: new Date().toJSON(), _from: 'merge_envar_files', about: ` - unrecognized files!` }, '\n,')
}
return envar
},
}