From b6ed7da0a7a5e30bc9ff4f3a8d0e113ae2c53e11 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 4 Jun 2019 18:49:03 +0300 Subject: [PATCH] fix(config) configManager: totally move away from singletone --- HELP.md | 15 ++++-- README.md | 10 ++++ bin/cloudcmd.js | 19 +++++-- server/cloudcmd.js | 14 +++-- server/config.js | 88 +++++++++++--------------------- server/config.spec.js | 14 ++--- server/distribute/export.spec.js | 3 +- server/distribute/import.spec.js | 41 ++++++++------- server/distribute/log.spec.js | 4 +- server/route.js | 9 ++-- server/route.spec.js | 6 ++- server/server.js | 8 +-- test/before.js | 2 + test/rest/pack.js | 5 +- test/server/console.js | 2 +- test/static.js | 2 +- 16 files changed, 127 insertions(+), 115 deletions(-) diff --git a/HELP.md b/HELP.md index c5b737e2..0ddf5ccc 100644 --- a/HELP.md +++ b/HELP.md @@ -671,8 +671,6 @@ const app = require('express')(); const port = 1337; const prefix = '/'; -const {createConfigManager} = cloudcmd; - const server = http.createServer(app); const socket = io.listen(server, { path: `{prefix}socket.io` @@ -699,13 +697,22 @@ const modules = { filePicker, }; +const { + createConfigManager, + configPath, +} = cloudcmd; + +const configManager = createConfigManager({ + configPath, +}), + app.use(prefix, cloudcmd({ socket, // used by Config, Edit (optional) and Console (required) config, // config data (optional) plugins, // DEPRECATED, use User Menu instead modules, // optional - configManager: createConfigManager(), //optional -})); + configManager, // optional +)); server.listen(port); ``` diff --git a/README.md b/README.md index 7bfe875c..33ad49a4 100644 --- a/README.md +++ b/README.md @@ -107,11 +107,21 @@ const modules = { filePicker, }; +const { + createConfigManager, + configPath, +} = cloudcmd; + +const configManager = createConfigManager({ + configPath, +}), + app.use(prefix, cloudcmd({ socket, // used by Config, Edit (optional) and Console (required) config, // config data (optional) plugins, // DEPRECATED, use User Menu instead modules, // optional + configManager, // optional })); server.listen(port); diff --git a/bin/cloudcmd.js b/bin/cloudcmd.js index ce3e0eaf..afda5cb8 100755 --- a/bin/cloudcmd.js +++ b/bin/cloudcmd.js @@ -6,10 +6,19 @@ const Info = require('../package'); const DIR_SERVER = '../server/'; const {promisify} = require('util'); + const wraptile = require('wraptile'); const exit = require(DIR_SERVER + 'exit'); -const config = require(DIR_SERVER + 'config'); +const { + createConfig, + configPath, +} = require(DIR_SERVER + 'config'); + +const config = createConfig({ + configPath, +}); + const env = require(DIR_SERVER + 'env'); const prefixer = require(DIR_SERVER + '/prefixer'); @@ -208,14 +217,14 @@ function main() { if (args['show-config']) showConfig(); - const startWraped = wraptile(start, options); + const startWraped = wraptile(start, options, config); const distribute = require('../server/distribute'); const importConfig = promisify(distribute.import); const caller = (fn) => fn(); importConfig(config) .then(args.save ? caller(config.save) : noop) - .then(startWraped(options)); + .then(startWraped); } function validateRoot(root, config) { @@ -237,14 +246,14 @@ function version() { console.log('v' + Info.version); } -function start(config) { +function start(options, config) { const SERVER = DIR_SERVER + 'server'; if (!args.server) return; const server = require(SERVER); - server(config); + server(options, config); } function port(arg) { diff --git a/server/cloudcmd.js b/server/cloudcmd.js index e49b3ba4..e878ea15 100644 --- a/server/cloudcmd.js +++ b/server/cloudcmd.js @@ -9,7 +9,11 @@ const fs = require('fs'); const cloudfunc = require(DIR_COMMON + 'cloudfunc'); const authentication = require(DIR + 'auth'); -const defaultConfig = require(DIR + 'config'); +const { + createConfig, + configPath, +} = require(DIR + 'config'); + const modulas = require(DIR + 'modulas'); const userMenu = require(DIR + 'user-menu'); const rest = require(DIR + 'rest'); @@ -45,7 +49,9 @@ const clean = (a) => a.filter(notEmpty); module.exports = (params) => { const p = params || {}; const options = p.config || {}; - const config = p.configManager || defaultConfig; + const config = p.configManager || createConfig({ + configPath, + }); const { modules, @@ -90,8 +96,8 @@ module.exports = (params) => { }); }; -module.exports.createConfigManager = defaultConfig.create; -module.exports.configPath = defaultConfig.path; +module.exports.createConfigManager = createConfig; +module.exports.configPath = configPath; module.exports._getIndexPath = getIndexPath; diff --git a/server/config.js b/server/config.js index 09874931..4927f2fe 100644 --- a/server/config.js +++ b/server/config.js @@ -29,28 +29,29 @@ const resolve = Promise.resolve.bind(Promise); const formatMsg = currify((a, b) => CloudFunc.formatMsg(a, b)); const {apiURL} = CloudFunc; -const changeEmitter = new Emitter(); const key = (a) => Object.keys(a).pop(); const ConfigPath = path.join(DIR, 'json/config.json'); const ConfigHome = path.join(HOME, '.cloudcmd.json'); -const config = read(); - const connection = currify(_connection); const connectionWraped = wraptile(_connection); const middle = currify(_middle); -function read(filename = ConfigHome) { - const readjsonSync = (name) => { - return jju.parse(fs.readFileSync(name, 'utf8'), { - mode: 'json', - }); - }; +const readjsonSync = (name) => { + return jju.parse(fs.readFileSync(name, 'utf8'), { + mode: 'json', + }); +}; + +const rootConfig = readjsonSync(ConfigPath); + +function read(filename) { + if (!filename) + return rootConfig; - const rootConfig = readjsonSync(ConfigPath); - const [error, configHome] = tryCatch(readjsonSync, ConfigHome); + const [error, configHome] = tryCatch(readjsonSync, filename); if (error && error.code !== 'ENOENT') exit(`cloudcmd --config ${filename}: ${error.message}`); @@ -61,18 +62,8 @@ function read(filename = ConfigHome) { }; } -module.exports = manage; -module.exports.create = create; -module.exports.middle = middle(manage); -module.exports.subscribe = (fn) => { - changeEmitter.on('change', fn); -}; - -module.exports.path = ConfigHome; - -module.exports.unsubscribe = (fn) => { - changeEmitter.removeListener('change', fn); -}; +module.exports.createConfig = createConfig; +module.exports.configPath = ConfigHome; const manageListen = currify((manage, socket, auth) => { if (!manage('configDialog')) @@ -83,23 +74,6 @@ const manageListen = currify((manage, socket, auth) => { return middle; }); -function manage(key, value) { - if (!key) - return; - - if (key === '*') - return config; - - if (value === undefined) - return config[key]; - - config[key] = value; - - changeEmitter.emit('change', key, value); - - return `${key} = ${value}`; -} - function initWrite(filename, configManager) { if (filename) return write.bind(null, filename, configManager); @@ -107,15 +81,9 @@ function initWrite(filename, configManager) { return resolve; } -function readConfig(filename) { - if (filename) - return read(filename); - - return config; -} - -function create({filename} = {}) { +function createConfig({configPath} = {}) { const config = {}; + const changeEmitter = new Emitter(); const configManager = (key, value) => { if (key === '*') @@ -127,26 +95,28 @@ function create({filename} = {}) { return config[key]; config[key] = value; + changeEmitter.emit('change', key, value); + + return `${key} = ${value}`; }; - spread(configManager); - Object.assign(config, readConfig(filename)); + Object.assign(config, read(configPath)); configManager.middle = middle(configManager); configManager.listen = manageListen(configManager); - configManager.write = initWrite(filename, configManager); + configManager.write = initWrite(configPath, configManager); + configManager.subscribe = (fn) => { + changeEmitter.on('change', fn); + }; + + configManager.unsubscribe = (fn) => { + // replace to off on node v10 + changeEmitter.removeListener('change', fn); + }; return configManager; } -function spread(store) { - const entries = Object.entries(config); - - for (const [name, value] of entries) { - store(name, value); - } -} - const write = async (filename, config) => { return writejson(filename, config('*'), {mode: 0o600}); }; diff --git a/server/config.spec.js b/server/config.spec.js index 2d6a500b..a9c6ad69 100644 --- a/server/config.spec.js +++ b/server/config.spec.js @@ -6,11 +6,11 @@ const stub = require('@cloudcmd/stub'); const root = '../'; const configPath = './config'; -const config = require(configPath); const { + createConfig, _cryptoPass, - create, -} = config; +} = require(configPath); +const config = createConfig(); const {apiURL} = require(root + 'common/cloudfunc'); @@ -24,14 +24,16 @@ test('config: manage', (t) => { test('config: manage: get', async (t) => { const editor = 'deepword'; + const configManager = createConfig(); const {done} = await connect({ config: {editor}, + configManager, }); done(); - t.equal(config('editor'), editor, 'should get config'); + t.equal(configManager('editor'), editor, 'should get config'); t.end(); }); @@ -63,7 +65,7 @@ test('config: cryptoPass: no password', (t) => { hello: 'world', }; - const config = create(); + const config = createConfig(); const result = _cryptoPass(config, json); t.deepEqual(result, [config, json], 'should not change json'); @@ -81,7 +83,7 @@ test('config: cryptoPass', (t) => { password, }; - const config = create(); + const config = createConfig(); const result = _cryptoPass(config, json); t.deepEqual(result, [config, expected], 'should crypt password'); diff --git a/server/distribute/export.spec.js b/server/distribute/export.spec.js index d984adaf..c828415a 100644 --- a/server/distribute/export.spec.js +++ b/server/distribute/export.spec.js @@ -4,7 +4,7 @@ const test = require('supertape'); const io = require('socket.io-client'); const {connect} = require('../../test/before'); -const config = require('../config'); +const config = require('../config').createConfig(); test('distribute: export', async (t) => { const defaultConfig = { @@ -17,6 +17,7 @@ test('distribute: export', async (t) => { const {port, done} = await connect({ config: defaultConfig, + configManager: config, }); const url = `http://localhost:${port}/distribute?port=${1111}`; diff --git a/server/distribute/import.spec.js b/server/distribute/import.spec.js index 76938248..2b77c52a 100644 --- a/server/distribute/import.spec.js +++ b/server/distribute/import.spec.js @@ -50,7 +50,9 @@ test('distribute: import: received: no error', async (t) => { }); test('distribute: import: received', async (t) => { + const configManager = createConfigManager(); const {done, port} = await connect({ + configManager, config: { name: 'bill', import: true, @@ -62,10 +64,9 @@ test('distribute: import: received', async (t) => { }, }); - const config = createConfigManager(); - config('importUrl', `http://localhost:${port}`); + configManager('importUrl', `http://localhost:${port}`); - const {status} = await distribute.import(config); + const {status} = await distribute.import(configManager); await done(); t.equal(status, 'received','should equal'); @@ -73,7 +74,9 @@ test('distribute: import: received', async (t) => { }); test('distribute: import: received: auth: reject', async (t) => { + const configManager = createConfigManager(); const {done, port} = await connect({ + configManager, config: { name: 'bill', import: true, @@ -85,10 +88,9 @@ test('distribute: import: received: auth: reject', async (t) => { }, }); - const config = createConfigManager(); - config('importUrl', `http://localhost:${port}`); + configManager('importUrl', `http://localhost:${port}`); - const {status} = await distribute.import(config); + const {status} = await distribute.import(configManager); await done(); t.equal(status, 'reject', 'should equal'); @@ -96,7 +98,9 @@ test('distribute: import: received: auth: reject', async (t) => { }); test('distribute: import: received: auth: accept', async (t) => { + const configManager = createConfigManager(); const {done, port} = await connect({ + configManager, config: { name: 'bill', import: true, @@ -108,10 +112,9 @@ test('distribute: import: received: auth: accept', async (t) => { }, }); - const config = createConfigManager(); - config('importUrl', `http://localhost:${port}`); + configManager('importUrl', `http://localhost:${port}`); - const {status} = await distribute.import(config); + const {status} = await distribute.import(configManager); await done(); t.equal(status, 'received','should equal'); @@ -119,7 +122,9 @@ test('distribute: import: received: auth: accept', async (t) => { }); test('distribute: import: received: no name', async (t) => { + const configManager = createConfigManager(); const {done, port} = await connect({ + configManager, config: { name: '', import: true, @@ -129,10 +134,9 @@ test('distribute: import: received: no name', async (t) => { }, }); - const config = createConfigManager(); - config('importUrl', `http://localhost:${port}`); + configManager('importUrl', `http://localhost:${port}`); - const {status} = await distribute.import(config); + const {status} = await distribute.import(configManager); await done(); t.equal(status, 'received','should equal'); @@ -140,7 +144,9 @@ test('distribute: import: received: no name', async (t) => { }); test('distribute: import: error', async (t) => { + const configManager = createConfigManager(); const {done} = await connect({ + configManager, config: { import: true, export: false, @@ -149,10 +155,9 @@ test('distribute: import: error', async (t) => { }, }); - const config = createConfigManager(); - config('importUrl', `http://localhost:0`); + configManager('importUrl', `http://localhost:0`); - const {status} = await distribute.import(config, { + const {status} = await distribute.import(configManager, { reconnection: false, }); @@ -163,7 +168,9 @@ test('distribute: import: error', async (t) => { }); test('distribute: import: config:change: no export', async (t) => { + const configManager = createConfigManager(); const {done} = await connect({ + configManager, config: { import: true, export: false, @@ -172,9 +179,7 @@ test('distribute: import: config:change: no export', async (t) => { }, }); - const config = createConfigManager(); - - const {status} = await distribute.import(config, { + const {status} = await distribute.import(configManager, { reconnection: false, }); diff --git a/server/distribute/log.spec.js b/server/distribute/log.spec.js index 962700b7..83c7a963 100644 --- a/server/distribute/log.spec.js +++ b/server/distribute/log.spec.js @@ -2,7 +2,7 @@ const test = require('supertape'); const log = require('./log'); -const config = require('../config'); +const {createConfig} = require('../config'); test('distribute: log: getMessage', (t) => { const e = 'hello'; @@ -23,7 +23,9 @@ test('distribute: log: getMessage: message', (t) => { }); test('distribute: log: config', (t) => { + const config = createConfig(); const logOriginal = config('log'); + config('log', true); log('log', 'test message'); config('log', logOriginal); diff --git a/server/route.js b/server/route.js index 1e014205..845dcbbb 100644 --- a/server/route.js +++ b/server/route.js @@ -10,18 +10,15 @@ const flop = require('flop'); const ponse = require('ponse'); const rendy = require('rendy'); const format = require('format-io'); -const squad = require('squad'); -const apart = require('apart'); const currify = require('currify'); const tryToCatch = require('try-to-catch'); const once = require('once'); -const config = require(DIR_SERVER + 'config'); const root = require(DIR_SERVER + 'root'); const prefixer = require(DIR_SERVER + 'prefixer'); const CloudFunc = require(DIR_COMMON + 'cloudfunc'); -const prefix = squad(prefixer, apart(config, 'prefix')); +const getPrefix = (config) => prefixer(config('prefix')); const onceRequire = once(require); @@ -172,7 +169,7 @@ function indexProcessing(config, options) { name, }), fm: left + right, - prefix: prefix(), + prefix: getPrefix(config), config: JSON.stringify(config('*')), columns: Columns[config('columns')], }); @@ -183,7 +180,7 @@ function indexProcessing(config, options) { function buildIndex(config, html, json) { const panel = CloudFunc.buildFromJSON({ data: json, - prefix: prefix(), + prefix: getPrefix(config), template: Template, }); diff --git a/server/route.spec.js b/server/route.spec.js index cd99868f..892fcceb 100644 --- a/server/route.spec.js +++ b/server/route.spec.js @@ -321,7 +321,10 @@ test('cloudcmd: route: sendIndex: encode', async (t) => { reRequire(routePath); const cloudcmd = reRequire(cloudcmdPath); - const {request} = serveOnce(cloudcmd); + const {request} = serveOnce(cloudcmd, { + configManager: createConfigManager(), + }); + const {body} = await request.get('/'); mockRequire.stop('flop'); @@ -411,6 +414,7 @@ test('cloudcmd: route: no termianl: /fs', async (t) => { const options = { config, + configManager: createConfigManager(), }; const {request} = serveOnce(cloudcmd); diff --git a/server/server.js b/server/server.js index 23d35b63..601407e8 100644 --- a/server/server.js +++ b/server/server.js @@ -10,8 +10,6 @@ const squad = require('squad'); const tryToCatch = require('try-to-catch'); const wraptile = require('wraptile'); -const config = require(DIR_SERVER + 'config'); - const two = currify((f, a, b) => f(a, b)); const exit = require(DIR_SERVER + 'exit'); @@ -32,7 +30,7 @@ const io = require('socket.io'); const tryRequire = require('tryrequire'); const logger = tryRequire('morgan'); -module.exports = async (options) => { +module.exports = async (options, config) => { const prefix = config('prefix'); const port = process.env.PORT || /* c9 */ config('port'); @@ -57,9 +55,7 @@ module.exports = async (options) => { app.use(prefix, cloudcmd({ config: options, socket: socketServer, - configManager: cloudcmd.createConfigManager({ - filename: cloudcmd.configPath, - }), + configManager: config, })); if (port < 0 || port > 65535) diff --git a/test/before.js b/test/before.js index 495d07a2..78ad0b73 100644 --- a/test/before.js +++ b/test/before.js @@ -24,6 +24,7 @@ function before(options, fn = options) { config, plugins, modules, + configManager, } = options; const app = express(); @@ -41,6 +42,7 @@ function before(options, fn = options) { socket, plugins, config: assign(defaultConfig(), config), + configManager, modules, })); diff --git a/test/rest/pack.js b/test/rest/pack.js index 276ffe6c..a22966d8 100644 --- a/test/rest/pack.js +++ b/test/rest/pack.js @@ -21,13 +21,15 @@ const fixture = { const defaultOptions = { config: { + auth: false, root: join(__dirname, '..'), }, }; + const cloudcmd = require(cloudcmdPath); const serveOnce = require('serve-once'); -const {request} = serveOnce(cloudcmd); +const {request} = serveOnce(cloudcmd, defaultOptions); const once = promisify((name, extract, fn) => { extract.once(name, (header, stream) => { @@ -76,7 +78,6 @@ test('cloudcmd: rest: pack: tar: put: file', async (t) => { const name = String(Math.random()) + '.tar.gz'; - const cloudcmd = reRequire(cloudcmdPath); const {request} = serveOnce(cloudcmd, defaultOptions); await request.put(`/api/v1/pack`, { diff --git a/test/server/console.js b/test/server/console.js index 689665b4..909b9da6 100644 --- a/test/server/console.js +++ b/test/server/console.js @@ -7,7 +7,7 @@ const io = require('socket.io-client'); const configPath = path.join(__dirname, '../..', 'server', 'config'); const {connect} = require('../before'); -const configFn = require(configPath); +const configFn = require(configPath).createConfig(); test('cloudcmd: console: enabled', async (t) => { const config = { diff --git a/test/static.js b/test/static.js index 29fec47c..f8f3e8a8 100644 --- a/test/static.js +++ b/test/static.js @@ -4,7 +4,7 @@ const test = require('supertape'); const criton = require('criton'); const cloudcmd = require('..'); -const configFn = require('../server/config'); +const configFn = cloudcmd.createConfigManager(); const config = { auth: false,