From 59f2e02dc59e6040259ff8d2bf5b80442ec6eacc Mon Sep 17 00:00:00 2001 From: derrickmehaffy Date: Wed, 5 Oct 2022 08:21:34 -0700 Subject: [PATCH 01/27] Create new admin rate limit middleware and rate limit error class Signed-off-by: Derrick Mehaffy --- .../admin/ee/server/routes/features-routes.js | 10 +++++-- packages/core/admin/package.json | 1 + packages/core/admin/server/index.js | 2 ++ .../core/admin/server/middlewares/index.js | 7 +++++ .../admin/server/middlewares/rateLimit.js | 22 ++++++++++++++ .../admin/server/routes/authentication.js | 30 +++++++++++++++---- packages/core/utils/lib/errors.js | 28 +++++++++++------ 7 files changed, 83 insertions(+), 17 deletions(-) create mode 100644 packages/core/admin/server/middlewares/index.js create mode 100644 packages/core/admin/server/middlewares/rateLimit.js diff --git a/packages/core/admin/ee/server/routes/features-routes.js b/packages/core/admin/ee/server/routes/features-routes.js index b9c5f72045..57dd4d4116 100644 --- a/packages/core/admin/ee/server/routes/features-routes.js +++ b/packages/core/admin/ee/server/routes/features-routes.js @@ -12,13 +12,19 @@ module.exports = { method: 'GET', path: '/connect/:provider', handler: 'authentication.providerLogin', - config: { auth: false }, + config: { + auth: false, + middlewares: ['admin::rateLimit'], + }, }, { method: 'POST', path: '/connect/:provider', handler: 'authentication.providerLogin', - config: { auth: false }, + config: { + auth: false, + middlewares: ['admin::rateLimit'], + }, }, { method: 'GET', diff --git a/packages/core/admin/package.json b/packages/core/admin/package.json index 17e22deccf..843bf87891 100644 --- a/packages/core/admin/package.json +++ b/packages/core/admin/package.json @@ -88,6 +88,7 @@ "jsonwebtoken": "8.5.1", "koa-compose": "4.1.0", "koa-passport": "5.0.0", + "koa2-ratelimit": "^1.1.1", "koa-static": "5.0.0", "lodash": "4.17.21", "markdown-it": "^12.3.2", diff --git a/packages/core/admin/server/index.js b/packages/core/admin/server/index.js index d0aad2e764..3a8e78b32c 100644 --- a/packages/core/admin/server/index.js +++ b/packages/core/admin/server/index.js @@ -10,6 +10,7 @@ const routes = require('./routes'); const services = require('./services'); const controllers = require('./controllers'); const contentTypes = require('./content-types'); +const middlewares = require('./middlewares'); module.exports = { register, @@ -21,4 +22,5 @@ module.exports = { services, controllers, contentTypes, + middlewares, }; diff --git a/packages/core/admin/server/middlewares/index.js b/packages/core/admin/server/middlewares/index.js new file mode 100644 index 0000000000..2abf96cbb1 --- /dev/null +++ b/packages/core/admin/server/middlewares/index.js @@ -0,0 +1,7 @@ +'use strict'; + +const rateLimit = require('./rateLimit'); + +module.exports = { + rateLimit, +}; diff --git a/packages/core/admin/server/middlewares/rateLimit.js b/packages/core/admin/server/middlewares/rateLimit.js new file mode 100644 index 0000000000..e7632e7fac --- /dev/null +++ b/packages/core/admin/server/middlewares/rateLimit.js @@ -0,0 +1,22 @@ +'use strict'; + +const utils = require('@strapi/utils'); + +const { RateLimitError } = utils.errors; + +module.exports = + (config, { strapi }) => + async (ctx, next) => { + const ratelimit = require('koa2-ratelimit').RateLimit; + + return ratelimit.middleware({ + interval: { min: 15 }, + max: 5, + prefixKey: `${ctx.request.path}:${ctx.request.ip}`, + handler() { + throw new RateLimitError(); + }, + ...strapi.config.get('admin.ratelimit'), + ...config, + })(ctx, next); + }; diff --git a/packages/core/admin/server/routes/authentication.js b/packages/core/admin/server/routes/authentication.js index 9b031e07ca..80fa2beb37 100644 --- a/packages/core/admin/server/routes/authentication.js +++ b/packages/core/admin/server/routes/authentication.js @@ -5,19 +5,28 @@ module.exports = [ method: 'POST', path: '/login', handler: 'authentication.login', - config: { auth: false }, + config: { + auth: false, + middlewares: ['admin::rateLimit'], + }, }, { method: 'POST', path: '/renew-token', handler: 'authentication.renewToken', - config: { auth: false }, + config: { + auth: false, + middlewares: ['admin::rateLimit'], + }, }, { method: 'POST', path: '/register-admin', handler: 'authentication.registerAdmin', - config: { auth: false }, + config: { + auth: false, + middlewares: ['admin::rateLimit'], + }, }, { method: 'GET', @@ -29,18 +38,27 @@ module.exports = [ method: 'POST', path: '/register', handler: 'authentication.register', - config: { auth: false }, + config: { + auth: false, + middlewares: ['admin::rateLimit'], + }, }, { method: 'POST', path: '/forgot-password', handler: 'authentication.forgotPassword', - config: { auth: false }, + config: { + auth: false, + middlewares: ['admin::rateLimit'], + }, }, { method: 'POST', path: '/reset-password', handler: 'authentication.resetPassword', - config: { auth: false }, + config: { + auth: false, + middlewares: ['admin::rateLimit'], + }, }, ]; diff --git a/packages/core/utils/lib/errors.js b/packages/core/utils/lib/errors.js index 34cb3c919d..56a42dc9ee 100644 --- a/packages/core/utils/lib/errors.js +++ b/packages/core/utils/lib/errors.js @@ -55,14 +55,6 @@ class ForbiddenError extends ApplicationError { } } -class PayloadTooLargeError extends ApplicationError { - constructor(message, details) { - super(message, details); - this.name = 'PayloadTooLargeError'; - this.message = message || 'Entity too large'; - } -} - class UnauthorizedError extends ApplicationError { constructor(message, details) { super(message, details); @@ -71,6 +63,23 @@ class UnauthorizedError extends ApplicationError { } } +class RateLimitError extends ApplicationError { + constructor(message, details) { + super(message, details); + this.name = 'RateLimitError'; + this.message = message || 'Too many requests, please try again later.'; + this.status = 429; + } +} + +class PayloadTooLargeError extends ApplicationError { + constructor(message, details) { + super(message, details); + this.name = 'PayloadTooLargeError'; + this.message = message || 'Entity too large'; + } +} + class PolicyError extends ForbiddenError { constructor(message, details) { super(message, details); @@ -88,7 +97,8 @@ module.exports = { PaginationError, NotFoundError, ForbiddenError, - PayloadTooLargeError, UnauthorizedError, + RateLimitError, + PayloadTooLargeError, PolicyError, }; From aa97ec895baf922fc4dbf5852dc6f09a6167f395 Mon Sep 17 00:00:00 2001 From: derrickmehaffy Date: Wed, 5 Oct 2022 11:07:23 -0700 Subject: [PATCH 02/27] pr feedback Signed-off-by: Derrick Mehaffy --- packages/core/admin/server/routes/authentication.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/admin/server/routes/authentication.js b/packages/core/admin/server/routes/authentication.js index 80fa2beb37..8974acd790 100644 --- a/packages/core/admin/server/routes/authentication.js +++ b/packages/core/admin/server/routes/authentication.js @@ -23,16 +23,16 @@ module.exports = [ method: 'POST', path: '/register-admin', handler: 'authentication.registerAdmin', - config: { - auth: false, - middlewares: ['admin::rateLimit'], - }, + config: { auth: false }, }, { method: 'GET', path: '/registration-info', handler: 'authentication.registrationInfo', - config: { auth: false }, + config: { + auth: false, + middlewares: ['admin::rateLimit'], + }, }, { method: 'POST', From 7576d8a2576d6628f34b07650b1c58448cd64399 Mon Sep 17 00:00:00 2001 From: derrickmehaffy Date: Wed, 5 Oct 2022 11:26:17 -0700 Subject: [PATCH 03/27] Signed-off-by: Derrick Mehaffy --- packages/core/strapi/lib/services/errors.js | 6 +++++- packages/core/utils/lib/errors.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/core/strapi/lib/services/errors.js b/packages/core/strapi/lib/services/errors.js index 0efba67a5d..b930f1307d 100644 --- a/packages/core/strapi/lib/services/errors.js +++ b/packages/core/strapi/lib/services/errors.js @@ -1,7 +1,7 @@ 'use strict'; const createError = require('http-errors'); -const { NotFoundError, UnauthorizedError, ForbiddenError, PayloadTooLargeError } = +const { NotFoundError, UnauthorizedError, ForbiddenError, PayloadTooLargeError, PolicyError } = require('@strapi/utils').errors; const mapErrorsAndStatus = [ @@ -21,6 +21,10 @@ const mapErrorsAndStatus = [ classError: PayloadTooLargeError, status: 413, }, + { + classError: PolicyError, + status: 429, + }, ]; const formatApplicationError = (error) => { diff --git a/packages/core/utils/lib/errors.js b/packages/core/utils/lib/errors.js index 56a42dc9ee..3ccf1975e9 100644 --- a/packages/core/utils/lib/errors.js +++ b/packages/core/utils/lib/errors.js @@ -68,7 +68,7 @@ class RateLimitError extends ApplicationError { super(message, details); this.name = 'RateLimitError'; this.message = message || 'Too many requests, please try again later.'; - this.status = 429; + this.details = details || {}; } } From cf4b74c4f4e32971bc8cc2a66b31da6dc9c27720 Mon Sep 17 00:00:00 2001 From: derrickmehaffy Date: Fri, 7 Oct 2022 07:50:26 -0700 Subject: [PATCH 04/27] pr feedback Signed-off-by: Derrick Mehaffy --- packages/core/admin/server/middlewares/rateLimit.js | 6 ++++-- packages/core/strapi/lib/services/errors.js | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/core/admin/server/middlewares/rateLimit.js b/packages/core/admin/server/middlewares/rateLimit.js index e7632e7fac..40cc5e0428 100644 --- a/packages/core/admin/server/middlewares/rateLimit.js +++ b/packages/core/admin/server/middlewares/rateLimit.js @@ -9,10 +9,12 @@ module.exports = async (ctx, next) => { const ratelimit = require('koa2-ratelimit').RateLimit; + const userEmail = ctx.request.body.email || 'unknownEmail'; + return ratelimit.middleware({ - interval: { min: 15 }, + interval: { min: 5 }, max: 5, - prefixKey: `${ctx.request.path}:${ctx.request.ip}`, + prefixKey: `${userEmail}${ctx.request.path}:${ctx.request.ip}`, handler() { throw new RateLimitError(); }, diff --git a/packages/core/strapi/lib/services/errors.js b/packages/core/strapi/lib/services/errors.js index b930f1307d..536529b781 100644 --- a/packages/core/strapi/lib/services/errors.js +++ b/packages/core/strapi/lib/services/errors.js @@ -1,7 +1,7 @@ 'use strict'; const createError = require('http-errors'); -const { NotFoundError, UnauthorizedError, ForbiddenError, PayloadTooLargeError, PolicyError } = +const { NotFoundError, UnauthorizedError, ForbiddenError, PayloadTooLargeError, RateLimitError } = require('@strapi/utils').errors; const mapErrorsAndStatus = [ @@ -22,7 +22,7 @@ const mapErrorsAndStatus = [ status: 413, }, { - classError: PolicyError, + classError: RateLimitError, status: 429, }, ]; From 88cf040e5a7f155eef5b1aac494665baea1c2fbb Mon Sep 17 00:00:00 2001 From: derrickmehaffy Date: Fri, 7 Oct 2022 07:50:49 -0700 Subject: [PATCH 05/27] forgot colon Signed-off-by: Derrick Mehaffy --- packages/core/admin/server/middlewares/rateLimit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/admin/server/middlewares/rateLimit.js b/packages/core/admin/server/middlewares/rateLimit.js index 40cc5e0428..01e157c0a7 100644 --- a/packages/core/admin/server/middlewares/rateLimit.js +++ b/packages/core/admin/server/middlewares/rateLimit.js @@ -14,7 +14,7 @@ module.exports = return ratelimit.middleware({ interval: { min: 5 }, max: 5, - prefixKey: `${userEmail}${ctx.request.path}:${ctx.request.ip}`, + prefixKey: `${userEmail}:${ctx.request.path}:${ctx.request.ip}`, handler() { throw new RateLimitError(); }, From c3716a0671d33c370d8a43787b527f47be7c0d70 Mon Sep 17 00:00:00 2001 From: derrickmehaffy Date: Fri, 28 Oct 2022 11:22:08 -0700 Subject: [PATCH 06/27] Update koa2-ratelimit to match https://github.com/strapi/strapi/pull/14730 Signed-off-by: Derrick Mehaffy --- packages/core/admin/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/admin/package.json b/packages/core/admin/package.json index 843bf87891..b9d15cfe76 100644 --- a/packages/core/admin/package.json +++ b/packages/core/admin/package.json @@ -88,7 +88,7 @@ "jsonwebtoken": "8.5.1", "koa-compose": "4.1.0", "koa-passport": "5.0.0", - "koa2-ratelimit": "^1.1.1", + "koa2-ratelimit": "^1.1.2", "koa-static": "5.0.0", "lodash": "4.17.21", "markdown-it": "^12.3.2", From 5744d62feff3c3c7f493a4148829b21bb575fc0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre=20No=C3=ABl?= Date: Wed, 2 Nov 2022 18:49:05 +0100 Subject: [PATCH 07/27] rate limit only on login --- .../admin/ee/server/routes/features-routes.js | 10 ++------ .../admin/server/middlewares/rateLimit.js | 3 ++- .../admin/server/routes/authentication.js | 25 ++++--------------- 3 files changed, 9 insertions(+), 29 deletions(-) diff --git a/packages/core/admin/ee/server/routes/features-routes.js b/packages/core/admin/ee/server/routes/features-routes.js index 57dd4d4116..b9c5f72045 100644 --- a/packages/core/admin/ee/server/routes/features-routes.js +++ b/packages/core/admin/ee/server/routes/features-routes.js @@ -12,19 +12,13 @@ module.exports = { method: 'GET', path: '/connect/:provider', handler: 'authentication.providerLogin', - config: { - auth: false, - middlewares: ['admin::rateLimit'], - }, + config: { auth: false }, }, { method: 'POST', path: '/connect/:provider', handler: 'authentication.providerLogin', - config: { - auth: false, - middlewares: ['admin::rateLimit'], - }, + config: { auth: false }, }, { method: 'GET', diff --git a/packages/core/admin/server/middlewares/rateLimit.js b/packages/core/admin/server/middlewares/rateLimit.js index 01e157c0a7..24c919c3a8 100644 --- a/packages/core/admin/server/middlewares/rateLimit.js +++ b/packages/core/admin/server/middlewares/rateLimit.js @@ -1,6 +1,7 @@ 'use strict'; const utils = require('@strapi/utils'); +const { toLower } = require('lodash/fp'); const { RateLimitError } = utils.errors; @@ -9,7 +10,7 @@ module.exports = async (ctx, next) => { const ratelimit = require('koa2-ratelimit').RateLimit; - const userEmail = ctx.request.body.email || 'unknownEmail'; + const userEmail = toLower(ctx.request.body.email) || 'unknownEmail'; return ratelimit.middleware({ interval: { min: 5 }, diff --git a/packages/core/admin/server/routes/authentication.js b/packages/core/admin/server/routes/authentication.js index 8974acd790..35c4dc02f3 100644 --- a/packages/core/admin/server/routes/authentication.js +++ b/packages/core/admin/server/routes/authentication.js @@ -14,10 +14,7 @@ module.exports = [ method: 'POST', path: '/renew-token', handler: 'authentication.renewToken', - config: { - auth: false, - middlewares: ['admin::rateLimit'], - }, + config: { auth: false }, }, { method: 'POST', @@ -29,36 +26,24 @@ module.exports = [ method: 'GET', path: '/registration-info', handler: 'authentication.registrationInfo', - config: { - auth: false, - middlewares: ['admin::rateLimit'], - }, + config: { auth: false }, }, { method: 'POST', path: '/register', handler: 'authentication.register', - config: { - auth: false, - middlewares: ['admin::rateLimit'], - }, + config: { auth: false }, }, { method: 'POST', path: '/forgot-password', handler: 'authentication.forgotPassword', - config: { - auth: false, - middlewares: ['admin::rateLimit'], - }, + config: { auth: false }, }, { method: 'POST', path: '/reset-password', handler: 'authentication.resetPassword', - config: { - auth: false, - middlewares: ['admin::rateLimit'], - }, + config: { auth: false }, }, ]; From b75e8a1d103eb61f97b9212be0da15e8fec1a678 Mon Sep 17 00:00:00 2001 From: derrickmehaffy Date: Fri, 16 Dec 2022 10:42:28 -0700 Subject: [PATCH 08/27] implement config option to disable ratelimit --- .../admin/server/middlewares/rateLimit.js | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/packages/core/admin/server/middlewares/rateLimit.js b/packages/core/admin/server/middlewares/rateLimit.js index 24c919c3a8..d4cb61c494 100644 --- a/packages/core/admin/server/middlewares/rateLimit.js +++ b/packages/core/admin/server/middlewares/rateLimit.js @@ -8,18 +8,30 @@ const { RateLimitError } = utils.errors; module.exports = (config, { strapi }) => async (ctx, next) => { - const ratelimit = require('koa2-ratelimit').RateLimit; + let ratelimitConfig = strapi.config.get('admin.ratelimit'); - const userEmail = toLower(ctx.request.body.email) || 'unknownEmail'; + if (!ratelimitConfig || !ratelimitConfig.enabled) { + ratelimitConfig = { + enabled: true, + }; + } - return ratelimit.middleware({ - interval: { min: 5 }, - max: 5, - prefixKey: `${userEmail}:${ctx.request.path}:${ctx.request.ip}`, - handler() { - throw new RateLimitError(); - }, - ...strapi.config.get('admin.ratelimit'), - ...config, - })(ctx, next); + if (ratelimitConfig.enabled === true) { + const ratelimit = require('koa2-ratelimit').RateLimit; + + const userEmail = toLower(ctx.request.body.email) || 'unknownEmail'; + + return ratelimit.middleware({ + interval: { min: 5 }, + max: 5, + prefixKey: `${userEmail}:${ctx.request.path}:${ctx.request.ip}`, + handler() { + throw new RateLimitError(); + }, + ...ratelimitConfig, + ...config, + })(ctx, next); + } + + return next(); }; From 0719084324aefe88be3d6a99fa5ff87d5647c171 Mon Sep 17 00:00:00 2001 From: DMehaffy Date: Mon, 19 Dec 2022 11:40:37 -0700 Subject: [PATCH 09/27] Update packages/core/admin/server/middlewares/rateLimit.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Pierre Noël --- packages/core/admin/server/middlewares/rateLimit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/admin/server/middlewares/rateLimit.js b/packages/core/admin/server/middlewares/rateLimit.js index d4cb61c494..f738219969 100644 --- a/packages/core/admin/server/middlewares/rateLimit.js +++ b/packages/core/admin/server/middlewares/rateLimit.js @@ -10,7 +10,7 @@ module.exports = async (ctx, next) => { let ratelimitConfig = strapi.config.get('admin.ratelimit'); - if (!ratelimitConfig || !ratelimitConfig.enabled) { + if (!ratelimitConfig || !has('enabled', ratelimitConfig)) { ratelimitConfig = { enabled: true, }; From 29864b444f3d3f9fcabcfc5e30f8235cd062afda Mon Sep 17 00:00:00 2001 From: DMehaffy Date: Tue, 20 Dec 2022 08:03:33 -0700 Subject: [PATCH 10/27] Update packages/core/admin/server/middlewares/rateLimit.js Co-authored-by: Ben Irvin --- packages/core/admin/server/middlewares/rateLimit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/admin/server/middlewares/rateLimit.js b/packages/core/admin/server/middlewares/rateLimit.js index f738219969..45f76856d9 100644 --- a/packages/core/admin/server/middlewares/rateLimit.js +++ b/packages/core/admin/server/middlewares/rateLimit.js @@ -1,7 +1,7 @@ 'use strict'; const utils = require('@strapi/utils'); -const { toLower } = require('lodash/fp'); +const { has, toLower } = require('lodash/fp'); const { RateLimitError } = utils.errors; From b29f5c4f5caaf5fad42e672ceee4ccbbca8c412c Mon Sep 17 00:00:00 2001 From: mikhail Date: Wed, 21 Dec 2022 17:29:53 +0700 Subject: [PATCH 11/27] fix transalation documentation for setting page --- packages/plugins/documentation/admin/src/translations/dk.json | 2 +- packages/plugins/documentation/admin/src/translations/en.json | 2 +- packages/plugins/documentation/admin/src/translations/es.json | 2 +- packages/plugins/documentation/admin/src/translations/ko.json | 2 +- packages/plugins/documentation/admin/src/translations/pl.json | 2 +- packages/plugins/documentation/admin/src/translations/sv.json | 2 +- packages/plugins/documentation/admin/src/translations/tr.json | 2 +- packages/plugins/documentation/admin/src/translations/zh.json | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/plugins/documentation/admin/src/translations/dk.json b/packages/plugins/documentation/admin/src/translations/dk.json index 294e23b612..16280f0131 100755 --- a/packages/plugins/documentation/admin/src/translations/dk.json +++ b/packages/plugins/documentation/admin/src/translations/dk.json @@ -29,7 +29,7 @@ "pages.PluginPage.table.icon.regenerate": "Regenerér {target}", "pages.PluginPage.table.icon.show": "Åben {target}", "pages.PluginPage.table.version": "Version", - "pages.SettingsPage.Button.description": "Konfigurér dokumentations pluginnet", + "pages.SettingsPage.header.description": "Konfigurér dokumentations pluginnet", "pages.SettingsPage.header.save": "Gem", "pages.SettingsPage.toggle.hint": "Gør dokumentationens endpoint privat", "pages.SettingsPage.toggle.label": "Begrænset adgang", diff --git a/packages/plugins/documentation/admin/src/translations/en.json b/packages/plugins/documentation/admin/src/translations/en.json index 89330197da..cad45850d0 100755 --- a/packages/plugins/documentation/admin/src/translations/en.json +++ b/packages/plugins/documentation/admin/src/translations/en.json @@ -29,7 +29,7 @@ "pages.PluginPage.table.icon.regenerate": "Regenerate {target}", "pages.PluginPage.table.icon.show": "Open {target}", "pages.PluginPage.table.version": "Version", - "pages.SettingsPage.Button.description": "Configure the documentation plugin", + "pages.SettingsPage.header.description": "Configure the documentation plugin", "pages.SettingsPage.header.save": "Save", "pages.SettingsPage.toggle.hint": "Make the documentation endpoint private", "pages.SettingsPage.toggle.label": "Restricted Access", diff --git a/packages/plugins/documentation/admin/src/translations/es.json b/packages/plugins/documentation/admin/src/translations/es.json index 87b9d6ef68..ac911c5e4e 100755 --- a/packages/plugins/documentation/admin/src/translations/es.json +++ b/packages/plugins/documentation/admin/src/translations/es.json @@ -29,7 +29,7 @@ "pages.PluginPage.table.icon.regenerate": "Regenerar {target}", "pages.PluginPage.table.icon.show": "Abrir {target}", "pages.PluginPage.table.version": "Versión", - "pages.SettingsPage.Button.description": "Configura el plugin de documentación", + "pages.SettingsPage.header.description": "Configura el plugin de documentación", "pages.SettingsPage.header.save": "Guardar", "pages.SettingsPage.toggle.hint": "Hacer que la documentación sea privada", "pages.SettingsPage.toggle.label": "Acceso restringido", diff --git a/packages/plugins/documentation/admin/src/translations/ko.json b/packages/plugins/documentation/admin/src/translations/ko.json index b84cb40f2d..455d5fbd65 100755 --- a/packages/plugins/documentation/admin/src/translations/ko.json +++ b/packages/plugins/documentation/admin/src/translations/ko.json @@ -29,7 +29,7 @@ "pages.PluginPage.table.icon.regenerate": "{target} 재생성", "pages.PluginPage.table.icon.show": "{target} 열기", "pages.PluginPage.table.version": "버전", - "pages.SettingsPage.Button.description": "도큐멘테이션 플러그인 설정", + "pages.SettingsPage.header.description": "도큐멘테이션 플러그인 설정", "pages.SettingsPage.header.save": "저장", "pages.SettingsPage.toggle.hint": "도큐멘테이션 엔드포인트를 비공개로 설정합니다.", "pages.SettingsPage.toggle.label": "액세스 제한", diff --git a/packages/plugins/documentation/admin/src/translations/pl.json b/packages/plugins/documentation/admin/src/translations/pl.json index a546792fa5..5c7f166921 100755 --- a/packages/plugins/documentation/admin/src/translations/pl.json +++ b/packages/plugins/documentation/admin/src/translations/pl.json @@ -29,7 +29,7 @@ "pages.PluginPage.table.icon.regenerate": "Wygeneruj ponownie {target}", "pages.PluginPage.table.icon.show": "Otwórz {target}", "pages.PluginPage.table.version": "Wersja", - "pages.SettingsPage.Button.description": "Skonfiguruj plugin dokumentacji", + "pages.SettingsPage.header.description": "Skonfiguruj plugin dokumentacji", "pages.SettingsPage.header.save": "Zapisz", "pages.SettingsPage.toggle.hint": "Ustaw endpoint dokumentacji na prywatny", "pages.SettingsPage.toggle.label": "Dostęp ograniczony", diff --git a/packages/plugins/documentation/admin/src/translations/sv.json b/packages/plugins/documentation/admin/src/translations/sv.json index 3c282fc021..23a54ebb75 100644 --- a/packages/plugins/documentation/admin/src/translations/sv.json +++ b/packages/plugins/documentation/admin/src/translations/sv.json @@ -29,7 +29,7 @@ "pages.PluginPage.table.icon.regenerate": "Återskapa {target}", "pages.PluginPage.table.icon.show": "Öppna {target}", "pages.PluginPage.table.version": "Version", - "pages.SettingsPage.Button.description": "Konfigurera dokumentationspluginet", + "pages.SettingsPage.header.description": "Konfigurera dokumentationspluginet", "pages.SettingsPage.header.save": "Spara", "pages.SettingsPage.toggle.hint": "Gör dokumentationensrutten privat", "pages.SettingsPage.toggle.label": "Begränsad åtkomst", diff --git a/packages/plugins/documentation/admin/src/translations/tr.json b/packages/plugins/documentation/admin/src/translations/tr.json index 913e69e6cc..0911625251 100755 --- a/packages/plugins/documentation/admin/src/translations/tr.json +++ b/packages/plugins/documentation/admin/src/translations/tr.json @@ -29,7 +29,7 @@ "pages.PluginPage.table.icon.regenerate": "Yeniden üret: {target}", "pages.PluginPage.table.icon.show": "Aç: {target}", "pages.PluginPage.table.version": "Versiyon", - "pages.SettingsPage.Button.description": "Dokümantasyon eklentisini ayarla", + "pages.SettingsPage.header.description": "Dokümantasyon eklentisini ayarla", "pages.SettingsPage.header.save": "Kaydet", "pages.SettingsPage.toggle.hint": "Dokümantasyon uç noktasını gizli yap", "pages.SettingsPage.toggle.label": "Kısıtlı Erişim", diff --git a/packages/plugins/documentation/admin/src/translations/zh.json b/packages/plugins/documentation/admin/src/translations/zh.json index 16f546c658..1476433abd 100755 --- a/packages/plugins/documentation/admin/src/translations/zh.json +++ b/packages/plugins/documentation/admin/src/translations/zh.json @@ -29,7 +29,7 @@ "pages.PluginPage.table.icon.regenerate": "重新產生 {target}", "pages.PluginPage.table.icon.show": "開啟 {target}", "pages.PluginPage.table.version": "版本", - "pages.SettingsPage.Button.description": "設定說明文件外掛程式", + "pages.SettingsPage.header.description": "設定說明文件外掛程式", "pages.SettingsPage.header.save": "儲存", "pages.SettingsPage.toggle.hint": "將說明文件端點設為私人", "pages.SettingsPage.toggle.label": "受限存取", From c4d407d2cb1f01415df2e9fcc7e0c3b925b0382f Mon Sep 17 00:00:00 2001 From: Ben Irvin Date: Thu, 22 Dec 2022 16:05:35 +0100 Subject: [PATCH 12/27] fix telemetry --- .../strapi/lib/commands/transfer/export.js | 43 ++++++++++++++++--- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/packages/core/strapi/lib/commands/transfer/export.js b/packages/core/strapi/lib/commands/transfer/export.js index 130a9466d5..7772fff7b0 100644 --- a/packages/core/strapi/lib/commands/transfer/export.js +++ b/packages/core/strapi/lib/commands/transfer/export.js @@ -74,6 +74,7 @@ module.exports = async (opts) => { }, }); + let transferExitCode; try { logger.log(`Starting export...`); @@ -88,16 +89,18 @@ module.exports = async (opts) => { }; }; - progress.on('transfer::start', (payload) => { - strapi.telemetry.send('didDEITSProcessStart', telemetryPayload(payload)); + progress.on('transfer::start', async (payload) => { + await strapi.telemetry.send('didDEITSProcessStart', telemetryPayload(payload)); }); - progress.on('transfer::finish', (payload) => { - strapi.telemetry.send('didDEITSProcessFinish', telemetryPayload(payload)); + progress.on('transfer::finish', async (payload) => { + await strapi.telemetry.send('didDEITSProcessFinish', telemetryPayload(payload)); + transferExitCode = 0; }); - progress.on('transfer::error', (payload) => { - strapi.telemetry.send('didDEITSProcessFail', telemetryPayload(payload)); + progress.on('transfer::error', async (payload) => { + await strapi.telemetry.send('didDEITSProcessFail', telemetryPayload(payload)); + transferExitCode = 1; }); const results = await engine.transfer(); @@ -113,11 +116,37 @@ module.exports = async (opts) => { logger.log(`${chalk.bold('Export process has been completed successfully!')}`); logger.log(`Export archive is in ${chalk.green(outFile)}`); - process.exit(0); } catch (e) { logger.error('Export process failed unexpectedly:', e.toString()); process.exit(1); } + + /* + * We need to wait for the telemetry to finish before exiting the process. + * The order of execution for this function is: + * - create providers and engine + * - create progress callbacks + * - await the engine transfer + * - having async calls inside, it allows the transfer::start to process + * - the code block including the table printing executes + * - *** any async code (for example, the fs.pathExists) after engine.transfer will execute next tick, therefore: + * - the progress callbacks execute + * + * Because of that, we can't exit the process in the progress callbacks and instead have to wait for them to tell us it's safe to exit + */ + const waitForExitCode = async (maxWait) => { + const startTime = Date.now(); + while (Date.now() - startTime < maxWait) { + if (transferExitCode !== undefined) { + process.exit(transferExitCode); + } + await new Promise((resolve) => { + setTimeout(resolve, 50); + }); + } + process.exit(0); + }; + waitForExitCode(5000); }; /** From 7ee5030bd60f005ccb0778e2ace143e6055af0e4 Mon Sep 17 00:00:00 2001 From: Ben Irvin Date: Thu, 22 Dec 2022 16:13:45 +0100 Subject: [PATCH 13/27] fix import and cleanup --- .../strapi/lib/commands/transfer/export.js | 42 +++++------ .../strapi/lib/commands/transfer/import.js | 69 +++++++++++++------ 2 files changed, 70 insertions(+), 41 deletions(-) diff --git a/packages/core/strapi/lib/commands/transfer/export.js b/packages/core/strapi/lib/commands/transfer/export.js index 7772fff7b0..2bf323475c 100644 --- a/packages/core/strapi/lib/commands/transfer/export.js +++ b/packages/core/strapi/lib/commands/transfer/export.js @@ -75,34 +75,34 @@ module.exports = async (opts) => { }); let transferExitCode; - try { - logger.log(`Starting export...`); + logger.log(`Starting export...`); - const progress = engine.progress.stream; + const progress = engine.progress.stream; - const telemetryPayload = (/* payload */) => { - return { - eventProperties: { - source: engine.sourceProvider.name, - destination: engine.destinationProvider.name, - }, - }; + const telemetryPayload = (/* payload */) => { + return { + eventProperties: { + source: engine.sourceProvider.name, + destination: engine.destinationProvider.name, + }, }; + }; - progress.on('transfer::start', async (payload) => { - await strapi.telemetry.send('didDEITSProcessStart', telemetryPayload(payload)); - }); + progress.on('transfer::start', async (payload) => { + await strapi.telemetry.send('didDEITSProcessStart', telemetryPayload(payload)); + }); - progress.on('transfer::finish', async (payload) => { - await strapi.telemetry.send('didDEITSProcessFinish', telemetryPayload(payload)); - transferExitCode = 0; - }); + progress.on('transfer::finish', async (payload) => { + await strapi.telemetry.send('didDEITSProcessFinish', telemetryPayload(payload)); + transferExitCode = 0; + }); - progress.on('transfer::error', async (payload) => { - await strapi.telemetry.send('didDEITSProcessFail', telemetryPayload(payload)); - transferExitCode = 1; - }); + progress.on('transfer::error', async (payload) => { + await strapi.telemetry.send('didDEITSProcessFail', telemetryPayload(payload)); + transferExitCode = 1; + }); + try { const results = await engine.transfer(); const outFile = results.destination.file.path; diff --git a/packages/core/strapi/lib/commands/transfer/import.js b/packages/core/strapi/lib/commands/transfer/import.js index df8506b3b8..21b635b223 100644 --- a/packages/core/strapi/lib/commands/transfer/import.js +++ b/packages/core/strapi/lib/commands/transfer/import.js @@ -79,42 +79,71 @@ module.exports = async (opts) => { }; const engine = createTransferEngine(source, destination, engineOptions); - try { - logger.info('Starting import...'); + let transferExitCode; + logger.info('Starting import...'); - const progress = engine.progress.stream; - const telemetryPayload = (/* payload */) => { - return { - eventProperties: { - source: engine.sourceProvider.name, - destination: engine.destinationProvider.name, - }, - }; + const progress = engine.progress.stream; + const telemetryPayload = (/* payload */) => { + return { + eventProperties: { + source: engine.sourceProvider.name, + destination: engine.destinationProvider.name, + }, }; + }; - progress.on('transfer::start', (payload) => { - strapiInstance.telemetry.send('didDEITSProcessStart', telemetryPayload(payload)); - }); + progress.on('transfer::start', async (payload) => { + await strapiInstance.telemetry.send('didDEITSProcessStart', telemetryPayload(payload)); + }); - progress.on('transfer::finish', (payload) => { - strapiInstance.telemetry.send('didDEITSProcessFinish', telemetryPayload(payload)); - }); + progress.on('transfer::finish', async (payload) => { + await strapiInstance.telemetry.send('didDEITSProcessFinish', telemetryPayload(payload)); + transferExitCode = 0; + }); - progress.on('transfer::error', (payload) => { - strapiInstance.telemetry.send('didDEITSProcessFail', telemetryPayload(payload)); - }); + progress.on('transfer::error', async (payload) => { + await strapiInstance.telemetry.send('didDEITSProcessFail', telemetryPayload(payload)); + transferExitCode = 1; + }); + try { const results = await engine.transfer(); const table = buildTransferTable(results.engine); logger.info(table.toString()); logger.info('Import process has been completed successfully!'); - process.exit(0); } catch (e) { logger.error('Import process failed unexpectedly:'); logger.error(e); process.exit(1); } + + /* + * We need to wait for the telemetry to finish before exiting the process. + * The order of execution for this function is: + * - create providers and engine + * - create progress callbacks + * - await the engine transfer + * - having async calls inside, it allows the transfer::start to process + * - the code block including the table printing executes + * - *** any async code (for example, the fs.pathExists) after engine.transfer will execute next tick, therefore: + * - the progress callbacks execute + * + * Because of that, we can't exit the process in the progress callbacks and instead have to wait for them to tell us it's safe to exit + */ + const waitForExitCode = async (maxWait) => { + const startTime = Date.now(); + while (Date.now() - startTime < maxWait) { + if (transferExitCode !== undefined) { + process.exit(transferExitCode); + } + await new Promise((resolve) => { + setTimeout(resolve, 50); + }); + } + process.exit(0); + }; + waitForExitCode(5000); }; /** From b59da2bca89658e2277006c0c3bb60dea84627ee Mon Sep 17 00:00:00 2001 From: Ben Irvin Date: Thu, 22 Dec 2022 16:50:31 +0100 Subject: [PATCH 14/27] clarify comment --- packages/core/strapi/lib/commands/transfer/export.js | 2 +- packages/core/strapi/lib/commands/transfer/import.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/strapi/lib/commands/transfer/export.js b/packages/core/strapi/lib/commands/transfer/export.js index 2bf323475c..912ed97711 100644 --- a/packages/core/strapi/lib/commands/transfer/export.js +++ b/packages/core/strapi/lib/commands/transfer/export.js @@ -123,7 +123,7 @@ module.exports = async (opts) => { /* * We need to wait for the telemetry to finish before exiting the process. - * The order of execution for this function is: + * The order of execution for the overall export function is: * - create providers and engine * - create progress callbacks * - await the engine transfer diff --git a/packages/core/strapi/lib/commands/transfer/import.js b/packages/core/strapi/lib/commands/transfer/import.js index 21b635b223..9c382aadb0 100644 --- a/packages/core/strapi/lib/commands/transfer/import.js +++ b/packages/core/strapi/lib/commands/transfer/import.js @@ -120,7 +120,7 @@ module.exports = async (opts) => { /* * We need to wait for the telemetry to finish before exiting the process. - * The order of execution for this function is: + * The order of execution for the overall import function is: * - create providers and engine * - create progress callbacks * - await the engine transfer From adb5073f00cc16f458e256bd5d9a7d83d17f6116 Mon Sep 17 00:00:00 2001 From: Ben Irvin Date: Thu, 22 Dec 2022 16:52:57 +0100 Subject: [PATCH 15/27] clarify comment --- packages/core/strapi/lib/commands/transfer/export.js | 3 ++- packages/core/strapi/lib/commands/transfer/import.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/core/strapi/lib/commands/transfer/export.js b/packages/core/strapi/lib/commands/transfer/export.js index 912ed97711..055710afd3 100644 --- a/packages/core/strapi/lib/commands/transfer/export.js +++ b/packages/core/strapi/lib/commands/transfer/export.js @@ -132,7 +132,8 @@ module.exports = async (opts) => { * - *** any async code (for example, the fs.pathExists) after engine.transfer will execute next tick, therefore: * - the progress callbacks execute * - * Because of that, we can't exit the process in the progress callbacks and instead have to wait for them to tell us it's safe to exit + * Because of that, we have to wait until the progress callbacks have executed, but can't allow them to exit by themselves. + * Instead we have to wait for them to tell us it's safe to exit */ const waitForExitCode = async (maxWait) => { const startTime = Date.now(); diff --git a/packages/core/strapi/lib/commands/transfer/import.js b/packages/core/strapi/lib/commands/transfer/import.js index 9c382aadb0..00f8498665 100644 --- a/packages/core/strapi/lib/commands/transfer/import.js +++ b/packages/core/strapi/lib/commands/transfer/import.js @@ -129,7 +129,8 @@ module.exports = async (opts) => { * - *** any async code (for example, the fs.pathExists) after engine.transfer will execute next tick, therefore: * - the progress callbacks execute * - * Because of that, we can't exit the process in the progress callbacks and instead have to wait for them to tell us it's safe to exit + * Because of that, we have to wait until the progress callbacks have executed, but can't allow them to exit by themselves. + * Instead we have to wait for them to tell us it's safe to exit */ const waitForExitCode = async (maxWait) => { const startTime = Date.now(); From d75ae711140366beb5da78379358c890dc444361 Mon Sep 17 00:00:00 2001 From: Ben Irvin Date: Thu, 22 Dec 2022 18:02:34 +0100 Subject: [PATCH 16/27] fix tests --- .../lib/commands/__tests__/export.test.js | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/core/strapi/lib/commands/__tests__/export.test.js b/packages/core/strapi/lib/commands/__tests__/export.test.js index bf30ebeeda..58da3110da 100644 --- a/packages/core/strapi/lib/commands/__tests__/export.test.js +++ b/packages/core/strapi/lib/commands/__tests__/export.test.js @@ -1,13 +1,20 @@ 'use strict'; +const { Readable } = require('stream'); const utils = require('../transfer/utils'); const mockDataTransfer = { createLocalFileDestinationProvider: jest.fn(), createLocalStrapiSourceProvider: jest.fn(), - createTransferEngine: jest.fn().mockReturnValue({ - transfer: jest.fn().mockReturnValue(Promise.resolve({})), - }), + createTransferEngine() { + return { + transfer: jest.fn().mockReturnValue(Promise.resolve({})), + progress: { + on: jest.fn(), + stream: Readable.from([1, 2, 3]), + }, + }; + }, }; jest.mock( @@ -85,12 +92,17 @@ describe('export', () => { expect(exit).toHaveBeenCalled(); }); - it('compresses the output file if specified', async () => { - const compress = true; - await exportCommand({ compress }); + it('uses compress option', async () => { + await exportCommand({ compress: false }); expect(mockDataTransfer.createLocalFileDestinationProvider).toHaveBeenCalledWith( expect.objectContaining({ - compression: { enabled: compress }, + compression: { enabled: false }, + }) + ); + await exportCommand({ compress: true }); + expect(mockDataTransfer.createLocalFileDestinationProvider).toHaveBeenCalledWith( + expect.objectContaining({ + compression: { enabled: true }, }) ); expect(exit).toHaveBeenCalled(); From e24fc80b79177482d00c780f3c880bd1a6b66812 Mon Sep 17 00:00:00 2001 From: Ben Irvin Date: Thu, 22 Dec 2022 18:16:55 +0100 Subject: [PATCH 17/27] fix stream --- packages/core/strapi/lib/commands/__tests__/export.test.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/core/strapi/lib/commands/__tests__/export.test.js b/packages/core/strapi/lib/commands/__tests__/export.test.js index 58da3110da..fefced0dc5 100644 --- a/packages/core/strapi/lib/commands/__tests__/export.test.js +++ b/packages/core/strapi/lib/commands/__tests__/export.test.js @@ -1,6 +1,5 @@ 'use strict'; -const { Readable } = require('stream'); const utils = require('../transfer/utils'); const mockDataTransfer = { @@ -11,7 +10,9 @@ const mockDataTransfer = { transfer: jest.fn().mockReturnValue(Promise.resolve({})), progress: { on: jest.fn(), - stream: Readable.from([1, 2, 3]), + stream: { + on: jest.fn(), + }, }, }; }, From ae7493f96affb2a78b5b794b42b476ea44b7cb95 Mon Sep 17 00:00:00 2001 From: Ivan Ha Date: Fri, 23 Dec 2022 16:43:48 +0800 Subject: [PATCH 18/27] fix: wrong translations key in profile -> experience --- packages/core/admin/admin/src/pages/ProfilePage/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/admin/admin/src/pages/ProfilePage/index.js b/packages/core/admin/admin/src/pages/ProfilePage/index.js index a0c6e8ad3d..c98014ba6e 100644 --- a/packages/core/admin/admin/src/pages/ProfilePage/index.js +++ b/packages/core/admin/admin/src/pages/ProfilePage/index.js @@ -458,7 +458,7 @@ const ProfilePage = () => { href="https://docs.strapi.io/developer-docs/latest/development/admin-customization.html#locales" > {formatMessage({ - id: 'Settings.profile.form.section.experience.documentation', + id: 'Settings.profile.form.section.experience.here', defaultMessage: 'here', })} From 863b0a021ad399b64d319414f50fe46b61662f65 Mon Sep 17 00:00:00 2001 From: Ivan Ha Date: Fri, 23 Dec 2022 16:50:27 +0800 Subject: [PATCH 19/27] fix: Settings.profile.form.section.experience.here in all translation files --- packages/core/admin/admin/src/translations/ca.json | 5 ++--- packages/core/admin/admin/src/translations/dk.json | 4 ++-- packages/core/admin/admin/src/translations/es.json | 4 ++-- packages/core/admin/admin/src/translations/fr.json | 4 ++-- packages/core/admin/admin/src/translations/hu.json | 4 ++-- packages/core/admin/admin/src/translations/ja.json | 4 ++-- packages/core/admin/admin/src/translations/nl.json | 4 ++-- packages/core/admin/admin/src/translations/zh-Hans.json | 2 +- packages/core/admin/admin/src/translations/zh.json | 4 ++-- 9 files changed, 17 insertions(+), 18 deletions(-) diff --git a/packages/core/admin/admin/src/translations/ca.json b/packages/core/admin/admin/src/translations/ca.json index 235f729295..254cbd06bb 100644 --- a/packages/core/admin/admin/src/translations/ca.json +++ b/packages/core/admin/admin/src/translations/ca.json @@ -125,11 +125,10 @@ "Settings.permissions.users.tabs.label": "Permisos de pestanyes", "Settings.profile.form.notify.data.loaded": "S'han carregat les dades del vostre perfil", "Settings.profile.form.section.experience.clear.select": "Esborrar l'idioma d'interfície seleccionat", - "Settings.profile.form.section.experience.documentation": "documentació", - "Settings.profile.form.section.experience.here": "aquí", + "Settings.profile.form.section.experience.here": "documentació", "Settings.profile.form.section.experience.interfaceLanguage": "Idioma d'interfície", "Settings.profile.form.section.experience.interfaceLanguage.hint": "Això només mostrarà la vostra pròpia interfície en l'idioma escollit.", - "Settings.profile.form.section.experience.interfaceLanguageHelp": "La selecció canviarà l'idioma de la interfície només per a vosaltres. Consulteu aquesta {documentation} perquè altres idiomes estiguin disponibles per al vostre ordinador.", + "Settings.profile.form.section.experience.interfaceLanguageHelp": "La selecció canviarà l'idioma de la interfície només per a vosaltres. Consulteu aquesta {here} perquè altres idiomes estiguin disponibles per al vostre ordinador.", "Settings.profile.form.section.experience.mode.hint": "Mostra la vostra interfície en el mode escollit.", "Settings.profile.form.section.experience.mode.label": "Mode d'interfície", "Settings.profile.form.section.experience.mode.option-label": "mode {nom}", diff --git a/packages/core/admin/admin/src/translations/dk.json b/packages/core/admin/admin/src/translations/dk.json index 0745a9199a..ceeb515517 100644 --- a/packages/core/admin/admin/src/translations/dk.json +++ b/packages/core/admin/admin/src/translations/dk.json @@ -123,10 +123,10 @@ "Settings.permissions.users.tabs.label": "Tabs Tilladelser", "Settings.profile.form.notify.data.loaded": "Dine profildata er blevet hentet", "Settings.profile.form.section.experience.clear.select": "Nulstil det valgte interface sprog", - "Settings.profile.form.section.experience.documentation": "dokumentation", + "Settings.profile.form.section.experience.here": "dokumentation", "Settings.profile.form.section.experience.interfaceLanguage": "Interface sprog", "Settings.profile.form.section.experience.interfaceLanguage.hint": "Dette vil kun vise dit eget interface i det valgte sprog.", - "Settings.profile.form.section.experience.interfaceLanguageHelp": "Valget vil kun ændre sproget for dig. Referér venligst til dette {documentation} for at gøre andre sprog tilgængelige for dit hold.", + "Settings.profile.form.section.experience.interfaceLanguageHelp": "Valget vil kun ændre sproget for dig. Referér venligst til dette {here} for at gøre andre sprog tilgængelige for dit hold.", "Settings.profile.form.section.experience.title": "Oplevelse", "Settings.profile.form.section.helmet.title": "Bruger profil", "Settings.profile.form.section.profile.page.title": "Profil side", diff --git a/packages/core/admin/admin/src/translations/es.json b/packages/core/admin/admin/src/translations/es.json index 5af4f1031b..9809356695 100644 --- a/packages/core/admin/admin/src/translations/es.json +++ b/packages/core/admin/admin/src/translations/es.json @@ -123,10 +123,10 @@ "Settings.permissions.users.tabs.label": "Permisos de pestañas", "Settings.profile.form.notify.data.loaded": "Se han cargado los datos de tu perfil", "Settings.profile.form.section.experience.clear.select": "Borrar el idioma de interfaz seleccionado", - "Settings.profile.form.section.experience.documentation": "documentación", + "Settings.profile.form.section.experience.here": "documentación", "Settings.profile.form.section.experience.interfaceLanguage": "Idioma de interfaz", "Settings.profile.form.section.experience.interfaceLanguage.hint": "Esto solo mostrará su propia interfaz en el idioma elegido.", - "Settings.profile.form.section.experience.interfaceLanguageHelp": "La selección cambiará el idioma de la interfaz solo para usted. Consulte esta {documentation} para que otros idiomas estén disponibles para su equipo.", + "Settings.profile.form.section.experience.interfaceLanguageHelp": "La selección cambiará el idioma de la interfaz solo para usted. Consulte esta {here} para que otros idiomas estén disponibles para su equipo.", "Settings.profile.form.section.experience.title": "Experiencia", "Settings.profile.form.section.helmet.title": "Perfil de usuario", "Settings.profile.form.section.profile.page.title": "Página de perfil", diff --git a/packages/core/admin/admin/src/translations/fr.json b/packages/core/admin/admin/src/translations/fr.json index 7e3b00a05b..9a9ff48c22 100644 --- a/packages/core/admin/admin/src/translations/fr.json +++ b/packages/core/admin/admin/src/translations/fr.json @@ -123,10 +123,10 @@ "Settings.permissions.users.tabs.label": "Onglet Autorisations", "Settings.profile.form.notify.data.loaded": "Les données de votre profil ont été chargées", "Settings.profile.form.section.experience.clear.select": "Vider la langue de l'interface sélectionnée", - "Settings.profile.form.section.experience.documentation": "documentation", + "Settings.profile.form.section.experience.here": "documentation", "Settings.profile.form.section.experience.interfaceLanguage": "Langue de l'interface", "Settings.profile.form.section.experience.interfaceLanguage.hint": "Cela affichera seulement votre propre interface dans la langue sélectionnée", - "Settings.profile.form.section.experience.interfaceLanguageHelp": "La sélection changera la langue de l'interface uniquement pour vous. Veuillez vous référer à cette {documentation} pour rendre d'autres langues disponibles pour votre équipe.", + "Settings.profile.form.section.experience.interfaceLanguageHelp": "La sélection changera la langue de l'interface uniquement pour vous. Veuillez vous référer à cette {here} pour rendre d'autres langues disponibles pour votre équipe.", "Settings.profile.form.section.experience.title": "Expérience", "Settings.profile.form.section.helmet.title": "Profil utilisateur", "Settings.profile.form.section.profile.page.title": "Page de profil", diff --git a/packages/core/admin/admin/src/translations/hu.json b/packages/core/admin/admin/src/translations/hu.json index f8fff028b7..b02e1616b5 100644 --- a/packages/core/admin/admin/src/translations/hu.json +++ b/packages/core/admin/admin/src/translations/hu.json @@ -123,10 +123,10 @@ "Settings.permissions.users.tabs.label": "Hozzáférések Tab", "Settings.profile.form.notify.data.loaded": "Profiladatok betöltve", "Settings.profile.form.section.experience.clear.select": "A kiválasztott felület nyelvének törlése", - "Settings.profile.form.section.experience.documentation": "dokumentáció", + "Settings.profile.form.section.experience.here": "dokumentáció", "Settings.profile.form.section.experience.interfaceLanguage": "A felület nyelve", "Settings.profile.form.section.experience.interfaceLanguage.hint": "Ez csak a saját felületét jeleníti meg a kiválasztott nyelven.", - "Settings.profile.form.section.experience.interfaceLanguageHelp": "A kiválasztás csak az Ön számára módosítja a felület nyelvét. Kérjük, olvassa el ezt a {document}, hogy más nyelveket a csapata számára is elérhetővé tehesse.", + "Settings.profile.form.section.experience.interfaceLanguageHelp": "A kiválasztás csak az Ön számára módosítja a felület nyelvét. Kérjük, olvassa el ezt a {here}, hogy más nyelveket a csapata számára is elérhetővé tehesse.", "Settings.profile.form.section.experience.title": "Tapasztalat", "Settings.profile.form.section.helmet.title": "Felhasználói profil", "Settings.profile.form.section.profile.page.title": "Profil oldal", diff --git a/packages/core/admin/admin/src/translations/ja.json b/packages/core/admin/admin/src/translations/ja.json index a86a96de21..4617aa3902 100644 --- a/packages/core/admin/admin/src/translations/ja.json +++ b/packages/core/admin/admin/src/translations/ja.json @@ -123,10 +123,10 @@ "Settings.permissions.users.tabs.label": "Tabs Permissions", "Settings.profile.form.notify.data.loaded": "Your profile data has been loaded", "Settings.profile.form.section.experience.clear.select": "Clear the interface language selected", - "Settings.profile.form.section.experience.documentation": "documentation", + "Settings.profile.form.section.experience.here": "documentation", "Settings.profile.form.section.experience.interfaceLanguage": "Interface language", "Settings.profile.form.section.experience.interfaceLanguage.hint": "This will only display your own interface in the chosen language.", - "Settings.profile.form.section.experience.interfaceLanguageHelp": "Selection will change the interface language only for you. Please refer to this {documentation} to make other languages available for your team.", + "Settings.profile.form.section.experience.interfaceLanguageHelp": "Selection will change the interface language only for you. Please refer to this {here} to make other languages available for your team.", "Settings.profile.form.section.experience.title": "Experience", "Settings.profile.form.section.helmet.title": "ユーザープロフィール", "Settings.profile.form.section.profile.page.title": "プロフィールページ", diff --git a/packages/core/admin/admin/src/translations/nl.json b/packages/core/admin/admin/src/translations/nl.json index b9bed4ec89..dac74ff76e 100644 --- a/packages/core/admin/admin/src/translations/nl.json +++ b/packages/core/admin/admin/src/translations/nl.json @@ -177,10 +177,10 @@ "Settings.permissions.users.strapi-author": "Auteur", "Settings.profile.form.notify.data.loaded": "Je profielgegevens zijn geladen", "Settings.profile.form.section.experience.clear.select": "Wis de geselecteerde interfacetaal", - "Settings.profile.form.section.experience.documentation": "documentatie", + "Settings.profile.form.section.experience.here": "documentatie", "Settings.profile.form.section.experience.interfaceLanguage": "Interfacetaal", "Settings.profile.form.section.experience.interfaceLanguage.hint": "Hierdoor wordt alleen je eigen interface in de gekozen taal weergegeven.", - "Settings.profile.form.section.experience.interfaceLanguageHelp": "Selectie zal de interfacetaal alleen voor jou veranderen. Raadpleeg deze {documentation} om andere talen beschikbaar te maken voor uw team.", + "Settings.profile.form.section.experience.interfaceLanguageHelp": "Selectie zal de interfacetaal alleen voor jou veranderen. Raadpleeg deze {here} om andere talen beschikbaar te maken voor uw team.", "Settings.profile.form.section.experience.mode.label": "Interface modus", "Settings.profile.form.section.experience.mode.hint": "Toont uw interface in de gekozen modus.", "Settings.profile.form.section.experience.mode.option-label": "{name} modus", diff --git a/packages/core/admin/admin/src/translations/zh-Hans.json b/packages/core/admin/admin/src/translations/zh-Hans.json index a818662771..dac2cdae7a 100644 --- a/packages/core/admin/admin/src/translations/zh-Hans.json +++ b/packages/core/admin/admin/src/translations/zh-Hans.json @@ -132,7 +132,7 @@ "Settings.permissions.users.strapi-author": "作者", "Settings.profile.form.notify.data.loaded": "你的个人数据已经加载完成", "Settings.profile.form.section.experience.clear.select": "清除已选择的界面语言", - "Settings.profile.form.section.experience.documentation": "文档", + "Settings.profile.form.section.experience.here": "文档", "Settings.profile.form.section.experience.interfaceLanguage": "界面语言", "Settings.profile.form.section.experience.interfaceLanguage.hint": "将会用所选择的语言显示你的界面", "Settings.profile.form.section.experience.interfaceLanguageHelp": "当前的语言选择只会更改你当前帐号界面语言。 请参考此 {here} 为你的团队提供其他语言。", diff --git a/packages/core/admin/admin/src/translations/zh.json b/packages/core/admin/admin/src/translations/zh.json index aac52e453a..c8c28f7084 100644 --- a/packages/core/admin/admin/src/translations/zh.json +++ b/packages/core/admin/admin/src/translations/zh.json @@ -177,10 +177,10 @@ "Settings.permissions.users.strapi-author": "作者", "Settings.profile.form.notify.data.loaded": "您的個人檔案資料已經載入", "Settings.profile.form.section.experience.clear.select": "清除已選的介面語言", - "Settings.profile.form.section.experience.here": "here", + "Settings.profile.form.section.experience.here": "此文檔", "Settings.profile.form.section.experience.interfaceLanguage": "介面語言", "Settings.profile.form.section.experience.interfaceLanguage.hint": "將會用所選擇的語言顯示您的介面", - "Settings.profile.form.section.experience.interfaceLanguageHelp": "只有您的介面會變為所選擇的語言。如果要為您的團隊提供其他語言,請參考此 {documentation}。", + "Settings.profile.form.section.experience.interfaceLanguageHelp": "只有您的介面會變為所選擇的語言。如果要為您的團隊提供其他語言,請參考{here}。", "Settings.profile.form.section.experience.mode.label": "介面模式", "Settings.profile.form.section.experience.mode.hint": "在選擇的模式中顯示您的介面。", "Settings.profile.form.section.experience.mode.option-label": "{name} 模式", From 33bd405a2baccc8cd1c6ace1dcc1f13d08fe1472 Mon Sep 17 00:00:00 2001 From: derrickmehaffy Date: Tue, 27 Dec 2022 08:20:38 -0700 Subject: [PATCH 20/27] refactor config loading --- .../core/admin/server/middlewares/rateLimit.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/core/admin/server/middlewares/rateLimit.js b/packages/core/admin/server/middlewares/rateLimit.js index 45f76856d9..7df3a429ec 100644 --- a/packages/core/admin/server/middlewares/rateLimit.js +++ b/packages/core/admin/server/middlewares/rateLimit.js @@ -10,18 +10,22 @@ module.exports = async (ctx, next) => { let ratelimitConfig = strapi.config.get('admin.ratelimit'); - if (!ratelimitConfig || !has('enabled', ratelimitConfig)) { + if (!ratelimitConfig) { ratelimitConfig = { enabled: true, }; } + if (!has('enabled', ratelimitConfig)) { + ratelimitConfig.enabled = true; + } + if (ratelimitConfig.enabled === true) { const ratelimit = require('koa2-ratelimit').RateLimit; const userEmail = toLower(ctx.request.body.email) || 'unknownEmail'; - return ratelimit.middleware({ + const loadConfig = { interval: { min: 5 }, max: 5, prefixKey: `${userEmail}:${ctx.request.path}:${ctx.request.ip}`, @@ -30,7 +34,11 @@ module.exports = }, ...ratelimitConfig, ...config, - })(ctx, next); + }; + + console.log(loadConfig); + + return ratelimit.middleware(loadConfig)(ctx, next); } return next(); From ae7c60980c09a5717603d19c07ee5d9a08cf8feb Mon Sep 17 00:00:00 2001 From: derrickmehaffy Date: Tue, 27 Dec 2022 09:19:57 -0700 Subject: [PATCH 21/27] change ratelimit to rateLimit --- .../admin/server/middlewares/rateLimit.js | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/core/admin/server/middlewares/rateLimit.js b/packages/core/admin/server/middlewares/rateLimit.js index 7df3a429ec..9c80034567 100644 --- a/packages/core/admin/server/middlewares/rateLimit.js +++ b/packages/core/admin/server/middlewares/rateLimit.js @@ -8,20 +8,20 @@ const { RateLimitError } = utils.errors; module.exports = (config, { strapi }) => async (ctx, next) => { - let ratelimitConfig = strapi.config.get('admin.ratelimit'); + let rateLimitConfig = strapi.config.get('admin.rateLimit'); - if (!ratelimitConfig) { - ratelimitConfig = { + if (!rateLimitConfig) { + rateLimitConfig = { enabled: true, }; } - if (!has('enabled', ratelimitConfig)) { - ratelimitConfig.enabled = true; + if (!has('enabled', rateLimitConfig)) { + rateLimitConfig.enabled = true; } - if (ratelimitConfig.enabled === true) { - const ratelimit = require('koa2-ratelimit').RateLimit; + if (rateLimitConfig.enabled === true) { + const rateLimit = require('koa2-ratelimit').RateLimit; const userEmail = toLower(ctx.request.body.email) || 'unknownEmail'; @@ -32,13 +32,11 @@ module.exports = handler() { throw new RateLimitError(); }, - ...ratelimitConfig, + ...rateLimitConfig, ...config, }; - console.log(loadConfig); - - return ratelimit.middleware(loadConfig)(ctx, next); + return rateLimit.middleware(loadConfig)(ctx, next); } return next(); From 8a62757911354932d0a859e19beb51305ff77499 Mon Sep 17 00:00:00 2001 From: Ben Irvin Date: Wed, 28 Dec 2022 09:14:56 +0100 Subject: [PATCH 22/27] fix events --- .../strapi/lib/commands/transfer/export.js | 48 +++---------------- .../strapi/lib/commands/transfer/import.js | 48 +++---------------- 2 files changed, 14 insertions(+), 82 deletions(-) diff --git a/packages/core/strapi/lib/commands/transfer/export.js b/packages/core/strapi/lib/commands/transfer/export.js index 055710afd3..e0c0de6d79 100644 --- a/packages/core/strapi/lib/commands/transfer/export.js +++ b/packages/core/strapi/lib/commands/transfer/export.js @@ -74,12 +74,11 @@ module.exports = async (opts) => { }, }); - let transferExitCode; logger.log(`Starting export...`); const progress = engine.progress.stream; - const telemetryPayload = (/* payload */) => { + const getTelemetryPayload = (/* payload */) => { return { eventProperties: { source: engine.sourceProvider.name, @@ -88,18 +87,8 @@ module.exports = async (opts) => { }; }; - progress.on('transfer::start', async (payload) => { - await strapi.telemetry.send('didDEITSProcessStart', telemetryPayload(payload)); - }); - - progress.on('transfer::finish', async (payload) => { - await strapi.telemetry.send('didDEITSProcessFinish', telemetryPayload(payload)); - transferExitCode = 0; - }); - - progress.on('transfer::error', async (payload) => { - await strapi.telemetry.send('didDEITSProcessFail', telemetryPayload(payload)); - transferExitCode = 1; + progress.on('transfer::start', async () => { + await strapi.telemetry.send('didDEITSProcessStart', getTelemetryPayload()); }); try { @@ -117,37 +106,14 @@ module.exports = async (opts) => { logger.log(`${chalk.bold('Export process has been completed successfully!')}`); logger.log(`Export archive is in ${chalk.green(outFile)}`); } catch (e) { + await strapi.telemetry.send('didDEITSProcessFail', getTelemetryPayload()); logger.error('Export process failed unexpectedly:', e.toString()); process.exit(1); } - /* - * We need to wait for the telemetry to finish before exiting the process. - * The order of execution for the overall export function is: - * - create providers and engine - * - create progress callbacks - * - await the engine transfer - * - having async calls inside, it allows the transfer::start to process - * - the code block including the table printing executes - * - *** any async code (for example, the fs.pathExists) after engine.transfer will execute next tick, therefore: - * - the progress callbacks execute - * - * Because of that, we have to wait until the progress callbacks have executed, but can't allow them to exit by themselves. - * Instead we have to wait for them to tell us it's safe to exit - */ - const waitForExitCode = async (maxWait) => { - const startTime = Date.now(); - while (Date.now() - startTime < maxWait) { - if (transferExitCode !== undefined) { - process.exit(transferExitCode); - } - await new Promise((resolve) => { - setTimeout(resolve, 50); - }); - } - process.exit(0); - }; - waitForExitCode(5000); + // Note: Telemetry can't be sent in a finish event, because it runs async after this block but we can't await it, so if process.exit is used it won't send + await strapi.telemetry.send('didDEITSProcessFinish', getTelemetryPayload()); + process.exit(0); }; /** diff --git a/packages/core/strapi/lib/commands/transfer/import.js b/packages/core/strapi/lib/commands/transfer/import.js index 00f8498665..2140f56ece 100644 --- a/packages/core/strapi/lib/commands/transfer/import.js +++ b/packages/core/strapi/lib/commands/transfer/import.js @@ -79,11 +79,10 @@ module.exports = async (opts) => { }; const engine = createTransferEngine(source, destination, engineOptions); - let transferExitCode; logger.info('Starting import...'); const progress = engine.progress.stream; - const telemetryPayload = (/* payload */) => { + const getTelemetryPayload = () => { return { eventProperties: { source: engine.sourceProvider.name, @@ -92,18 +91,8 @@ module.exports = async (opts) => { }; }; - progress.on('transfer::start', async (payload) => { - await strapiInstance.telemetry.send('didDEITSProcessStart', telemetryPayload(payload)); - }); - - progress.on('transfer::finish', async (payload) => { - await strapiInstance.telemetry.send('didDEITSProcessFinish', telemetryPayload(payload)); - transferExitCode = 0; - }); - - progress.on('transfer::error', async (payload) => { - await strapiInstance.telemetry.send('didDEITSProcessFail', telemetryPayload(payload)); - transferExitCode = 1; + progress.on('transfer::start', async () => { + await strapiInstance.telemetry.send('didDEITSProcessStart', getTelemetryPayload()); }); try { @@ -113,38 +102,15 @@ module.exports = async (opts) => { logger.info('Import process has been completed successfully!'); } catch (e) { + await strapiInstance.telemetry.send('didDEITSProcessFail', getTelemetryPayload()); logger.error('Import process failed unexpectedly:'); logger.error(e); process.exit(1); } - /* - * We need to wait for the telemetry to finish before exiting the process. - * The order of execution for the overall import function is: - * - create providers and engine - * - create progress callbacks - * - await the engine transfer - * - having async calls inside, it allows the transfer::start to process - * - the code block including the table printing executes - * - *** any async code (for example, the fs.pathExists) after engine.transfer will execute next tick, therefore: - * - the progress callbacks execute - * - * Because of that, we have to wait until the progress callbacks have executed, but can't allow them to exit by themselves. - * Instead we have to wait for them to tell us it's safe to exit - */ - const waitForExitCode = async (maxWait) => { - const startTime = Date.now(); - while (Date.now() - startTime < maxWait) { - if (transferExitCode !== undefined) { - process.exit(transferExitCode); - } - await new Promise((resolve) => { - setTimeout(resolve, 50); - }); - } - process.exit(0); - }; - waitForExitCode(5000); + // Note: Telemetry can't be sent in a finish event, because it runs async after this block but we can't await it, so if process.exit is used it won't send + await strapi.telemetry.send('didDEITSProcessFinish', getTelemetryPayload()); + process.exit(0); }; /** From 2a1335717bd9712081674853d656fd5faf24e41b Mon Sep 17 00:00:00 2001 From: Ben Irvin Date: Wed, 28 Dec 2022 11:00:52 +0100 Subject: [PATCH 23/27] fix tests --- .../lib/commands/__tests__/export.test.js | 145 +++++++++++------- 1 file changed, 90 insertions(+), 55 deletions(-) diff --git a/packages/core/strapi/lib/commands/__tests__/export.test.js b/packages/core/strapi/lib/commands/__tests__/export.test.js index fefced0dc5..6a57e78533 100644 --- a/packages/core/strapi/lib/commands/__tests__/export.test.js +++ b/packages/core/strapi/lib/commands/__tests__/export.test.js @@ -1,111 +1,146 @@ 'use strict'; -const utils = require('../transfer/utils'); - -const mockDataTransfer = { - createLocalFileDestinationProvider: jest.fn(), - createLocalStrapiSourceProvider: jest.fn(), - createTransferEngine() { - return { - transfer: jest.fn().mockReturnValue(Promise.resolve({})), - progress: { - on: jest.fn(), - stream: { - on: jest.fn(), - }, - }, - }; - }, -}; - -jest.mock( - '@strapi/data-transfer', - () => { - return mockDataTransfer; - }, - { virtual: true } -); - -const exportCommand = require('../transfer/export'); - -const exit = jest.spyOn(process, 'exit').mockImplementation(() => {}); -jest.spyOn(console, 'error').mockImplementation(() => {}); - -jest.mock('../transfer/utils'); - -const defaultFileName = 'defaultFilename'; - describe('export', () => { - beforeEach(() => { - jest.resetAllMocks(); - }); + const defaultFileName = 'defaultFilename'; + + // mock @strapi/data-transfer + const mockDataTransfer = { + createLocalFileDestinationProvider: jest.fn().mockReturnValue({ name: 'testDest' }), + createLocalStrapiSourceProvider: jest.fn().mockReturnValue({ name: 'testSource' }), + createTransferEngine() { + return { + transfer: jest.fn().mockReturnValue(Promise.resolve({})), + progress: { + on: jest.fn(), + stream: { + on: jest.fn(), + }, + }, + sourceProvider: { name: 'testSource' }, + destinationProvider: { name: 'testDestination' }, + }; + }, + }; + jest.mock( + '@strapi/data-transfer', + () => { + return mockDataTransfer; + }, + { virtual: true } + ); + + // mock utils + const mockUtils = { + createStrapiInstance() { + return { + telemetry: { + send: jest.fn(), + }, + }; + }, + getDefaultExportName: jest.fn(() => defaultFileName), + }; + jest.mock( + '../transfer/utils', + () => { + return mockUtils; + }, + { virtual: true } + ); + + // other spies= + jest.spyOn(console, 'log').mockImplementation(() => {}); + jest.spyOn(console, 'warn').mockImplementation(() => {}); + jest.spyOn(console, 'error').mockImplementation(() => {}); + + // Now that everything is mocked, import export command + const exportCommand = require('../transfer/export'); + + const expectExit = async (code, fn) => { + const exit = jest.spyOn(process, 'exit').mockImplementation((number) => { + throw new Error(`process.exit: ${number}`); + }); + await expect(async () => { + await fn(); + }).rejects.toThrow(); + expect(exit).toHaveBeenCalledWith(code); + exit.mockRestore(); + }; + + beforeEach(() => {}); it('uses path provided by user', async () => { - const filename = 'testfile'; + const filename = 'test'; - await exportCommand({ file: filename }); + await expectExit(1, async () => { + await exportCommand({ file: filename }); + }); expect(mockDataTransfer.createLocalFileDestinationProvider).toHaveBeenCalledWith( expect.objectContaining({ file: { path: filename }, }) ); - expect(utils.getDefaultExportName).not.toHaveBeenCalled(); - expect(exit).toHaveBeenCalled(); + expect(mockUtils.getDefaultExportName).not.toHaveBeenCalled(); }); it('uses default path if not provided by user', async () => { - utils.getDefaultExportName.mockReturnValue(defaultFileName); - - await exportCommand({}); + await expectExit(1, async () => { + await exportCommand({}); + }); + expect(mockUtils.getDefaultExportName).toHaveBeenCalledTimes(1); expect(mockDataTransfer.createLocalFileDestinationProvider).toHaveBeenCalledWith( expect.objectContaining({ file: { path: defaultFileName }, }) ); - - expect(utils.getDefaultExportName).toHaveBeenCalled(); - expect(exit).toHaveBeenCalled(); }); it('encrypts the output file if specified', async () => { const encrypt = true; - await exportCommand({ encrypt }); + await expectExit(1, async () => { + await exportCommand({ encrypt }); + }); + expect(mockDataTransfer.createLocalFileDestinationProvider).toHaveBeenCalledWith( expect.objectContaining({ encryption: { enabled: encrypt }, }) ); - expect(exit).toHaveBeenCalled(); }); it('encrypts the output file with the given key', async () => { const key = 'secret-key'; const encrypt = true; + await expectExit(1, async () => { + await exportCommand({ encrypt, key }); + }); - await exportCommand({ encrypt, key }); expect(mockDataTransfer.createLocalFileDestinationProvider).toHaveBeenCalledWith( expect.objectContaining({ encryption: { enabled: encrypt, key }, }) ); - expect(exit).toHaveBeenCalled(); }); it('uses compress option', async () => { - await exportCommand({ compress: false }); + await expectExit(1, async () => { + await exportCommand({ compress: false }); + }); + expect(mockDataTransfer.createLocalFileDestinationProvider).toHaveBeenCalledWith( expect.objectContaining({ compression: { enabled: false }, }) ); - await exportCommand({ compress: true }); + await expectExit(1, async () => { + await exportCommand({ compress: true }); + }); expect(mockDataTransfer.createLocalFileDestinationProvider).toHaveBeenCalledWith( expect.objectContaining({ compression: { enabled: true }, }) ); - expect(exit).toHaveBeenCalled(); }); }); From 1090e2244c7b895bb9fd19594e0ec7b8097c571e Mon Sep 17 00:00:00 2001 From: Convly Date: Wed, 28 Dec 2022 15:58:53 +0100 Subject: [PATCH 24/27] Run lerna watch commands in parallel + prevent output clear from tsc -w --- package.json | 2 +- packages/core/data-transfer/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index a6f2655350..77306117f0 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "prepare": "husky install", "setup": "yarn && yarn clean && yarn build", "clean": "lerna run --stream clean --no-private", - "watch": "lerna run --stream watch --no-private", + "watch": "lerna run --stream watch --no-private --parallel", "build": "lerna run --stream build --no-private", "generate": "plop --plopfile ./packages/generators/admin/plopfile.js", "lint": "npm-run-all -p lint:code lint:css", diff --git a/packages/core/data-transfer/package.json b/packages/core/data-transfer/package.json index 0c4f3e093e..3838a1543b 100644 --- a/packages/core/data-transfer/package.json +++ b/packages/core/data-transfer/package.json @@ -30,7 +30,7 @@ "build": "tsc -p tsconfig.json", "clean": "rimraf ./dist", "build:clean": "yarn clean && yarn build", - "watch": "yarn build -w", + "watch": "yarn build -w --preserveWatchOutput", "test:unit": "jest --verbose" }, "directories": { From fadcfac5ccc7c253acf4281e2e00a99f1f373d46 Mon Sep 17 00:00:00 2001 From: Ben Irvin Date: Wed, 28 Dec 2022 16:08:08 +0100 Subject: [PATCH 25/27] throw errors before restoring --- packages/core/data-transfer/lib/engine/index.ts | 7 ++++--- .../providers/local-file-source-provider/index.ts | 15 +++++++++++---- .../core/strapi/lib/commands/transfer/export.js | 3 +-- .../core/strapi/lib/commands/transfer/import.js | 3 +-- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/packages/core/data-transfer/lib/engine/index.ts b/packages/core/data-transfer/lib/engine/index.ts index f40c7b61fb..a8b4e4d1de 100644 --- a/packages/core/data-transfer/lib/engine/index.ts +++ b/packages/core/data-transfer/lib/engine/index.ts @@ -159,7 +159,7 @@ class TransferEngine< }); } - #emitTransferUpdate(type: 'start' | 'finish' | 'error', payload?: object) { + #emitTransferUpdate(type: 'init' | 'start' | 'finish' | 'error', payload?: object) { this.progress.stream.emit(`transfer::${type}`, payload); } @@ -336,9 +336,8 @@ class TransferEngine< // reset data between transfers this.progress.data = {}; - this.#emitTransferUpdate('start'); - try { + this.#emitTransferUpdate('init'); await this.bootstrap(); await this.init(); @@ -351,6 +350,8 @@ class TransferEngine< ); } + this.#emitTransferUpdate('start'); + await this.beforeTransfer(); // Run the transfer stages diff --git a/packages/core/data-transfer/lib/providers/local-file-source-provider/index.ts b/packages/core/data-transfer/lib/providers/local-file-source-provider/index.ts index 8904817982..fb828e41db 100644 --- a/packages/core/data-transfer/lib/providers/local-file-source-provider/index.ts +++ b/packages/core/data-transfer/lib/providers/local-file-source-provider/index.ts @@ -49,6 +49,8 @@ class LocalFileSourceProvider implements ISourceProvider { options: ILocalFileSourceProviderOptions; + #metadata?: IMetadata; + constructor(options: ILocalFileSourceProviderOptions) { this.options = options; @@ -60,15 +62,20 @@ class LocalFileSourceProvider implements ISourceProvider { } /** - * Pre flight checks regarding the provided options (making sure that the provided path is correct, etc...) + * Pre flight checks regarding the provided options, making sure that the file can be opened (decrypted, decompressed), etc. */ async bootstrap() { const { path: filePath } = this.options.file; + try { - // This is only to show a nicer error, it doesn't ensure the file will still exist when we try to open it later - await fs.access(filePath, fs.constants.R_OK); + // Read the metadata to ensure the file can be parsed + this.#metadata = await this.getMetadata(); } catch (e) { - throw new Error(`Can't access file "${filePath}".`); + throw new Error(`Can't read file "${filePath}".`); + } + + if (!this.#metadata) { + throw new Error('Can\'t read file "metadata.json"'); } } diff --git a/packages/core/strapi/lib/commands/transfer/export.js b/packages/core/strapi/lib/commands/transfer/export.js index 130a9466d5..cfc23bc26c 100644 --- a/packages/core/strapi/lib/commands/transfer/export.js +++ b/packages/core/strapi/lib/commands/transfer/export.js @@ -75,8 +75,6 @@ module.exports = async (opts) => { }); try { - logger.log(`Starting export...`); - const progress = engine.progress.stream; const telemetryPayload = (/* payload */) => { @@ -89,6 +87,7 @@ module.exports = async (opts) => { }; progress.on('transfer::start', (payload) => { + logger.log(`Starting export...`); strapi.telemetry.send('didDEITSProcessStart', telemetryPayload(payload)); }); diff --git a/packages/core/strapi/lib/commands/transfer/import.js b/packages/core/strapi/lib/commands/transfer/import.js index df8506b3b8..d19f8ac51d 100644 --- a/packages/core/strapi/lib/commands/transfer/import.js +++ b/packages/core/strapi/lib/commands/transfer/import.js @@ -80,8 +80,6 @@ module.exports = async (opts) => { const engine = createTransferEngine(source, destination, engineOptions); try { - logger.info('Starting import...'); - const progress = engine.progress.stream; const telemetryPayload = (/* payload */) => { return { @@ -93,6 +91,7 @@ module.exports = async (opts) => { }; progress.on('transfer::start', (payload) => { + logger.info('Starting import...'); strapiInstance.telemetry.send('didDEITSProcessStart', telemetryPayload(payload)); }); From 8677a4ac6c68ec9b6431dfd49a121fcf3cd991d5 Mon Sep 17 00:00:00 2001 From: Ben Irvin Date: Wed, 28 Dec 2022 16:56:27 +0100 Subject: [PATCH 26/27] Update packages/core/data-transfer/lib/providers/local-file-source-provider/index.ts --- .../lib/providers/local-file-source-provider/index.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/core/data-transfer/lib/providers/local-file-source-provider/index.ts b/packages/core/data-transfer/lib/providers/local-file-source-provider/index.ts index fb828e41db..647cf3111e 100644 --- a/packages/core/data-transfer/lib/providers/local-file-source-provider/index.ts +++ b/packages/core/data-transfer/lib/providers/local-file-source-provider/index.ts @@ -74,9 +74,6 @@ class LocalFileSourceProvider implements ISourceProvider { throw new Error(`Can't read file "${filePath}".`); } - if (!this.#metadata) { - throw new Error('Can\'t read file "metadata.json"'); - } } getMetadata() { From b566c0d44c6555a2d6174bfcecdbada638ceaf35 Mon Sep 17 00:00:00 2001 From: Ben Irvin Date: Wed, 28 Dec 2022 16:56:57 +0100 Subject: [PATCH 27/27] Update packages/core/data-transfer/lib/providers/local-file-source-provider/index.ts --- .../lib/providers/local-file-source-provider/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/core/data-transfer/lib/providers/local-file-source-provider/index.ts b/packages/core/data-transfer/lib/providers/local-file-source-provider/index.ts index 647cf3111e..ff078043dd 100644 --- a/packages/core/data-transfer/lib/providers/local-file-source-provider/index.ts +++ b/packages/core/data-transfer/lib/providers/local-file-source-provider/index.ts @@ -73,7 +73,6 @@ class LocalFileSourceProvider implements ISourceProvider { } catch (e) { throw new Error(`Can't read file "${filePath}".`); } - } getMetadata() {