var fs = require('fs') var path = require('path') var merge = require('lodash.merge') function createConfig(basePath = undefined, envPrefix = undefined) { let configDefaults let configLocal let envPrefixUpper = envPrefix ? envPrefix.toUpperCase() + "_" : '' if (basePath) { configDefaults = path.join(basePath, 'config.defaults.json') configLocal = path.join(basePath, 'config.json') } else { configDefaults = path.join(path.dirname(require.main.filename), 'config.defaults.json') configLocal = path.join(path.dirname(require.main.filename), 'config.json') } let config = {} const objectDeepKeys = function (obj) { return Object.keys(obj).filter(key => obj[key] instanceof Object).map(key => objectDeepKeys(obj[key]).map(k => `${key}.${k}`)).reduce((x, y) => x.concat(y), Object.keys(obj)) } const set = function(obj, path, value) { var schema = obj // a moving reference to internal objects within obj var pList = path.split('.') var len = pList.length for(var i = 0; i < len-1; i++) { var elem = pList[i] if( !schema[elem] ) schema[elem] = {} schema = schema[elem] } schema[pList[len-1]] = value } const get = function(obj, path) { var schema = obj // a moving reference to internal objects within obj var pList = path.split('.') var len = pList.length for(var i = 0; i < len-1; i++) { var elem = pList[i] if( !schema[elem] ) schema[elem] = {} schema = schema[elem] } return schema[pList[len-1]] } config._reload = function () { try { fs.accessSync(configDefaults) config = merge(this, JSON.parse(fs.readFileSync(configDefaults, 'utf8'))) } catch(error) { console.log('No File ' + configDefaults) } try { fs.accessSync(configLocal) config = merge(this, JSON.parse(fs.readFileSync(configLocal, 'utf8'))) } catch (error) { console.log('No File ' + configLocal) } let keys = objectDeepKeys(this) for (let index = 0; index < keys.length; index++) { const element = keys[index] let env = process.env[envPrefixUpper + element.toUpperCase().replace(/\./g, '_')] if (env) { env = (env == 'true') ? true : env env = (env == 'false') ? false : env set(this, element, env) } } for (let index = 0; index < keys.length; index++) { const element = keys[index] let value = get(config, element).toString() if (value.startsWith('file:')) { try { set(config, element, fs.readFileSync(value.replace('file:', ''), 'utf8')) } catch (error) { console.error(error.message) } } } } config._show = function () { // clone the config object let config = JSON.parse(JSON.stringify(this)) // remove _reload delete config._reload // remove _show delete config._show // remove _set delete config._set // remove _get delete config._get // redact all nested objects where the key is // 'password' or 'secret' or 'token' or 'key' or 'apiKey' or 'apiToken' or 'apiSecret' // or 'user' or 'username' let keys = objectDeepKeys(config) for (let index = 0; index < keys.length; index++) { const element = keys[index] if (element.toLowerCase().includes('password') || element.toLowerCase().includes('secret') || element.toLowerCase().includes('token') || element.toLowerCase().includes('key') || element.toLowerCase().includes('apikey') || element.toLowerCase().includes('apitoken') || element.toLowerCase().includes('apisecret') || element.toLowerCase().includes('user') || element.toLowerCase().includes('username')) { set(config, element, 'REDACTED') } } return config } config._reload() return config } module.exports = { createConfig };