From f9c659612a515479a71f1a8f3e43e06032041ec4 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 30 May 2019 19:27:21 +0300 Subject: [PATCH] feature(config-manager) add (#208) --- HELP.md | 3 + bin/cloudcmd.js | 13 +- common/util.spec.js | 2 +- package.json | 1 + server/auth.js | 13 +- server/cloudcmd.js | 63 +++--- server/cloudcmd.spec.js | 87 ++++----- server/config.js | 184 +++++++++++------- server/config.spec.js | 29 ++- server/distribute/export.js | 35 ++-- server/distribute/import.js | 25 +-- server/distribute/import.spec.js | 29 ++- server/distribute/log.js | 3 +- server/markdown.js | 12 +- server/markdown.spec.js | 6 +- server/rest/index.js | 126 ++++++------ server/rest/index.spec.js | 47 ++--- server/rest/info.js | 5 +- server/root.js | 7 +- server/root.spec.js | 14 -- server/route.js | 27 +-- server/route.spec.js | 11 +- server/server.js | 3 + server/terminal.js | 10 +- .../terminal.js => server/terminal.spec.js | 57 ++---- server/validate.js | 6 +- server/validate.spec.js | 24 +-- test/rest/config.js | 1 + test/rest/cp.js | 4 + test/rest/fs.js | 6 +- test/rest/mv.js | 18 +- test/rest/pack.js | 3 + test/rest/rest.js | 49 ----- 33 files changed, 446 insertions(+), 477 deletions(-) rename test/server/terminal.js => server/terminal.spec.js (53%) delete mode 100644 test/rest/rest.js diff --git a/HELP.md b/HELP.md index 0b134b95..090f80d3 100644 --- a/HELP.md +++ b/HELP.md @@ -671,6 +671,8 @@ 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` @@ -702,6 +704,7 @@ app.use(prefix, cloudcmd({ config, // config data (optional) plugins, // DEPRECATED, use User Menu instead modules, // optional + configManager: createConfigManager(), //optional })); server.listen(port); diff --git a/bin/cloudcmd.js b/bin/cloudcmd.js index 1e4e3ac5..ce3e0eaf 100755 --- a/bin/cloudcmd.js +++ b/bin/cloudcmd.js @@ -203,7 +203,7 @@ function main() { if (password) config('password', getPassword(password)); - validateRoot(options.root); + validateRoot(options.root, config); if (args['show-config']) showConfig(); @@ -213,14 +213,19 @@ function main() { const importConfig = promisify(distribute.import); const caller = (fn) => fn(); - importConfig() + importConfig(config) .then(args.save ? caller(config.save) : noop) .then(startWraped(options)); } -function validateRoot(root) { +function validateRoot(root, config) { const validate = require(DIR_SERVER + 'validate'); - validate.root(root, console.log); + validate.root(root, config); + + if (root === '/') + return; + + console.log(`root: ${root}`); } function getPassword(password) { diff --git a/common/util.spec.js b/common/util.spec.js index 458abbd8..3393dc6c 100644 --- a/common/util.spec.js +++ b/common/util.spec.js @@ -96,7 +96,7 @@ test('util: getRegExp', (t) => { test('util: getRegExp: no', (t) => { const reg = getRegExp(''); - t.deepEqual(reg, RegExp('^.*$'), 'should return regexp'); + t.deepEqual(reg, RegExp('^$'), 'should return regexp'); t.end(); }); diff --git a/package.json b/package.json index 81ac544a..aa2034bd 100644 --- a/package.json +++ b/package.json @@ -118,6 +118,7 @@ "minimist": "^1.2.0", "nomine": "^3.0.0", "object.omit": "^3.0.0", + "once": "^1.4.0", "onezip": "^3.0.0", "open": "^6.0.0", "package-json": "^6.0.0", diff --git a/server/auth.js b/server/auth.js index ce125170..d8f72956 100644 --- a/server/auth.js +++ b/server/auth.js @@ -4,18 +4,17 @@ const httpAuth = require('http-auth'); const criton = require('criton'); const currify = require('currify'); const middle = currify(_middle); +const check = currify(_check); -const config = require('./config'); - -module.exports = () => { +module.exports = (config) => { const auth = httpAuth.basic({ realm: 'Cloud Commander', - }, check); + }, check(config)); - return middle(auth); + return middle(config, auth); }; -function _middle(authentication, req, res, next) { +function _middle(config, authentication, req, res, next) { const is = config('auth'); if (!is) @@ -25,7 +24,7 @@ function _middle(authentication, req, res, next) { authentication.check(req, res, success); } -function check(username, password, callback) { +function _check(config, username, password, callback) { const BAD_CREDENTIALS = false; const name = config('username'); const pass = config('password'); diff --git a/server/cloudcmd.js b/server/cloudcmd.js index 873e4624..e49b3ba4 100644 --- a/server/cloudcmd.js +++ b/server/cloudcmd.js @@ -9,7 +9,7 @@ const fs = require('fs'); const cloudfunc = require(DIR_COMMON + 'cloudfunc'); const authentication = require(DIR + 'auth'); -const config = require(DIR + 'config'); +const defaultConfig = require(DIR + 'config'); const modulas = require(DIR + 'modulas'); const userMenu = require(DIR + 'user-menu'); const rest = require(DIR + 'rest'); @@ -38,15 +38,15 @@ const getDist = (isDev) => isDev ? 'dist-dev' : 'dist'; const getIndexPath = (isDev) => path.join(DIR, '..', `${getDist(isDev)}/index.html`); const defaultHtml = fs.readFileSync(getIndexPath(isDev), 'utf8'); -const auth = currify(_auth); -const root = () => config('root'); - +const initAuth = currify(_initAuth); const notEmpty = (a) => a; const clean = (a) => a.filter(notEmpty); module.exports = (params) => { const p = params || {}; const options = p.config || {}; + const config = p.configManager || defaultConfig; + const { modules, plugins, @@ -59,7 +59,10 @@ module.exports = (params) => { keys.forEach((name) => { let value = options[name]; - if (/root|editor|packer|columns/.test(name)) + if (/root/.test(name)) + validate.root(value, config); + + if (/editor|packer|columns/.test(name)) validate[name](value); if (/prefix/.test(name)) @@ -68,21 +71,31 @@ module.exports = (params) => { config(name, value); }); - config('console', defaultValue('console', options)); - config('configDialog', defaultValue('configDialog', options)); + config('console', defaultValue(config, 'console', options)); + config('configDialog', defaultValue(config, 'configDialog', options)); - const {prefix} = prefixer(options.prefix); const prefixSocket = prefixer(options.prefixSocket); if (p.socket) - listen(prefixSocket, p.socket); + listen({ + prefixSocket, + config, + socket: p.socket, + }); - return cloudcmd(prefix, plugins, modules); + return cloudcmd({ + plugins, + modules, + config, + }); }; +module.exports.createConfigManager = defaultConfig.create; +module.exports.configPath = defaultConfig.path; + module.exports._getIndexPath = getIndexPath; -function defaultValue(name, options) { +function defaultValue(config, name, options) { const value = options[name]; const previous = config(name); @@ -100,8 +113,8 @@ function getPrefix(prefix) { return prefix || ''; } -module.exports._auth = _auth; -function _auth(accept, reject, username, password) { +module.exports._initAuth = _initAuth; +function _initAuth(config, accept, reject, username, password) { if (!config('auth')) return accept(); @@ -114,10 +127,14 @@ function _auth(accept, reject, username, password) { reject(); } -function listen(prefixSocket, socket) { +function listen({prefixSocket, socket, config}) { + const root = () => config('root'); + const auth = initAuth(config); + prefixSocket = getPrefix(prefixSocket); - config.listen(socket, auth); + if (config.listen) + config.listen(socket, auth); edward.listen(socket, { root, @@ -148,17 +165,17 @@ function listen(prefixSocket, socket) { prefix: prefixSocket + '/fileop', }); - config('terminal') && terminal().listen(socket, { + config('terminal') && terminal(config).listen(socket, { auth, prefix: prefixSocket + '/gritty', command: config('terminalCommand'), autoRestart: config('terminalAutoRestart'), }); - distribute.export(socket); + distribute.export(config, socket); } -function cloudcmd(prefix, plugins, modules) { +function cloudcmd({plugins, modules, config}) { const online = apart(config, 'online'); const cache = false; const diff = apart(config, 'diff'); @@ -169,13 +186,14 @@ function cloudcmd(prefix, plugins, modules) { const dropbox = config('dropbox'); const dropboxToken = config('dropboxToken'); + const root = () => config('root'); const funcs = clean([ config('console') && konsole({ online, }), - config('terminal') && terminal({}), + config('terminal') && terminal(config, {}), edward({ online, @@ -202,13 +220,12 @@ function cloudcmd(prefix, plugins, modules) { }), fileop(), - nomine(), setUrl, setSW, logout, - authentication(), + authentication(config), config.middle, modules && modulas(modules), @@ -228,8 +245,8 @@ function cloudcmd(prefix, plugins, modules) { menuName: '.cloudcmd.menu.js', }), - rest, - route({ + rest(config), + route(config, { html: defaultHtml, }), diff --git a/server/cloudcmd.spec.js b/server/cloudcmd.spec.js index 73fa9883..4f32cfc8 100644 --- a/server/cloudcmd.spec.js +++ b/server/cloudcmd.spec.js @@ -4,17 +4,16 @@ const path = require('path'); const test = require('supertape'); const stub = require('@cloudcmd/stub'); -const currify = require('currify'); const {reRequire} = require('mock-require'); const DIR = './'; const cloudcmdPath = DIR + 'cloudcmd'; -const config = require(DIR + 'config'); const cloudcmd = require(cloudcmdPath); const { + createConfigManager, _getPrefix, - _auth, + _initAuth, } = cloudcmd; const {request} = require('serve-once')(cloudcmd, { @@ -41,25 +40,27 @@ test('cloudcmd: args: plugins: error', (t) => { }); test('cloudcmd: defaults: config', (t) => { - const configDialog = config('configDialog'); + const configManager = createConfigManager(); - config('configDialog', false); - cloudcmd(); - t.notOk(config('configDialog'), 'should not override config with defaults'); + configManager('configDialog', false); - config('configDialog', configDialog); + cloudcmd({ + configManager, + }); + t.notOk(configManager('configDialog'), 'should not override config with defaults'); t.end(); }); test('cloudcmd: defaults: console', (t) => { - const console = config('console'); - config('console', false); - cloudcmd(); - t.notOk(config('console'), 'should not override config with defaults'); + const configManager = createConfigManager(); + configManager('console', false); - config('console', console); + cloudcmd({ + configManager, + }); + t.notOk(configManager('console'), 'should not override config with defaults'); t.end(); }); @@ -120,79 +121,61 @@ test('cloudcmd: replaceDist: !isDev', (t) => { }); test('cloudcmd: auth: reject', (t) => { - const auth = config('auth'); const accept = stub(); const reject = stub(); + + const config = createConfigManager(); + const username = 'root'; const password = 'toor'; - - const set = credentials(); - const reset = set('hello', 'world'); config('auth', true); + config('username', username); + config('password', password); - _auth(accept, reject, username, password); + _initAuth(config, accept, reject, username, 'abc'); - config('auth', auth); - reset(); - - t.ok(reject.called, 'should accept'); + t.ok(reject.called, 'should reject'); t.end(); }); test('cloudcmd: auth: accept', (t) => { - const auth = config('auth'); const accept = stub(); const reject = stub(); + const username = 'root'; const password = 'toor'; + const auth = true; - const set = credentials(); - const reset = set(username, password); - config('auth', true); - - _auth(accept, reject, username, password); - + const config = createConfigManager(); + config('username', username); + config('password', password); config('auth', auth); - reset(); + + _initAuth(config, accept, reject, username, password); t.ok(accept.called, 'should accept'); t.end(); }); test('cloudcmd: auth: accept: no auth', (t) => { - const auth = config('auth'); const accept = stub(); const reject = stub(); + + const auth = false; const username = 'root'; const password = 'toor'; - config('auth', false); - _auth(accept, reject, username, password); + const config = createConfigManager(); + config('username', username); + config('password', password); config('auth', auth); + _initAuth(config, accept, reject, username, password); + t.ok(accept.called, 'should accept'); t.end(); }); -function credentials() { - const username = config('username'); - const password = config('password'); - - const reset = () => { - config('username', username); - config('password', password); - }; - - const set = currify((fn, a, b) => { - config('username', a); - config('password', b); - - return fn; - }); - - return set(reset); -} - test('cloudcmd: getIndexPath: production', (t) => { const isDev = false; const name = path.join(__dirname, '..', 'dist', 'index.html'); diff --git a/server/config.js b/server/config.js index c96a99f1..09874931 100644 --- a/server/config.js +++ b/server/config.js @@ -15,70 +15,73 @@ const CloudFunc = require(DIR_COMMON + 'cloudfunc'); const currify = require('currify'); const wraptile = require('wraptile'); -const squad = require('squad'); const tryToCatch = require('try-to-catch'); const pullout = require('pullout'); const ponse = require('ponse'); const jonny = require('jonny'); const jju = require('jju'); -const writejson = require('writejson'); +const writejson = promisify(require('writejson')); const tryCatch = require('try-catch'); const criton = require('criton'); const HOME = homedir(); -const manageConfig = squad(traverse, cryptoPass); -const save = promisify(_save); - +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 readjsonSync = (name) => { - return jju.parse(fs.readFileSync(name, 'utf8'), { - mode: 'json', - }); -}; +const config = read(); -const rootConfig = readjsonSync(ConfigPath); -const key = (a) => Object.keys(a).pop(); +const connection = currify(_connection); +const connectionWraped = wraptile(_connection); +const middle = currify(_middle); -const [error, configHome] = tryCatch(readjsonSync, ConfigHome); - -if (error && error.code !== 'ENOENT') - exit(`cloudcmd --config ${ConfigHome}: ${error.message}`); - -const config = { - ...rootConfig, - ...configHome, -}; - -const connectionWraped = wraptile(connection); +function read(filename = ConfigHome) { + const readjsonSync = (name) => { + return jju.parse(fs.readFileSync(name, 'utf8'), { + mode: 'json', + }); + }; + + const rootConfig = readjsonSync(ConfigPath); + const [error, configHome] = tryCatch(readjsonSync, ConfigHome); + + if (error && error.code !== 'ENOENT') + exit(`cloudcmd --config ${filename}: ${error.message}`); + + return { + ...rootConfig, + ...configHome, + }; +} module.exports = manage; -module.exports.save = save; -module.exports.middle = middle; +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.listen = (socket, auth) => { - check(socket, auth); - +const manageListen = currify((manage, socket, auth) => { if (!manage('configDialog')) return middle; - listen(socket, auth); + listen(manage, socket, auth); return middle; -}; +}); function manage(key, value) { if (!key) @@ -97,25 +100,59 @@ function manage(key, value) { return `${key} = ${value}`; } -function _save(callback) { - writejson(ConfigHome, config, {mode: 0o600}, callback); -} - -function listen(sock, auth) { - const prefix = manage('prefixSocket'); +function initWrite(filename, configManager) { + if (filename) + return write.bind(null, filename, configManager); - sock.of(prefix + '/config') - .on('connection', (socket) => { - if (!manage('auth')) - return connection(socket); - - const reject = () => socket.emit('reject'); - socket.on('auth', auth(connectionWraped(socket), reject)); - }); + return resolve; } -function connection(socket) { - socket.emit('config', config); +function readConfig(filename) { + if (filename) + return read(filename); + + return config; +} + +function create({filename} = {}) { + const config = {}; + + const configManager = (key, value) => { + if (key === '*') + return { + ...config, + }; + + if (value === undefined) + return config[key]; + + config[key] = value; + }; + + spread(configManager); + Object.assign(config, readConfig(filename)); + + configManager.middle = middle(configManager); + configManager.listen = manageListen(configManager); + configManager.write = initWrite(filename, configManager); + + 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}); +}; + +function _connection(manage, socket) { + socket.emit('config', manage('*')); const emit = currify((socket, name, e) => { return socket.emit(name, e.message); @@ -125,7 +162,7 @@ function connection(socket) { if (typeof json !== 'object') return socket.emit('err', 'Error: Wrong data type!'); - manageConfig(json); + traverse(cryptoPass(manage, json)); const send = () => { const data = CloudFunc.formatMsg('config', key(json)); @@ -134,13 +171,26 @@ function connection(socket) { socket.emit('log', data); }; - save() + manage.write() .then(send) .catch(emit(socket, 'err')); }); } -function middle(req, res, next) { +function listen(manage, sock, auth) { + const prefix = manage('prefixSocket'); + + sock.of(prefix + '/config') + .on('connection', (socket) => { + if (!manage('auth')) + return connection(manage, socket); + + const reject = () => socket.emit('reject'); + socket.on('auth', auth(connectionWraped(manage, socket), reject)); + }); +} + +function _middle(manage, req, res, next) { const noConfigDialog = !manage('configDialog'); if (req.url !== `${apiURL}/config`) @@ -148,7 +198,7 @@ function middle(req, res, next) { switch(req.method) { case 'GET': - get(req, res, next); + get(manage, req, res, next); break; case 'PATCH': @@ -157,7 +207,7 @@ function middle(req, res, next) { .status(404) .send('Config is disabled'); - patch(req, res); + patch(manage, req, res); break; default: @@ -165,8 +215,8 @@ function middle(req, res, next) { } } -function get(request, response) { - const data = jonny.stringify(config); +function get(manage, request, response) { + const data = jonny.stringify(manage('*')); ponse.send(data, { name : 'config.json', @@ -176,7 +226,7 @@ function get(request, response) { }); } -async function patch(request, response) { +async function patch(manage, request, response) { const name = 'config.json'; const cache = false; const options = { @@ -186,18 +236,18 @@ async function patch(request, response) { cache, }; - const [e] = await tryToCatch(patchConfig, options); + const [e] = await tryToCatch(patchConfig, manage, options); if (e) ponse.sendError(e, options); } -async function patchConfig({name, request, response, cache}) { +async function patchConfig(manage, {name, request, response, cache}) { const str = await pullout(request); const json = jonny.parse(str); - manageConfig(json); - await save(); + traverse(cryptoPass(manage, json)); + await manage.write(); const msg = formatMsg('config', key(json)); ponse.send(msg, { @@ -208,32 +258,24 @@ async function patchConfig({name, request, response, cache}) { }); } -function traverse(json) { +function traverse([manage, json]) { Object.keys(json).forEach((name) => { manage(name, json[name]); }); } module.exports._cryptoPass = cryptoPass; -function cryptoPass(json) { +function cryptoPass(manage, json) { const algo = manage('algo'); if (!json.password) - return json; + return [manage, json]; const password = criton(json.password, algo); - return { + return [manage, { ...json, password, - }; -} - -function check(socket, auth) { - if (!socket) - throw Error('socket could not be empty!'); - - if (auth && typeof auth !== 'function') - throw Error('auth should be function!'); + }]; } diff --git a/server/config.spec.js b/server/config.spec.js index 7c656dcb..2d6a500b 100644 --- a/server/config.spec.js +++ b/server/config.spec.js @@ -7,7 +7,11 @@ const root = '../'; const configPath = './config'; const config = require(configPath); -const {_cryptoPass} = config; +const { + _cryptoPass, + create, +} = config; + const {apiURL} = require(root + 'common/cloudfunc'); const fixture = require('./config.fixture'); @@ -54,27 +58,15 @@ test('config: manage: get: *', (t) => { t.end(); }); -test('config: listen: no socket', (t) => { - t.throws(config.listen, 'should throw when no socket'); - t.end(); -}); - -test('config: listen: authCheck: not function', (t) => { - const socket = {}; - const fn = () => config.listen(socket, 'hello'); - - t.throws(fn, 'should throw when authCheck not function'); - t.end(); -}); - test('config: cryptoPass: no password', (t) => { const json = { hello: 'world', }; - const result = _cryptoPass(json); + const config = create(); + const result = _cryptoPass(config, json); - t.equal(result, json, 'should not change json'); + t.deepEqual(result, [config, json], 'should not change json'); t.end(); }); @@ -89,9 +81,10 @@ test('config: cryptoPass', (t) => { password, }; - const result = _cryptoPass(json); + const config = create(); + const result = _cryptoPass(config, json); - t.deepEqual(result, expected, 'should crypt password'); + t.deepEqual(result, [config, expected], 'should crypt password'); t.end(); }); diff --git a/server/distribute/export.js b/server/distribute/export.js index 4bfc84ce..7b2e3641 100644 --- a/server/distribute/export.js +++ b/server/distribute/export.js @@ -5,7 +5,6 @@ const wraptile = require('wraptile'); const squad = require('squad'); const omit = require('object.omit'); -const config = require('../config'); const log = require('./log'); const { @@ -39,22 +38,23 @@ const omitList = [ const omitConfig = (config) => omit(config, omitList); -module.exports = (socket) => { +module.exports = (config, socket) => { if (!config('export')) return; const prefix = config('prefix'); const distributePrefix = `${prefix}/distribute`; + const isLog = config('log'); const onError = squad( - logWraped(exportStr), + logWraped(isLog, exportStr), getMessage, ); - const onConnectError = squad(logWraped(exportStr), getDescription); + const onConnectError = squad(logWraped(isLog, exportStr), getDescription); socket.of(distributePrefix) - .on('connection', onConnection(push)) + .on('connection', onConnection(push, config)) .on('error', onError) .on('connect_error', onConnectError); }; @@ -83,41 +83,46 @@ function getHost(socket) { return `${colorName} [${remoteAddress}:${port}]`; } -const connectPush = wraptile((push, socket) => { +const connectPush = wraptile((push, config, socket) => { socket.emit('accept'); + const isLog = config('log'); const host = getHost(socket); const subscription = push(socket); - socket.on('disconnect', onDisconnect(subscription, host)); + socket.on('disconnect', onDisconnect(subscription, config, host)); - log(exportStr, `${connectedStr} to ${host}`); + log(isLog, exportStr, `${connectedStr} to ${host}`); socket.emit('config', omitConfig(config('*'))); - log(exportStr, `config send to ${host}`); + log(isLog, exportStr, `config send to ${host}`); config.subscribe(subscription); }); -const onConnection = currify((push, socket) => { +const onConnection = currify((push, config, socket) => { const host = getHost(socket); const reject = () => { socket.emit('reject'); socket.disconnect(); }; - log(exportStr, `${authTryStr} from ${host}`); - socket.on('auth', auth(connectPush(push, socket), reject)); + const isLog = config('log'); + + log(isLog, exportStr, `${authTryStr} from ${host}`); + socket.on('auth', auth(config, reject, connectPush(push, config, socket))); }); -const auth = currify((fn, reject, token) => { +const auth = currify((config, reject, fn, token) => { if (token === config('exportToken')) return fn(); reject(); }); -const onDisconnect = wraptile((subscription, host) => { +const onDisconnect = wraptile((subscription, config, host) => { + const isLog = config('log'); + config.unsubscribe(subscription); - log(exportStr, `${disconnectedStr} from ${host}`); + log(isLog, exportStr, `${disconnectedStr} from ${host}`); }); diff --git a/server/distribute/import.js b/server/distribute/import.js index ba671579..6b0191e8 100644 --- a/server/distribute/import.js +++ b/server/distribute/import.js @@ -8,7 +8,6 @@ const fullstore = require('fullstore'); const io = require('socket.io-client'); const forEachKey = currify(require('for-each-key')); -const config = require('../config'); const log = require('./log'); const { @@ -57,12 +56,13 @@ const done = wraptile((fn, store) => fn(null, { status: store(), })); -const emitAuth = wraptile((importUrl, socket) => { - log(importStr, `${authTryStr} to ${importUrl}`); +const emitAuth = wraptile((importUrl, config, socket) => { + const isLog = config('log'); + log(isLog, importStr, `${authTryStr} to ${importUrl}`); socket.emit('auth', config('importToken')); }); -module.exports = (options, fn) => { +module.exports = (config, options, fn) => { fn = fn || options; if (!config('import')) @@ -72,6 +72,7 @@ module.exports = (options, fn) => { const importListen = config('importListen'); const name = config('name'); const port = config('port'); + const isLog = config('log'); const query = toLine({ name, @@ -93,30 +94,30 @@ module.exports = (options, fn) => { const onConfig = squad( close, - logWraped(importStr, `config received from ${colorUrl}`), + logWraped(isLog, importStr, `config received from ${colorUrl}`), statusStoreWraped('received'), forEachKey(config), ); const onError = squad( superFn('error'), - logWraped(importStr), + logWraped(isLog, config, importStr), addUrl(colorUrl), getMessage, ); const onConnectError = squad( superFn('connect_error'), - logWraped(importStr), + logWraped(isLog, importStr), addUrl(colorUrl), getDescription, ); - const onConnect = emitAuth(importUrl, socket); - const onAccept = logWraped(importStr,`${connectedStr} to ${colorUrl}`); + const onConnect = emitAuth(importUrl, config, socket); + const onAccept = logWraped(isLog, importStr,`${connectedStr} to ${colorUrl}`); const onDisconnect = squad( done(fn, statusStore), - logWraped(importStr, `${disconnectedStr} from ${colorUrl}`), + logWraped(isLog, importStr, `${disconnectedStr} from ${colorUrl}`), rmListeners(socket, { onError, onConnect, @@ -125,13 +126,13 @@ module.exports = (options, fn) => { ); const onChange = squad( - logWraped(importStr), + logWraped(isLog, importStr), config, ); const onReject = squad( superFn('reject'), - logWraped(importStr, tokenRejectedStr), + logWraped(isLog, importStr, tokenRejectedStr), ); socket.on('connect', onConnect); diff --git a/server/distribute/import.spec.js b/server/distribute/import.spec.js index e06eff3d..76938248 100644 --- a/server/distribute/import.spec.js +++ b/server/distribute/import.spec.js @@ -4,11 +4,13 @@ const test = require('supertape'); const {promisify} = require('util'); const tryToCatch = require('try-to-catch'); const {connect} = require('../../test/before'); -const config = require('../config'); +const {createConfigManager} = require('../cloudcmd'); const distribute = { import: promisify(require('./import')), }; +const config = createConfigManager(); + test('distribute: import: canceled', async (t) => { const {done} = await connect({ config: { @@ -19,7 +21,7 @@ test('distribute: import: canceled', async (t) => { }, }); - const {status} = await distribute.import(); + const {status} = await distribute.import(config); await done(); @@ -39,7 +41,7 @@ test('distribute: import: received: no error', async (t) => { config('importUrl', `http://localhost:${port}`); - const [e] = await tryToCatch(distribute.import); + const [e] = await tryToCatch(distribute.import, config); await done(); @@ -60,9 +62,10 @@ test('distribute: import: received', async (t) => { }, }); + const config = createConfigManager(); config('importUrl', `http://localhost:${port}`); - const {status} = await distribute.import(); + const {status} = await distribute.import(config); await done(); t.equal(status, 'received','should equal'); @@ -82,12 +85,13 @@ test('distribute: import: received: auth: reject', async (t) => { }, }); + const config = createConfigManager(); config('importUrl', `http://localhost:${port}`); - const {status} = await distribute.import(); + const {status} = await distribute.import(config); await done(); - t.equal(status, 'reject','should equal'); + t.equal(status, 'reject', 'should equal'); t.end(); }); @@ -104,9 +108,10 @@ test('distribute: import: received: auth: accept', async (t) => { }, }); + const config = createConfigManager(); config('importUrl', `http://localhost:${port}`); - const {status} = await distribute.import(); + const {status} = await distribute.import(config); await done(); t.equal(status, 'received','should equal'); @@ -124,9 +129,10 @@ test('distribute: import: received: no name', async (t) => { }, }); + const config = createConfigManager(); config('importUrl', `http://localhost:${port}`); - const {status} = await distribute.import(); + const {status} = await distribute.import(config); await done(); t.equal(status, 'received','should equal'); @@ -143,9 +149,10 @@ test('distribute: import: error', async (t) => { }, }); + const config = createConfigManager(); config('importUrl', `http://localhost:0`); - const {status} = await distribute.import({ + const {status} = await distribute.import(config, { reconnection: false, }); @@ -165,7 +172,9 @@ test('distribute: import: config:change: no export', async (t) => { }, }); - const {status} = await distribute.import({ + const config = createConfigManager(); + + const {status} = await distribute.import(config, { reconnection: false, }); diff --git a/server/distribute/log.js b/server/distribute/log.js index f37d06e9..8ec98e1d 100644 --- a/server/distribute/log.js +++ b/server/distribute/log.js @@ -3,10 +3,9 @@ const wraptile = require('wraptile'); const chalk = require('chalk'); -const config = require('../config'); const datetime = require('../../common/datetime'); -const log = (name, msg) => config('log') && console.log(`${datetime()} -> ${name}: ${msg}`); +const log = (isLog, name, msg) => isLog && console.log(`${datetime()} -> ${name}: ${msg}`); const makeColor = (a, color) => chalk.rgb(...(color || stringToRGB(a)))(a); const getMessage = (e) => e.message || e; const getDescription = (e) => `${e.type}: ${e.description}`; diff --git a/server/markdown.js b/server/markdown.js index 9500591c..5982701f 100644 --- a/server/markdown.js +++ b/server/markdown.js @@ -15,32 +15,32 @@ const readFile = promisify(fs.readFile); const root = require('./root'); -module.exports = callbackify(async (name, request) => { +module.exports = callbackify(async (name, rootDir, request) => { check(name, request); const {method} = request; switch(method) { case 'GET': - return onGET(request, name); + return onGET(request, name, rootDir); case 'PUT': return onPUT(request); } }); -function parseName(query, name) { +function parseName(query, name, rootDir) { const shortName = name.replace('/markdown', ''); if (query === 'relative') return DIR_ROOT + shortName; - return root(shortName); + return root(shortName, rootDir); } -async function onGET(request, name) { +async function onGET(request, name, root) { const query = ponse.getQuery(request); - const fileName = parseName(query, name); + const fileName = parseName(query, name, root); const data = await readFile(fileName, 'utf8'); return parse(data); diff --git a/server/markdown.spec.js b/server/markdown.spec.js index b177c044..05510a65 100644 --- a/server/markdown.spec.js +++ b/server/markdown.spec.js @@ -15,8 +15,12 @@ const cloudcmd = require('..'); const config = { auth: false, }; + +const configManager = cloudcmd.createConfigManager(); + const {request} = require('serve-once')(cloudcmd, { config, + configManager, }); test('cloudcmd: markdown: error', async (t) => { @@ -64,7 +68,7 @@ test('cloudcmd: markdown: put: error', async (t) => { mdStream.url = 'http://hello.world'; mdStream.method = 'PUT'; - const [e] = await tryToCatch(_markdown, name, mdStream); + const [e] = await tryToCatch(_markdown, name, '/', mdStream); t.ok(e.message.includes('ENOENT: no such file or directory'), 'should emit error'); t.end(); diff --git a/server/rest/index.js b/server/rest/index.js index a9f4aaf5..27074b92 100644 --- a/server/rest/index.js +++ b/server/rest/index.js @@ -7,7 +7,6 @@ const path = require('path'); const fs = require('fs'); const root = require(DIR + 'root'); -const config = require(DIR + 'config'); const CloudFunc = require(DIR_COMMON + 'cloudfunc'); const markdown = require(DIR + 'markdown'); const info = require('./info'); @@ -16,6 +15,7 @@ const jaguar = require('jaguar'); const onezip = require('onezip'); const inly = require('inly'); const wraptile = require('wraptile'); +const currify = require('currify'); const pullout = require('pullout'); const json = require('jonny'); const ponse = require('ponse'); @@ -27,9 +27,7 @@ const swap = wraptile((fn, a, b) => fn(b, a)); const isWin32 = process.platform === 'win32'; const {apiURL} = CloudFunc; -module.exports = (request, response, next) => { - check(request, response, next); - +module.exports = currify((config, request, response, next) => { const name = ponse.getPathName(request); const regExp = RegExp('^' + apiURL); const is = regExp.test(name); @@ -37,10 +35,10 @@ module.exports = (request, response, next) => { if (!is) return next(); - rest(request, response); -}; + rest(config, request, response); +}); -function rest(request, response) { +function rest(config, request, response) { const name = ponse.getPathName(request); const params = { request, @@ -48,7 +46,7 @@ function rest(request, response) { name: name.replace(apiURL, '') || '/', }; - sendData(params, (error, options, data) => { + sendData(params, config, (error, options, data) => { params.gzip = !error; if (!data) { @@ -65,9 +63,12 @@ function rest(request, response) { if (options.query) params.query = options.query; - if (error) + if (error && error.code) return ponse.sendError(error, params); + if (error) + return ponse.sendError(error.stack, params); + ponse.send(data, params); }); } @@ -77,44 +78,53 @@ function rest(request, response) { * * @param params {name, method, body, requrest, response} */ -function sendData(params, callback) { +function sendData(params, config, callback) { const p = params; const isMD = RegExp('^/markdown').test(p.name); + const rootDir = config('root'); if (isMD) - return markdown(p.name, p.request, callback); + return markdown(p.name, rootDir, p.request, callback); const {method} = p.request; switch(method) { case 'GET': - return onGET(params, callback); + return onGET(params, config, callback); case 'PUT': return pullout(p.request) .then((body) => { - onPUT(p.name, body, callback); + onPUT({ + name: p.name, + config, + body, + callback, + }); }) .catch(callback); } } -function onGET(params, callback) { +function onGET(params, config, callback) { let cmd; const p = params; + const packer = config('packer'); + const prefix = config('prefix'); + const rootDir = config('root'); if (p.name[0] === '/') cmd = p.name.replace('/', ''); if (/^pack/.test(cmd)) { cmd = cmd.replace(/^pack/, ''); - streamPack(root(cmd), p.response); + streamPack(root(cmd, rootDir), p.response, packer); return; } switch(cmd) { case '': - p.data = json.stringify(info()); + p.data = json.stringify(info(prefix)); callback(null, {name: 'api.json'}, p.data); break; @@ -127,22 +137,22 @@ function onGET(params, callback) { } } -function getPackReg() { - if (config('packer') === 'zip') +function getPackReg(packer) { + if (packer === 'zip') return /\.zip$/; return /\.tar\.gz$/; } -function streamPack(cmd, response) { +function streamPack(cmd, response, packer) { const noop = () => {}; - const filename = cmd.replace(getPackReg(), ''); + const filename = cmd.replace(getPackReg(packer), ''); const dir = path.dirname(filename); const names = [ path.basename(filename), ]; - operation('pack', dir, response, names, noop); + operation('pack', packer, dir, response, names, noop); } function getCMD(cmd) { @@ -160,25 +170,26 @@ const getMoveMsg = (files) => { }; module.exports._onPUT = onPUT; -function onPUT(name, body, callback) { +function onPUT({name, config, body, callback}) { checkPut(name, body, callback); const cmd = getCMD(name); const files = json.parse(body); + const rootDir = config('root'); switch(cmd) { case 'mv': { if (!files.from || !files.to) return callback(body); - if (isRootAll([files.to, files.from])) + if (isRootAll(rootDir, [files.to, files.from])) return callback(getWin32RootMsg()); const msg = getMoveMsg(files); const fn = swap(callback, msg); - const from = root(files.from); - const to = root(files.to); + const from = root(files.from, rootDir); + const to = root(files.to, rootDir); const {names} = files; if (!names) @@ -191,11 +202,11 @@ function onPUT(name, body, callback) { if (!files.from || !files.names || !files.to) return callback(body); - if (isRootAll([files.to, files.from])) + if (isRootAll(rootDir, [files.to, files.from])) return callback(getWin32RootMsg()); - files.from = root(files.from); - files.to = root(files.to); + files.from = root(files.from, rootDir); + files.to = root(files.to, rootDir); copy(files.from, files.to, files.names, (error) => { const msg = formatMsg('copy', files.names); @@ -207,14 +218,14 @@ function onPUT(name, body, callback) { if (!files.from) return callback(body); - pack(files.from, files.to, files.names, callback); + pack(files.from, files.to, files.names, config, callback); break; case 'extract': if (!files.from) return callback(body); - extract(files.from, files.to, callback); + extract(files.from, files.to, config, callback); break; @@ -224,9 +235,12 @@ function onPUT(name, body, callback) { } } -function pack(from, to, names, fn) { - from = root(from); - to = root(to); +function pack(from, to, names, config, fn) { + const rootDir = config('root'); + const packer = config('packer'); + + from = root(from, rootDir); + to = root(to, rootDir); if (!names) { names = [ @@ -236,31 +250,33 @@ function pack(from, to, names, fn) { from = path.dirname(from); } - operation('pack', from, to, names, fn); + operation('pack', packer, from, to, names, fn); } -function extract(from, to, fn) { - from = root(from); +function extract(from, to, config, fn) { + const rootDir = config('root'); + + from = root(from, rootDir); if (to) - to = root(to); + to = root(to, rootDir); else to = from.replace(/\.tar\.gz$/, ''); - operation('extract', from, to, fn); + operation('extract', config('packer'), from, to, fn); } -function getPacker(operation) { +function getPacker(operation, packer) { if (operation === 'extract') return inly; - if (config('packer') === 'zip') + if (packer === 'zip') return onezip.pack; return jaguar.pack; } -function operation(op, from, to, names, fn) { +function operation(op, packer, from, to, names, fn) { if (!fn) { fn = names; names = [ @@ -268,8 +284,8 @@ function operation(op, from, to, names, fn) { ]; } - const packer = getPacker(op); - const pack = packer(from, to, names); + const packerFn = getPacker(op, packer); + const pack = packerFn(from, to, names); pack.on('error', fn); @@ -300,17 +316,18 @@ function copy(from, to, names, fn) { }); } -module.exports._isRootWin32 = isRootWin32; -function isRootWin32(path) { +const isRootWin32 = currify((root, path) => { const isRoot = path === '/'; - const isConfig = config('root') === '/'; + const isConfig = root === '/'; return isWin32 && isRoot && isConfig; -} +}); +module.exports._isRootWin32 = isRootWin32; module.exports._isRootAll = isRootAll; -function isRootAll(names) { - return names.some(isRootWin32); + +function isRootAll(root, names) { + return names.some(isRootWin32(root)); } module.exports._getWin32RootMsg = getWin32RootMsg; @@ -339,17 +356,6 @@ function formatMsg(msg, data, status) { return CloudFunc.formatMsg(msg, value, status); } -function check(request, response, next) { - if (typeof request !== 'object') - throw Error('request should be an object!'); - - if (typeof response !== 'object') - throw Error('response should be an object!'); - - if (typeof next !== 'function') - throw Error('next should be a function!'); -} - function checkPut(name, body, callback) { if (typeof name !== 'string') throw Error('name should be a string!'); diff --git a/server/rest/index.spec.js b/server/rest/index.spec.js index e7059a62..58e215a4 100644 --- a/server/rest/index.spec.js +++ b/server/rest/index.spec.js @@ -1,6 +1,8 @@ 'use strict'; const test = require('supertape'); +const tryToCatch = require('try-to-catch'); + const rest = require('.'); const { _formatMsg, @@ -34,50 +36,41 @@ test('rest: getWin32RootMsg', (t) => { }); test('rest: isRootWin32', (t) => { - const result = _isRootWin32('/'); + const result = _isRootWin32('/', '/'); t.notOk(result, 'should equal'); t.end(); }); test('rest: isRootAll', (t) => { - const result = _isRootAll(['/', '/h']); + const result = _isRootAll('/', ['/', '/h']); t.notOk(result, 'should equal'); t.end(); }); -test('rest: onPUT: no args', (t) => { - t.throws(_onPUT, /name should be a string!/, 'should throw when no args'); +test('rest: onPUT: no args', async (t) => { + const [e] = await tryToCatch(_onPUT, {}); + t.equal(e.message, 'name should be a string!', 'should throw when no args'); t.end(); }); -test('rest: onPUT: no body', (t) => { - const fn = () => _onPUT('hello'); - t.throws(fn, /body should be a string!/, 'should throw when no body'); +test('rest: onPUT: no body', async (t) => { + const [e] = await tryToCatch(_onPUT, { + name: 'hello', + }); + + t.equal(e.message, 'body should be a string!', 'should throw when no body'); t.end(); }); -test('rest: onPUT: no callback', (t) => { - const fn = () => _onPUT('hello', 'world'); - t.throws(fn, /callback should be a function!/, 'should throw when no callback'); - t.end(); -}); - -test('rest: no args', (t) => { - t.throws(rest, /request should be an object!/, 'should throw when no args'); - t.end(); -}); - -test('rest: no response', (t) => { - const fn = () => rest({}); - t.throws(fn, /response should be an object!/, 'should throw when no response'); - t.end(); -}); - -test('rest: no next', (t) => { - const fn = () => rest({}, {}); - t.throws(fn, /next should be a function!/, 'should throw when no response'); +test('rest: onPUT: no callback', async (t) => { + const [e] = await tryToCatch(_onPUT, { + name: 'hello', + body: 'world', + }); + + t.equal(e.message, 'callback should be a function!', 'should throw when no callback'); t.end(); }); diff --git a/server/rest/info.js b/server/rest/info.js index bc63fffa..13758b21 100644 --- a/server/rest/info.js +++ b/server/rest/info.js @@ -3,16 +3,15 @@ const format = require('format-io'); const {version} = require('../../package'); -const config = require('../config'); const getMemory = () => { const {rss} = process.memoryUsage(); return format.size(rss); }; -module.exports = () => ({ +module.exports = (prefix) => ({ version, + prefix, memory: getMemory(), - prefix: config('prefix'), }); diff --git a/server/root.js b/server/root.js index 5c3ea26b..fcbfcf7e 100644 --- a/server/root.js +++ b/server/root.js @@ -1,11 +1,8 @@ 'use strict'; -const config = require('./config'); const mellow = require('mellow'); -module.exports = (dir) => { - const root = config('root') || '/'; - - return mellow.pathToWin(dir, root); +module.exports = (dir, root) => { + return mellow.pathToWin(dir, root || '/'); }; diff --git a/server/root.spec.js b/server/root.spec.js index 783145c4..a8c65079 100644 --- a/server/root.spec.js +++ b/server/root.spec.js @@ -8,20 +8,6 @@ const {reRequire} = mockRequire; const pathConfig = './config'; const pathRoot = './root'; -test('cloudcmd: root: config', (t) => { - const config = stub().returns(false); - - mockRequire(pathConfig, config); - const root = reRequire(pathRoot); - - root('hello'); - - mockRequire.stop(pathConfig); - - t.ok(config.calledWith('root'), 'should call config'); - t.end(); -}); - test('cloudcmd: root: mellow', (t) => { const config = stub().returns(''); const pathToWin = stub(); diff --git a/server/route.js b/server/route.js index fcafb8be..1e014205 100644 --- a/server/route.js +++ b/server/route.js @@ -14,6 +14,7 @@ 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'); @@ -22,6 +23,8 @@ const CloudFunc = require(DIR_COMMON + 'cloudfunc'); const prefix = squad(prefixer, apart(config, 'prefix')); +const onceRequire = once(require); + const sendIndex = (params, data) => { const ponseParams = { ...params, @@ -36,36 +39,35 @@ const {FS} = CloudFunc; const Columns = require(`${DIR_SERVER}/columns`); const Template = require(`${DIR_SERVER}/template`); -const getReadDir = () => { +const tokenize = (fn, a) => (b) => fn(a, b); +const getReadDir = (config) => { if (!config('dropbox')) return promisify(flop.read); - const tokenize = (fn, a) => (b) => fn(a, b); - const {readDir} = require('@cloudcmd/dropbox'); + const {readDir} = onceRequire('@cloudcmd/dropbox'); return tokenize(readDir, config('dropboxToken')); }; -const read = getReadDir(); const realpath = promisify(fs.realpath); /** * routing of server queries */ -module.exports = currify((options, request, response, next) => { +module.exports = currify((config, options, request, response, next) => { const name = ponse.getPathName(request); const isFS = RegExp('^/$|^' + FS).test(name); if (!isFS) return next(); - route(options, request, response) + route({config, options, request, response}) .catch(next); }); module.exports._getReadDir = getReadDir; -async function route(options, request, response) { +async function route({config, options, request, response}) { const name = ponse.getPathName(request); const gzip = true; const p = { @@ -78,13 +80,14 @@ async function route(options, request, response) { config('prefix', prefixer(request.baseUrl)); const rootName = name.replace(CloudFunc.FS, '') || '/'; - const fullPath = root(rootName); + const fullPath = root(rootName, config('root')); + const read = getReadDir(config); const [error, dir] = await tryToCatch(read, fullPath); const {html} = options; if (!error) - return sendIndex(p, buildIndex(html, { + return sendIndex(p, buildIndex(config, html, { ...dir, path: format.addSlashToEnd(rootName), })); @@ -104,7 +107,7 @@ async function route(options, request, response) { /** * additional processing of index file */ -function indexProcessing(options) { +function indexProcessing(config, options) { const oneFilePanel = config('oneFilePanel'); const noKeysPanel = !config('keysPanel'); const noContact = !config('contact'); @@ -177,14 +180,14 @@ function indexProcessing(options) { return data; } -function buildIndex(html, json) { +function buildIndex(config, html, json) { const panel = CloudFunc.buildFromJSON({ data: json, prefix: prefix(), template: Template, }); - return indexProcessing({ + return indexProcessing(config, { panel, data: html, }); diff --git a/server/route.spec.js b/server/route.spec.js index f9446b66..cd99868f 100644 --- a/server/route.spec.js +++ b/server/route.spec.js @@ -14,6 +14,7 @@ const routePath = './route'; const cloudcmdPath = './cloudcmd'; const cloudcmd = require(cloudcmdPath); +const {createConfigManager} = cloudcmd; const serveOnce = require('serve-once'); const defaultConfig = { auth: false, @@ -476,21 +477,15 @@ test('cloudcmd: route: buttons: contact', async (t) => { }); test('cloudcmd: route: dropbox', async (t) => { - const config = require('./config'); - const dropbox = config('dropbox'); - const dropboxToken = config('dropboxToken'); - + const config = createConfigManager(); config('dropbox', true); config('dropboxToken', ''); const {_getReadDir} = reRequire(routePath); - const readdir = _getReadDir(); + const readdir = _getReadDir(config); const [e] = await tryToCatch(readdir, '/root'); - config('dropbox', dropbox); - config('dropboxToken', dropboxToken); - t.ok(/token/.test(e.message), 'should contain word token in message'); t.end(); }); diff --git a/server/server.js b/server/server.js index 1c83f2b6..23d35b63 100644 --- a/server/server.js +++ b/server/server.js @@ -57,6 +57,9 @@ module.exports = async (options) => { app.use(prefix, cloudcmd({ config: options, socket: socketServer, + configManager: cloudcmd.createConfigManager({ + filename: cloudcmd.configPath, + }), })); if (port < 0 || port > 65535) diff --git a/server/terminal.js b/server/terminal.js index 5be754d0..9d1e9c35 100644 --- a/server/terminal.js +++ b/server/terminal.js @@ -1,17 +1,15 @@ 'use strict'; const tryCatch = require('try-catch'); -const config = require('./config'); const noop = (req, res, next) => { next && next(); }; + noop.listen = noop; -module.exports = (arg) => getTerminal(config('terminal'), arg); - -function getTerminal(term, arg) { - if (!term) +module.exports = (config, arg) => { + if (!config('terminal')) return noop; const [e, terminalModule] = tryCatch(require, config('terminalPath')); @@ -26,5 +24,5 @@ function getTerminal(term, arg) { console.log(`cloudcmd --terminal: ${e.message}`); return noop; -} +}; diff --git a/test/server/terminal.js b/server/terminal.spec.js similarity index 53% rename from test/server/terminal.js rename to server/terminal.spec.js index e734c85b..2b6d8f99 100644 --- a/test/server/terminal.js +++ b/server/terminal.spec.js @@ -4,33 +4,26 @@ const test = require('supertape'); const stub = require('@cloudcmd/stub'); const mockRequire = require('mock-require'); -const {reRequire} = mockRequire; -const configPath = '../../server/config'; -const terminalPath = '../../server/terminal'; +const terminalPath = './terminal'; +const terminal = require('./terminal'); +const {createConfigManager} = require('./cloudcmd'); test('cloudcmd: terminal: disabled', (t) => { - mockRequire(configPath, () => { - return false; - }); + const config = createConfigManager(); + config('terminal', false); - const terminal = reRequire('../../server/terminal'); - const fn = terminal(); - - mockRequire.stop(configPath); + const fn = terminal(config); t.notOk(fn(), 'should return noop'); t.end(); }); test('cloudcmd: terminal: disabled: listen', (t) => { - mockRequire(configPath, () => false); + const config = createConfigManager(); + config('terminal', false); - const terminal = reRequire(terminalPath); - const fn = terminal().listen(); - - mockRequire.stop(configPath); - reRequire(terminalPath); + const fn = terminal(config).listen(); t.notOk(fn, 'should return noop'); t.end(); @@ -40,15 +33,11 @@ test('cloudcmd: terminal: enabled', (t) => { const term = stub(); const arg = 'hello'; - mockRequire(configPath, () => '/terminal'); mockRequire(terminalPath, term); const terminal = require(terminalPath); terminal(arg); - mockRequire.stop(configPath); - mockRequire.stop(terminalPath); - t.ok(term.calledWith(arg), 'should call terminal'); t.end(); }); @@ -57,18 +46,16 @@ test('cloudcmd: terminal: enabled: no string', (t) => { const {log:originalLog} = console; const log = stub(); - mockRequire(configPath, () => 'hello'); - console.log = log; - const terminal = reRequire(terminalPath); - terminal(); + const config = createConfigManager(); + + config('terminal', true); + config('terminalPath', 'hello'); + terminal(config); + console.log = originalLog; - mockRequire.stop(configPath); - reRequire(terminalPath); - const msg = 'cloudcmd --terminal: Cannot find module \'hello\''; - const [arg] = log.args[0]; t.ok(arg.includes(msg), 'should call with msg'); @@ -79,19 +66,13 @@ test('cloudcmd: terminal: no arg', (t) => { const gritty = {}; mockRequire('gritty', gritty); - mockRequire(configPath, (a) => { - if (a === 'terminal') - return true; - - return 'gritty'; - }); + const config = createConfigManager(); + config('terminal', true); + config('terminalPath', 'gritty'); - const terminal = reRequire(terminalPath); - const result = terminal(); + const result = terminal(config); mockRequire.stop('gritty'); - mockRequire.stop(configPath); - reRequire(terminalPath); t.equal(result, gritty, 'should equal'); t.end(); diff --git a/server/validate.js b/server/validate.js index 8a091605..e87ce5a5 100644 --- a/server/validate.js +++ b/server/validate.js @@ -2,11 +2,10 @@ const tryCatch = require('try-catch'); -const config = require('./config'); const exit = require('./exit'); const columns = require('./columns'); -module.exports.root = (dir, fn) => { +module.exports.root = (dir, config) => { if (typeof dir !== 'string') throw Error('dir should be a string'); @@ -21,9 +20,6 @@ module.exports.root = (dir, fn) => { if (error) return exit('cloudcmd --root: %s', error.message); - - if (typeof fn === 'function') - fn('root:', dir); }; module.exports.editor = (name) => { diff --git a/server/validate.spec.js b/server/validate.spec.js index ba2ab305..a82fe004 100644 --- a/server/validate.spec.js +++ b/server/validate.spec.js @@ -14,7 +14,6 @@ const validatePath = `${dir}/server/validate`; const exitPath = `${dir}/server/exit`; const columnsPath = `${dir}/server/columns`; const cloudcmdPath = `${dir}/server/cloudcmd`; -const configPath = `${dir}/server/config`; const validate = require(validatePath); const cloudcmd = require(cloudcmdPath); @@ -30,17 +29,11 @@ test('validate: root: bad', (t) => { }); test('validate: root: config', (t) => { - const configFn = stub() - .returns(true); + const config = stub().returns(true); - mockRequire(configPath, configFn); + validate.root('/hello', config); - const validate = reRequire(validatePath); - validate.root('/hello'); - - mockRequire.stop(configPath); - - t.ok(configFn.calledWith('dropbox'), 'should call config'); + t.ok(config.calledWith('dropbox'), 'should call config'); t.end(); }); @@ -52,17 +45,6 @@ test('validate: root: /', (t) => { t.end(); }); -test('validate: root: /home', (t) => { - const fn = stub(); - - validate.root('/home', (...args) => { - fn(...args); - - t.ok(fn.calledWith('root:', '/home'), 'should not call fn'); - t.end(); - }); -}); - test('validate: root: stat', (t) => { const fn = stub(); const {statSync} = fs; diff --git a/test/rest/config.js b/test/rest/config.js index 57ffb01f..3ae84f4b 100644 --- a/test/rest/config.js +++ b/test/rest/config.js @@ -12,6 +12,7 @@ const {request} = require('serve-once')(cloudcmd, { config: { auth: false, }, + configManager: cloudcmd.createConfigManager(), }); const manageConfig = require('../../server/config'); diff --git a/test/rest/cp.js b/test/rest/cp.js index d2d3cb16..e3bce034 100644 --- a/test/rest/cp.js +++ b/test/rest/cp.js @@ -11,8 +11,12 @@ const config = { }; const cloudcmd = require('../..'); +const configManager = cloudcmd.createConfigManager(); +configManager('auth', false); + const {request} = require('serve-once')(cloudcmd, { config, + configManager, }); test('cloudcmd: rest: cp', async (t) => { diff --git a/test/rest/fs.js b/test/rest/fs.js index 9659b708..8fd0497d 100644 --- a/test/rest/fs.js +++ b/test/rest/fs.js @@ -3,7 +3,11 @@ const test = require('supertape'); const cloudcmd = require('../..'); -const {request} = require('serve-once')(cloudcmd); +const {request} = require('serve-once')(cloudcmd, { + config: { + auth: false, + }, +}); test('cloudcmd: rest: fs: path', async (t) => { const {body} = await request.get(`/api/v1/fs`, { diff --git a/test/rest/mv.js b/test/rest/mv.js index 58ada557..3ee8031e 100644 --- a/test/rest/mv.js +++ b/test/rest/mv.js @@ -33,11 +33,14 @@ test('cloudcmd: rest: mv', async (t) => { reRequire(restPath); const cloudcmd = reRequire(cloudcmdPath); + const {createConfigManager} = cloudcmd; + + const configManager = createConfigManager(); + configManager('auth', false); + configManager('root', '/'); const {request} = serveOnce(cloudcmd, { - config: { - root: '/', - }, + configManager, }); const files = { @@ -77,11 +80,14 @@ test('cloudcmd: rest: mv: rename', async (t) => { reRequire(restPath); const cloudcmd = reRequire(cloudcmdPath); + const {createConfigManager} = cloudcmd; + const configManager = createConfigManager(); + + configManager('auth', false); + configManager('root', '/'); const {request} = serveOnce(cloudcmd, { - config: { - root: '/', - }, + configManager, }); const files = { diff --git a/test/rest/pack.js b/test/rest/pack.js index f90a1b60..276ffe6c 100644 --- a/test/rest/pack.js +++ b/test/rest/pack.js @@ -25,6 +25,7 @@ const defaultOptions = { }, }; const cloudcmd = require(cloudcmdPath); + const serveOnce = require('serve-once'); const {request} = serveOnce(cloudcmd); @@ -37,6 +38,7 @@ const once = promisify((name, extract, fn) => { test('cloudcmd: rest: pack: tar: get', async (t) => { const config = { packer: 'tar', + auth: false, }; const options = { @@ -202,6 +204,7 @@ test('cloudcmd: rest: pack: zip: put: response', async (t) => { test('cloudcmd: rest: pack: zip: put: error', async (t) => { const config = { packer: 'zip', + auth: false, }; const options = { diff --git a/test/rest/rest.js b/test/rest/rest.js deleted file mode 100644 index 70da208a..00000000 --- a/test/rest/rest.js +++ /dev/null @@ -1,49 +0,0 @@ -'use strict'; - -const test = require('supertape'); -const rest = require('../../server/rest'); - -const { - _formatMsg, - _getWin32RootMsg, - _isRootWin32, - _isRootAll, -} = rest; - -test('rest: formatMsg', (t) => { - const result = _formatMsg('hello', 'world'); - - t.equal(result, 'hello: ok("world")', 'should be equal'); - t.end(); -}); - -test('rest: formatMsg: json', (t) => { - const result = _formatMsg('hello', { - name: 'world', - }); - - t.equal(result, 'hello: ok("{"name":"world"}")', 'should parse json'); - t.end(); -}); - -test('rest: getWin32RootMsg', (t) => { - const {message} = _getWin32RootMsg(); - - t.equal(message,'Could not copy from/to root on windows!', 'should return error'); - t.end(); -}); - -test('rest: isRootWin32', (t) => { - const result = _isRootWin32('/'); - - t.notOk(result, 'should equal'); - t.end(); -}); - -test('rest: isRootAll', (t) => { - const result = _isRootAll(['/', '/h']); - - t.notOk(result, 'should equal'); - t.end(); -}); -