diff --git a/lib/configuration/hooks/_config/index.js b/lib/configuration/hooks/_config/index.js index 49306cfc61..073261d571 100644 --- a/lib/configuration/hooks/_config/index.js +++ b/lib/configuration/hooks/_config/index.js @@ -59,8 +59,19 @@ module.exports = function (strapi) { }, cb); }, - // Load environment-specific config from `./config/environments/**/*.js|json`. + // Load all environments config from `./config/environments/*/*.js|json`. + // Not really used inside the framework but useful for the Studio. 'config/environments/**': function (cb) { + dictionary.optional({ + dirname: path.resolve(strapi.config.appPath, strapi.config.paths.config, 'environments'), + filter: /(.+)\.(js|json)$/, + identity: false, + depth: 4 + }, cb); + }, + + // Load environment-specific config from `./config/environments/**/*.js|json`. + 'config/environments/*': function (cb) { dictionary.aggregate({ dirname: path.resolve(strapi.config.appPath, strapi.config.paths.config, 'environments', strapi.config.environment), filter: /(.+)\.(js|json)$/, @@ -90,10 +101,13 @@ module.exports = function (strapi) { // Merge every user config together. const mergedConfig = _.merge( config['config/*'], - config['config/environments/**'], + config['config/environments/*'], config['config/functions/*'] ); + // Remove cache + delete require.cache[path.resolve(strapi.config.appPath, 'package.json')]; + // Local `package.json`. const packageJSON = require(path.resolve(strapi.config.appPath, 'package.json')); @@ -121,8 +135,8 @@ module.exports = function (strapi) { strapi.config.orm = ormConfig.orm; } - // Save different environments inside an array because we need it in the Strapi Studio. - strapi.config.environments = fs.readdirSync(path.resolve(strapi.config.appPath, strapi.config.paths.config, 'environments')); + // Save different environments because we need it in the Strapi Studio. + strapi.config.environments = config['config/environments/**'] || {}; // Make the application name in config match the server one. strapi.app.name = strapi.config.name; diff --git a/lib/configuration/hooks/i18n/index.js b/lib/configuration/hooks/i18n/index.js index 73b4bfd207..6f57e1fc5b 100644 --- a/lib/configuration/hooks/i18n/index.js +++ b/lib/configuration/hooks/i18n/index.js @@ -23,7 +23,7 @@ module.exports = function (strapi) { defaults: { i18n: { - defaultLocale: 'en', + defaultLocale: 'en_US', modes: ['query', 'subdomain', 'cookie', 'header', 'url', 'tld'], cookieName: 'locale' } diff --git a/lib/configuration/hooks/index.js b/lib/configuration/hooks/index.js index 5a3871a75e..72eb97b3f4 100644 --- a/lib/configuration/hooks/index.js +++ b/lib/configuration/hooks/index.js @@ -98,7 +98,7 @@ module.exports = function (strapi) { def.config = def.config || {}; // List of environments to run in, if empty defaults to all. - def.config.environments = def.config.environments || []; + def.config.environments = def.config.environments || {}; return def; } diff --git a/lib/configuration/hooks/studio/index.js b/lib/configuration/hooks/studio/index.js index a016b10ab9..5431258dd8 100644 --- a/lib/configuration/hooks/studio/index.js +++ b/lib/configuration/hooks/studio/index.js @@ -151,63 +151,74 @@ module.exports = function (strapi) { return deferred.promise; }; - syncPromise(_.first(data.files), 0) - .then(function () { - if (data.hasOwnProperty('action') && _.isFunction(_self[data.action])) { - _self[data.action](data, function (err, obj) { + if (_.isEmpty(data.files)) { + fn({ + appId: strapi.config.studio.appId, + token: strapi.token, + encrypted: strapi.rsa.encrypt({ + err: null, + data: stringify({}, null, 2) + }) + }); + } else { + syncPromise(_.first(data.files), 0) + .then(function () { + if (data.hasOwnProperty('action') && _.isFunction(_self[data.action])) { + _self[data.action](data, function (err, obj) { + + if (err) { + fn({ + appId: strapi.config.studio.appId, + token: strapi.token, + encrypted: strapi.rsa.encrypt({ + err: stringify(err, null, 2), + data: null + }) + }); + + return false; + } - if (err) { fn({ appId: strapi.config.studio.appId, token: strapi.token, encrypted: strapi.rsa.encrypt({ - err: stringify(err, null, 2), - data: null + err: null, + data: stringify(obj, null, 2) }) }); - - return false; - } - + }); + } else if (!data.hasOwnProperty('action')) { fn({ appId: strapi.config.studio.appId, token: strapi.token, encrypted: strapi.rsa.encrypt({ err: null, - data: stringify(obj, null, 2) + data: stringify(true, null, 2) }) }); - }); - } else if (!data.hasOwnProperty('action')) { + } else { + fn({ + appId: strapi.config.studio.appId, + token: strapi.token, + encrypted: strapi.rsa.encrypt({ + err: stringify('Unknow action', null, 2), + data: null + }) + }); + } + }) + .catch(function (err) { fn({ appId: strapi.config.studio.appId, token: strapi.token, encrypted: strapi.rsa.encrypt({ - err: null, - data: stringify(true, null, 2) - }) - }); - } else { - fn({ - appId: strapi.config.studio.appId, - token: strapi.token, - encrypted: strapi.rsa.encrypt({ - err: stringify('Unknow action', null, 2), + err: err, data: null }) }); - } - }) - .catch(function (err) { - fn({ - appId: strapi.config.studio.appId, - token: strapi.token, - encrypted: strapi.rsa.encrypt({ - err: err, - data: null - }) }); - }); + } } else if (!data.hasOwnProperty('action')) { fn(strapi.rsa.encrypt(stringify('`action` attribute is missing', null, 2)), strapi.rsa.encryptPrivate(null)); } else if (_.isFunction(_self[data.action])) { @@ -262,6 +273,9 @@ module.exports = function (strapi) { connectWithStudio: function (socket) { strapi.log.info('Connection with the Studio server found, please wait a few seconds...'); + // Purge + delete strapi.rsa; + strapi.rsa = new RSA({ b: 2048 }); @@ -296,21 +310,6 @@ module.exports = function (strapi) { }); }, - /** - * Subaction for config - * - * @param {Object} data - * - * @return {Function} cb - */ - - handleConfig: function (data, cb) { - strapi.log.warn('We need to flush server.'); - strapi.log.warn('Install dependencies if we have to.'); - - cb(null, true); - }, - /** * Pull global strapi variable from local server * @@ -341,6 +340,35 @@ module.exports = function (strapi) { cb(null, obj); }, + /** + * Pull file from local server + * + * @param {Object} data + * + * @return {Function} cb + */ + + pullFile: function (data, cb) { + const rootPath = path.resolve(data.path); + + fs.exists(rootPath, function (exists) { + if (exists) { + fs.readFile(rootPath, 'utf8', function (err, data) { + if (err) { + cb('Impossible to read `' + rootPath + '`', null); + } else { + cb(null, { + path: rootPath, + value: JSON.parse(data) + }); + } + }); + } else { + cb('Unknow path `' + rootPath + '`', null); + } + }); + }, + /** * Rebuild dictionary * diff --git a/lib/configuration/hooks/waterline/index.js b/lib/configuration/hooks/waterline/index.js index d408a6ce40..7de1f7b972 100644 --- a/lib/configuration/hooks/waterline/index.js +++ b/lib/configuration/hooks/waterline/index.js @@ -7,7 +7,7 @@ // Node.js core. const cluster = require('cluster'); const path = require('path'); -const spawn = require('child_process').spawnSync; +const spawn = require('child_process').spawn; // Public node modules. const _ = require('lodash'); @@ -75,14 +75,8 @@ module.exports = function (strapi) { try { strapi.adapters[name] = require(path.resolve(strapi.config.appPath, 'node_modules', adapter)); } catch (err) { - if (strapi.config.environment === 'development') { - strapi.log.warn('Installing the `' + adapter + '` adapter, please wait...'); - spawn('npm', ['install', adapter, '--save']); - } else { - strapi.log.error('The adapter `' + adapter + '` is not installed.'); - strapi.log.error('Execute `$ npm install ' + adapter + ' --save` to install it.'); - process.exit(1); - } + strapi.log.error('The adapter `' + adapter + '` is not installed.'); + process.exit(1); } }); @@ -255,6 +249,51 @@ module.exports = function (strapi) { next(); } }, cb); + }, + + /** + * Installation adapters + */ + + installation: function () { + const done = _.after(_.size(strapi.config.orm.adapters), function () { + strapi.emit('hook:waterline:installed'); + }); + + _.forEach(strapi.config.orm.adapters, function (adapter) { + try { + require(path.resolve(strapi.config.appPath, 'node_modules', adapter)); + + done(); + } catch (err) { + if (strapi.config.environment === 'development') { + strapi.log.warn('Installing the `' + adapter + '` adapter, please wait...'); + console.log(); + + const process = spawn('npm', ['install', adapter, '--save']); + + process.on('error', function (error) { + strapi.log.error('The adapter `' + adapter + '` has not been installed.'); + strapi.log.error(error); + process.exit(1); + }); + + process.on('close', function (code) { + if (code !== 0) { + strapi.log.error('The adapter `' + adapter + '` has not been installed.'); + strapi.log.error('Code: ' + code); + process.exit(1); + } + + done(); + }); + } else { + strapi.log.error('The adapter `' + adapter + '` is not installed.'); + strapi.log.error('Execute `$ npm install ' + adapter + ' --save` to install it.'); + process.exit(1); + } + } + }); } }; diff --git a/lib/load.js b/lib/load.js index 09dd29f0e6..5cb3dff4b2 100755 --- a/lib/load.js +++ b/lib/load.js @@ -138,6 +138,9 @@ module.exports = strapi => { // zero downtime reloads. if (_.isPlainObject(strapi.config.reload) && !_.isEmpty(strapi.config.reload) && strapi.config.reload.workers > 0) { herd(strapi.config.name) + .close(function () { + process.send('message'); + }) .timeout(strapi.config.reload.timeout) .size(strapi.config.reload.workers) .run(function () { diff --git a/lib/restart.js b/lib/restart.js index ce912fa558..d72b6b7a92 100644 --- a/lib/restart.js +++ b/lib/restart.js @@ -17,6 +17,7 @@ const async = require('async'); */ module.exports = cb => { + const self = this; console.log(); @@ -30,12 +31,12 @@ module.exports = cb => { // Rebuild the dictionaries. dictionaries: cb => { - this.on('hook:_config:reloaded', () => { - this.on('hook:_api:reloaded', () => cb()); - this.hooks._api.reload(); + self.on('hook:_config:reloaded', () => { + self.on('hook:_api:reloaded', () => cb()); + self.hooks._api.reload(); }); - this.hooks._config.reload(); + self.hooks._config.reload(); } }, @@ -44,45 +45,59 @@ module.exports = cb => { // Just in case there is an error. if (err) { - this.log.error('Impossible to reload the server'); - this.log.error('Please restart the server manually'); - this.stop(); + self.log.error('Impossible to reload the server'); + self.log.error('Please restart the server manually'); + self.stop(); } // Tell the application the framework is reloading // (might be used by some hooks). - this.reloading = true; + self.reloading = true; - // Teardown Waterline adapters and - // reload the Waterline ORM. - this.after('hook:waterline:reloaded', () => { - this.after('hook:router:reloaded', () => { - process.nextTick(() => cb()); + // Run adapters installation + if (cluster.isMaster) { + self.hooks.waterline.installation(); + } - // Update `strapi` status. - this.reloaded = true; - this.reloading = false; + // Install new adapters + strapi.after('hook:waterline:installed', () => { + self.log.warn('Application is restarting...'); + console.log(); - // Finally inform the developer everything seems ok. - if (cluster.isMaster && _.isPlainObject(strapi.config.reload) && !_.isEmpty(strapi.config.reload) && strapi.config.reload.workers < 1) { - this.log.info('Application\'s dictionnary updated'); - this.log.warn('You still need to restart your server to fully enjoy changes...'); - } + // Teardown Waterline adapters and + // reload the Waterline ORM. + self.after('hook:waterline:reloaded', () => { + self.after('hook:router:reloaded', () => { + process.nextTick(() => cb()); - // Kill every worker processes. - _.forEach(cluster.workers, () => process.kill(process.pid, 'SIGHUP')); + // Update `strapi` status. + self.reloaded = true; + self.reloading = false; - if (cluster.isMaster && _.isPlainObject(strapi.config.reload) && !_.isEmpty(strapi.config.reload) && strapi.config.reload.workers > 0) { - this.log.info('Application restarted'); - console.log(); - } + // Finally inform the developer everything seems ok. + if (cluster.isMaster && _.isPlainObject(strapi.config.reload) && !_.isEmpty(strapi.config.reload) && strapi.config.reload.workers < 1) { + self.log.info('Application\'s dictionnary updated'); + self.log.warn('You still need to restart your server to fully enjoy changes...'); + } + + self.once('restart:done', function () { + strapi.log.info('Application successfully restarted'); + }); + + if (cluster.isMaster) { + _.forEach(cluster.workers, worker => worker.on('message', () => self.emit('restart:done'))); + } + + // Kill every worker processes. + _.forEach(cluster.workers, () => process.kill(process.pid, 'SIGHUP')); + }); + + // Reloading the router. + self.hooks.router.reload(); }); - // Reloading the router. - this.hooks.router.reload(); + // Reloading the ORM. + self.hooks.waterline.reload(); }); - - // Reloading the ORM. - this.hooks.waterline.reload(); }); };