diff --git a/packages/strapi-plugin-setings-manager/admin/src/translations/en.json b/packages/strapi-plugin-setings-manager/admin/src/translations/en.json index 9e26dfeeb6..0d5058c6af 100644 --- a/packages/strapi-plugin-setings-manager/admin/src/translations/en.json +++ b/packages/strapi-plugin-setings-manager/admin/src/translations/en.json @@ -1 +1,4 @@ -{} \ No newline at end of file +{ + "menu.section.global-settings": "Global settings", + "menu.section.environments": "Environments" +} diff --git a/packages/strapi-plugin-setings-manager/config/routes.json b/packages/strapi-plugin-setings-manager/config/routes.json index d0cfcd0846..b595155005 100644 --- a/packages/strapi-plugin-setings-manager/config/routes.json +++ b/packages/strapi-plugin-setings-manager/config/routes.json @@ -2,8 +2,48 @@ "routes": [ { "method": "GET", - "path": "/", - "handler": "SettingsManager.index", + "path": "/menu", + "handler": "SettingsManager.menu", + "config": { + "policies": [] + } + }, + { + "method": "GET", + "path": "/environments", + "handler": "SettingsManager.environments", + "config": { + "policies": [] + } + }, + { + "method": "GET", + "path": "/:slug", + "handler": "SettingsManager.get", + "config": { + "policies": [] + } + }, + { + "method": "GET", + "path": "/:slug/:env", + "handler": "SettingsManager.get", + "config": { + "policies": [] + } + }, + { + "method": "PUT", + "path": "/:slug", + "handler": "SettingsManager.update", + "config": { + "policies": [] + } + }, + { + "method": "PUT", + "path": "/:slug/:env", + "handler": "SettingsManager.update", "config": { "policies": [] } diff --git a/packages/strapi-plugin-setings-manager/controllers/SettingsManager.js b/packages/strapi-plugin-setings-manager/controllers/SettingsManager.js index 8234cde92f..bb77c74439 100644 --- a/packages/strapi-plugin-setings-manager/controllers/SettingsManager.js +++ b/packages/strapi-plugin-setings-manager/controllers/SettingsManager.js @@ -1,25 +1,54 @@ 'use strict'; -/** - * SettingsManager.js controller - * - * @description: A set of functions called "actions" of the `settings-manager` plugin. - */ - module.exports = { + menu: async ctx => { + const Service = strapi.plugins['settings-manager'].services.settingsmanager; - /** - * Default action. - * - * @return {Object} - */ + ctx.send(Service.menu); + }, - index: async (ctx) => { - // Add your own logic here. + environments: async ctx => { + const Service = strapi.plugins['settings-manager'].services.settingsmanager; - // Send 200 `ok` - ctx.send({ - message: 'ok' - }); - } + ctx.send({ environments: Service.getEnvironments() }); + }, + + get: async ctx => { + const Service = strapi.plugins['settings-manager'].services.settingsmanager; + const { slug, env } = ctx.params; + + if (env && _.isEmpty(_.find(Service.getEnvironments(), { name: env }))) return ctx.badData('request.error.environment'); + + const model = env ? Service[slug](env) : Service[slug]; + + if (_.isUndefined(model)) return ctx.badData('request.error.config'); + + ctx.send(model); + }, + + update: async ctx => { + const Service = strapi.plugins['settings-manager'].services.settingsmanager; + const { slug, env } = ctx.params; + let params = ctx.request.body; + + if (env && _.isEmpty(_.find(Service.getEnvironments(), { name: env }))) return ctx.badData('request.error.environment'); + + const model = env ? Service[slug](env) : Service[slug]; + + if (_.isUndefined(config)) return ctx.badData('request.error.config'); + + const items = Service.getItems(model); + + params = Service.cleanParams(params, items); + + let validationErrors = Service.paramsValidation(params, items); + + if (!_.isEmpty(validationErrors)) { + return ctx.badData(null, validationErrors); + } + + Service.updateSettings(params, items, env); + + ctx.send(); + }, }; diff --git a/packages/strapi-plugin-setings-manager/package.json b/packages/strapi-plugin-setings-manager/package.json index 2ef3a54941..eadc3f2b32 100644 --- a/packages/strapi-plugin-setings-manager/package.json +++ b/packages/strapi-plugin-setings-manager/package.json @@ -1,7 +1,7 @@ { "name": "strapi-plugin-settings-manager", - "version": "0.0.0", - "description": "This is the description of the plugin.", + "version": "0.1.0", + "description": "Strapi plugin to manage settings.", "strapi": { "name": "settings-manager", "icon": "ion-document-text", @@ -24,20 +24,21 @@ "test": "echo Tests are not implemented.", "prepublish": "npm run build" }, - "dependencies": {}, + "dependencies": { + }, "devDependencies": { "strapi-helper-plugin": "3.0.0-alpha.3" }, "author": { - "name": "A Strapi developer", - "email": "", - "url": "" + "name": "Strapi team", + "email": "hi@strapi.io", + "url": "http://strapi.io" }, "maintainers": [ { - "name": "A Strapi developer", - "email": "", - "url": "" + "name": "Strapi team", + "email": "hi@strapi.io", + "url": "http://strapi.io" } ], "engines": { diff --git a/packages/strapi-plugin-setings-manager/services/SettingsManager.js b/packages/strapi-plugin-setings-manager/services/SettingsManager.js index 0d23e9045b..35c4fc8ee8 100644 --- a/packages/strapi-plugin-setings-manager/services/SettingsManager.js +++ b/packages/strapi-plugin-setings-manager/services/SettingsManager.js @@ -1,11 +1,344 @@ 'use strict'; -/** - * SettingsManager.js service - * - * @description: A set of functions similar to controller's actions to avoid code duplication. - */ +const fs = require('fs'); +const path = require('path'); module.exports = { + menu: { + sections: [ + { + name: 'menu.section.global-settings', + items: [ + { + slug: 'general', + name: 'menu.item.general', + icon: 'globe' + }, + { + slug: 'languages', + name: 'menu.item.languages', + icon: 'language' + }, + { + slug: 'advenced', + name: 'menu.item.advenced', + icon: 'cogs' + } + ] + }, + { + name: 'menu.section.environments', + items: [ + { + slug: 'databases', + name: 'menu.item.databases', + icon: 'databases' + }, + { + slug: 'security', + name: 'menu.item.security', + icon: 'shield' + }, + { + slug: 'server', + name: 'menu.item.server', + icon: 'server' + } + ] + } + ] + }, + general: { + name: 'form.general', + description: 'form.general.desc', + sections: [ + { + name: '', + items: [ + { + name: 'form.general.name', + target: 'package.name', + type: 'string', + value: strapi.config.name, + validations : { + maxLength: 255, + required: true + } + }, + { + name: 'form.general.description', + target: 'package.description', + type: 'string', + value: strapi.config.description, + validations : { + maxLength: 255, + required: true + } + }, + { + name: 'form.general.version', + target: 'package.version', + type: 'string', + value: strapi.config.version, + validations : { + maxLength: 255, + required: true + } + } + ] + } + ] + }, + + security: env => { + return { + name: 'form.security', + description: 'form.security.desc', + sections: [ + { + name: 'form.security.session', + items: [ + { + name: 'form.security.session.key', + target: 'security.session.key', + type: 'string', + value: strapi.config.environments[env].security.session.key + }, + { + name: 'form.security.session.maxAge', + target: 'security.session.maxAge', + type: 'number', + value: strapi.config.environments[env].security.session.maxAge + } + ] + }, + { + name: '', + items: [ + { + name: 'form.security.xframe', + target: 'security.xframe', + type: 'enum', + value: strapi.config.environments[env].security.xframe, + items: [ + { + name: 'server.xframe.deny', + value: 'DENY', + }, + { + name: 'server.xframe.sameorigin', + value: 'SAMEORIGIN', + }, + { + name: 'server.xframe.allow-from', + value: 'ALLOW-FROM', + }, + ] + }, + { + name: 'form.security.xssProtection', + target: 'security.xssProtection', + type: 'boolean', + value: strapi.config.environments[env].security.xssProtection + } + ] + }, + { + name: 'form.security.cors', + items: [ + { + name: 'form.security.cors.origin', + target: 'security.cors.origin', + type: 'string', + value: strapi.config.environments[env].security.cors.origin, + } + ] + } + ] + }; + }, + + server: env => { + return { + name: 'form.server', + description: 'form.server.desc', + sections: [ + { + name: '', + items: [ + { + name: 'form.server.host', + target: 'server.host', + type: 'string', + value: strapi.config.environments[env].server.host + }, + { + name: 'form.server.port', + target: 'server.port', + type: 'number', + value: strapi.config.environments[env].server.port + } + ] + }, + { + name: 'form.server.parser', + items: [ + { + name: 'form.server.parser.xframe', + target: 'server.xframe', + type: 'enum', + value: strapi.config.environments[env].server.xframe, + items: [ + { + name: 'server.xframe.deny', + value: 'DENY', + }, + { + name: 'server.xframe.sameorigin', + value: 'SAMEORIGIN', + }, + { + name: 'server.xframe.allow-from', + value: 'ALLOW-FROM', + }, + ] + }, + { + name: 'form.server.xssProtection', + target: 'server.xssProtection', + type: 'boolean', + value: strapi.config.environments[env].server.xssProtection + } + ] + }, + { + name: 'form.server.cors', + items: [ + { + name: 'form.server.cors.origin', + target: 'server.cors.origin', + type: 'string', + value: strapi.config.environments[env].server.cors.origin + } + ] + } + ] + }; + }, + + getEnvironments: () => { + return _.map(_.keys(strapi.config.environments), environment => { + return { + name: environment, + active: (strapi.config.environment === environment) + } + }); + }, + + getItems: model => { + let items = []; + _.forEach(model.sections, section => items = _.concat(items, section.items)); + + return items; + }, + + cleanParams: (params, items) => { + const cleanParams = {}; + + _.forEach(items, ({ target }) => _.has(params, target) ? _.set(cleanParams, target, _.get(params, target)) : ''); + + return cleanParams; + }, + + paramsValidation: (params, items) => { + let errors = []; + + const checkType = (input, { type, target }) => { + if ((type === 'string' || type === 'text') && !_.isString(input)) errors.push({ + target: target, + message: 'form.error.type.string' + }); + + if (type === 'number' && !_.isNumber(input)) errors.push({ + target: target, + message: 'form.error.type.number' + }); + + if (type === 'boolean' && !_.isBoolean(input)) errors.push({ + target: target, + message: 'form.error.type.boolean' + }); + }; + + const checkValidations = (input, item) => { + _.forEach(item.validations, (value, key) => { + if (key === 'required' && (_.isNull(input) || _.isEmpty(input) || _.isUndefined(input))) errors.push({ + target: item.target, + message: 'form.error.validation.required' + }); + + if (key === 'max' && parseInt(input) > value) errors.push({ + target: item.target, + message: 'form.error.validation.max' + }); + + if (key === 'min' && parseInt(input) < value) errors.push({ + target: item.target, + message: 'form.error.validation.min' + }); + + if (key === 'maxLength' && input.length > value) errors.push({ + target: item.target, + message: 'form.error.validation.maxLength' + }); + + if (key === 'minLength' && input.length < value) errors.push({ + target: item.target, + message: 'form.error.validation.minLength' + }); + }); + }; + + _.forEach(items, item => { + if (_.has(params, item.target)) { + const input = _.get(params, item.target, null); + + checkType(input, item) + checkValidations(input, item) + } + }); + + if (!_.isEmpty(errors)) { + const grpTarget = _.groupBy(errors, 'target'); + + errors = _.map(grpTarget, (errs, target) => { + return { + target, + messages: _.map(errs, err => err.message) + } + }); + } + + return errors; + }, + + updateSettings: (params, items, env = '') => { + const appPath = process.cwd(); + + _.forEach(items, ({ target }) => { + if (_.has(params, target)) { + const input = _.get(params, target, null); + const [file, ...objPath] = target.split('.'); + + let filePath = (file === 'package') ? path.join(appPath, 'package.json') : path.join(appPath, 'config', 'environments', env, `${_.replace(file, '.', '/')}.json`); + + const fileContent = require(filePath); + + _.set(fileContent, objPath, input); + + fs.writeFileSync(filePath, JSON.stringify(fileContent, null, 2), 'utf8'); + } + }); + } }; diff --git a/packages/strapi/lib/configuration/hooks/core/responses/policy.js b/packages/strapi/lib/configuration/hooks/core/responses/policy.js index d940e9c771..007ac41289 100644 --- a/packages/strapi/lib/configuration/hooks/core/responses/policy.js +++ b/packages/strapi/lib/configuration/hooks/core/responses/policy.js @@ -51,13 +51,19 @@ module.exports = async function(ctx, next) { } // Empty body is considered as `notFound` response. - if (!ctx.body) { - ctx.notFound(); + if (_.isUndefined(ctx.body) && _.isUndefined(ctx.status)) { + return ctx.notFound(); } - // Format `ctx.body` and `ctx.status`. - ctx.status = ctx.body.isBoom ? ctx.body.output.statusCode : ctx.status; - ctx.body = ctx.body.isBoom ? ctx.body.output.payload : ctx.body; + if (_.isObject(ctx.body)) { + if (ctx.body.isBoom && ctx.body.data) { + ctx.body.output.payload.data = ctx.body.data; + } + + // Format `ctx.body` and `ctx.status`. + ctx.status = ctx.body.isBoom ? ctx.body.output.statusCode : ctx.status; + ctx.body = ctx.body.isBoom ? ctx.body.output.payload : ctx.body; + } // Call custom responses. if (_.isFunction(_.get(strapi.config, `responses.${ctx.status}`))) {