ghost 4.22.1 → 4.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/.c8rc.json +24 -0
  2. package/.eslintrc.js +6 -0
  3. package/Gruntfile.js +1 -1
  4. package/content/public/README.md +3 -0
  5. package/core/boot.js +20 -12
  6. package/core/built/assets/{chunk.3.1148677ff3b78e5aeaee.js → chunk.3.8f95b516d88ff4eec64c.js} +18 -18
  7. package/core/built/assets/{ghost-dark-684ad238e1a858c7cb5be6988de7c6f5.css → ghost-dark-42cf6e0c730578940ec069bda45aea41.css} +1 -1
  8. package/core/built/assets/{ghost.min-f7037eca328f4d4eb99f0309c19c9bae.js → ghost.min-cccc107e881b74c7aaf1a73e1e5e0dee.js} +189 -143
  9. package/core/built/assets/{ghost.min-66e08535f8bb797a8c40e0a2b31f1e9e.css → ghost.min-fcf6a0738421f86c47c55f20d00c5ba9.css} +1 -1
  10. package/core/built/assets/icons/powered-by-tenor.svg +35 -0
  11. package/core/built/assets/icons/tenor.svg +7 -0
  12. package/core/built/assets/{vendor.min-7c8fdd90f7ecd2e94328a07ea3b64608.js → vendor.min-c9002845b6c30ac978abdadde9f33d7c.js} +8189 -7601
  13. package/core/frontend/apps/amp/lib/views/amp.hbs +104 -0
  14. package/core/frontend/apps/private-blogging/lib/router.js +1 -1
  15. package/core/frontend/services/card-assets/service.js +21 -13
  16. package/core/frontend/services/routing/CollectionRouter.js +4 -5
  17. package/core/frontend/services/routing/EmailRouter.js +1 -1
  18. package/core/frontend/services/routing/ParentRouter.js +0 -8
  19. package/core/frontend/services/routing/PreviewRouter.js +1 -1
  20. package/core/frontend/services/routing/StaticPagesRouter.js +1 -1
  21. package/core/frontend/services/routing/StaticRoutesRouter.js +4 -4
  22. package/core/frontend/services/routing/TaxonomyRouter.js +3 -3
  23. package/core/frontend/services/routing/{middlewares → middleware}/index.js +0 -0
  24. package/core/frontend/services/routing/{middlewares → middleware}/page-param.js +0 -0
  25. package/core/frontend/services/routing/router-manager.js +7 -2
  26. package/core/frontend/services/rss/generate-feed.js +2 -1
  27. package/core/frontend/src/cards/css/bookmark.css +66 -48
  28. package/core/frontend/src/cards/css/button.css +30 -0
  29. package/core/frontend/src/cards/css/callout.css +50 -0
  30. package/core/frontend/src/cards/css/gallery.css +8 -13
  31. package/core/frontend/src/cards/css/nft.css +94 -0
  32. package/core/frontend/src/cards/css/toggle.css +47 -0
  33. package/core/frontend/src/cards/js/toggle.js +16 -0
  34. package/core/frontend/web/middleware/serve-public-file.js +14 -8
  35. package/core/frontend/web/routes.js +0 -1
  36. package/core/frontend/web/site.js +15 -12
  37. package/core/server/adapters/storage/LocalFilesStorage.js +17 -0
  38. package/core/server/adapters/storage/LocalImagesStorage.js +1 -0
  39. package/core/server/adapters/storage/LocalMediaStorage.js +2 -1
  40. package/core/server/adapters/storage/LocalStorageBase.js +30 -5
  41. package/core/server/api/canary/authentication.js +1 -1
  42. package/core/server/api/canary/files.js +19 -0
  43. package/core/server/api/canary/index.js +4 -0
  44. package/core/server/api/canary/media.js +25 -5
  45. package/core/server/api/canary/oembed.js +3 -0
  46. package/core/server/api/canary/utils/serializers/input/index.js +4 -0
  47. package/core/server/api/canary/utils/serializers/input/media.js +8 -0
  48. package/core/server/api/canary/utils/serializers/output/config.js +21 -14
  49. package/core/server/api/canary/utils/serializers/output/files.js +27 -0
  50. package/core/server/api/canary/utils/serializers/output/index.js +4 -0
  51. package/core/server/api/canary/utils/serializers/output/media.js +9 -0
  52. package/core/server/api/canary/utils/validators/input/files.js +7 -0
  53. package/core/server/api/canary/utils/validators/input/index.js +4 -0
  54. package/core/server/api/canary/utils/validators/input/media.js +4 -0
  55. package/core/server/api/v2/authentication.js +1 -1
  56. package/core/server/api/v3/authentication.js +1 -1
  57. package/core/server/data/db/connection.js +7 -0
  58. package/core/server/data/importer/importers/data/data-importer.js +3 -3
  59. package/core/server/data/migrations/init/2-create-fixtures.js +3 -20
  60. package/core/server/data/migrations/versions/1.21/1-add-contributor-role.js +5 -5
  61. package/core/server/data/migrations/versions/2.15/2-insert-zapier-integration.js +3 -3
  62. package/core/server/data/migrations/versions/2.2/3-insert-admin-integration-role.js +5 -5
  63. package/core/server/data/migrations/versions/2.27/1-insert-ghost-db-backup-role.js +5 -6
  64. package/core/server/data/migrations/versions/2.27/2-insert-db-backup-integration.js +3 -4
  65. package/core/server/data/migrations/versions/2.28/3-insert-ghost-scheduler-role.js +7 -7
  66. package/core/server/data/migrations/versions/2.28/4-insert-scheduler-integration.js +3 -3
  67. package/core/server/data/migrations/versions/4.23/01-truncate-offer-names.js +58 -0
  68. package/core/server/data/schema/fixtures/fixture-manager.js +340 -0
  69. package/core/server/data/schema/fixtures/index.js +8 -2
  70. package/core/server/services/mega/post-email-serializer.js +5 -1
  71. package/core/server/services/mega/segment-parser.js +1 -2
  72. package/core/server/services/mega/template.js +69 -1
  73. package/core/server/services/nft-oembed.js +57 -0
  74. package/core/server/services/oembed.js +161 -126
  75. package/core/server/services/public-config/config.js +2 -1
  76. package/core/server/services/stripe/index.js +4 -2
  77. package/core/server/services/url/Resource.js +1 -1
  78. package/core/server/services/url/Resources.js +36 -23
  79. package/core/server/services/url/UrlGenerator.js +23 -20
  80. package/core/server/services/url/UrlService.js +123 -21
  81. package/core/server/services/url/Urls.js +7 -2
  82. package/core/server/services/url/index.js +9 -1
  83. package/core/server/web/admin/app.js +6 -6
  84. package/core/server/web/admin/views/default-prod.html +4 -4
  85. package/core/server/web/admin/views/default.html +4 -4
  86. package/core/server/web/api/app.js +1 -1
  87. package/core/server/web/api/canary/admin/app.js +4 -4
  88. package/core/server/web/api/canary/admin/middleware.js +6 -6
  89. package/core/server/web/api/canary/admin/routes.js +20 -5
  90. package/core/server/web/api/canary/content/app.js +4 -4
  91. package/core/server/web/api/canary/content/middleware.js +3 -3
  92. package/core/server/web/api/middleware/cors.js +7 -7
  93. package/core/server/web/api/v2/admin/app.js +4 -4
  94. package/core/server/web/api/v2/admin/middleware.js +6 -6
  95. package/core/server/web/api/v2/admin/routes.js +5 -5
  96. package/core/server/web/api/v2/content/app.js +4 -4
  97. package/core/server/web/api/v2/content/middleware.js +3 -3
  98. package/core/server/web/api/v3/admin/app.js +4 -4
  99. package/core/server/web/api/v3/admin/middleware.js +6 -6
  100. package/core/server/web/api/v3/admin/routes.js +5 -5
  101. package/core/server/web/api/v3/content/app.js +4 -4
  102. package/core/server/web/api/v3/content/middleware.js +3 -3
  103. package/core/server/web/members/app.js +7 -7
  104. package/core/server/web/oauth/app.js +1 -1
  105. package/core/server/web/parent/app.js +2 -3
  106. package/core/server/web/parent/frontend.js +1 -1
  107. package/core/server/web/shared/index.js +2 -2
  108. package/core/server/web/shared/{middlewares → middleware}/api/index.js +0 -0
  109. package/core/server/web/shared/{middlewares → middleware}/api/spam-prevention.js +0 -0
  110. package/core/server/web/shared/{middlewares → middleware}/brute.js +0 -0
  111. package/core/server/web/shared/{middlewares → middleware}/cache-control.js +0 -0
  112. package/core/server/web/shared/{middlewares → middleware}/error-handler.js +0 -0
  113. package/core/server/web/shared/{middlewares → middleware}/index.js +0 -0
  114. package/core/server/web/shared/{middlewares → middleware}/maintenance.js +0 -0
  115. package/core/server/web/shared/{middlewares → middleware}/pretty-urls.js +0 -0
  116. package/core/server/web/shared/{middlewares → middleware}/uncapitalise.js +0 -0
  117. package/core/server/web/shared/{middlewares → middleware}/url-redirects.js +0 -0
  118. package/core/shared/config/defaults.json +10 -2
  119. package/core/shared/config/helpers.js +44 -0
  120. package/core/shared/config/loader.js +1 -1
  121. package/core/shared/config/overrides.json +2 -2
  122. package/core/shared/labs.js +8 -1
  123. package/loggingrc.js +19 -20
  124. package/package.json +35 -35
  125. package/urls.json +597 -0
  126. package/yarn.lock +655 -339
  127. package/core/server/data/schema/fixtures/utils.js +0 -321
  128. package/core/server/web/parent/vhost-utils.js +0 -39
@@ -3,5 +3,9 @@ const limitService = require('../../../../../services/limits');
3
3
  module.exports = {
4
4
  async upload(apiConfig, frame) {
5
5
  await limitService.errorIfIsOverLimit('uploads', {currentCount: frame.file.size});
6
+ },
7
+
8
+ async uploadThumbnail(apiConfig, frame) {
9
+ await limitService.errorIfIsOverLimit('uploads', {currentCount: frame.file.size});
6
10
  }
7
11
  };
@@ -145,7 +145,7 @@ module.exports = {
145
145
  options = Object.assign(options, {context: {internal: true}});
146
146
  return auth.passwordreset.doReset(options, tokenParts, api.settings)
147
147
  .then((params) => {
148
- web.shared.middlewares.api.spamPrevention.userLogin().reset(frame.options.ip, `${tokenParts.email}login`);
148
+ web.shared.middleware.api.spamPrevention.userLogin().reset(frame.options.ip, `${tokenParts.email}login`);
149
149
  return params;
150
150
  });
151
151
  });
@@ -146,7 +146,7 @@ module.exports = {
146
146
  options = Object.assign(options, {context: {internal: true}});
147
147
  return auth.passwordreset.doReset(options, tokenParts, api.settings)
148
148
  .then((params) => {
149
- web.shared.middlewares.api.spamPrevention.userLogin().reset(frame.options.ip, `${tokenParts.email}login`);
149
+ web.shared.middleware.api.spamPrevention.userLogin().reset(frame.options.ip, `${tokenParts.email}login`);
150
150
  return params;
151
151
  });
152
152
  });
@@ -17,6 +17,13 @@ function configure(dbConfig) {
17
17
  dbConfig.pool = {
18
18
  afterCreate(conn, cb) {
19
19
  conn.run('PRAGMA foreign_keys = ON', cb);
20
+
21
+ // These two are meant to improve performance at the cost of reliability
22
+ // Should be safe for tests. We add them here and leave them on
23
+ if (config.get('env').startsWith('testing')) {
24
+ conn.run('PRAGMA synchronous = OFF;');
25
+ conn.run('PRAGMA journal_mode = TRUNCATE;');
26
+ }
20
27
  }
21
28
  };
22
29
 
@@ -112,7 +112,7 @@ DataImporter = {
112
112
  });
113
113
  });
114
114
 
115
- sequence(ops)
115
+ return sequence(ops)
116
116
  .then(function () {
117
117
  results.forEach(function (promise) {
118
118
  if (!promise.isFulfilled()) {
@@ -121,9 +121,9 @@ DataImporter = {
121
121
  });
122
122
 
123
123
  if (errors.length === 0) {
124
- transacting.commit();
124
+ return;
125
125
  } else {
126
- transacting.rollback(errors);
126
+ throw errors;
127
127
  }
128
128
  });
129
129
  }).then(function () {
@@ -1,26 +1,9 @@
1
- const Promise = require('bluebird');
2
- const _ = require('lodash');
3
- const fixtures = require('../../schema/fixtures');
4
- const logging = require('@tryghost/logging');
1
+ const {fixtureManager} = require('../../schema/fixtures');
5
2
 
6
3
  module.exports.config = {
7
4
  transaction: true
8
5
  };
9
6
 
10
- module.exports.up = async (options) => {
11
- const localOptions = _.merge({
12
- context: {internal: true},
13
- migrating: true
14
- }, options);
15
-
16
- await Promise.mapSeries(fixtures.models, async (model) => {
17
- logging.info('Model: ' + model.name);
18
-
19
- await fixtures.utils.addFixturesForModel(model, localOptions);
20
- });
21
-
22
- await Promise.mapSeries(fixtures.relations, async (relation) => {
23
- logging.info('Relation: ' + relation.from.model + ' to ' + relation.to.model);
24
- await fixtures.utils.addFixturesForRelation(relation, localOptions);
25
- });
7
+ module.exports.up = async function insertFixtures(options) {
8
+ return await fixtureManager.addAllFixtures(options);
26
9
  };
@@ -1,19 +1,19 @@
1
1
  const merge = require('lodash/merge');
2
- const utils = require('../../../schema/fixtures/utils');
2
+ const {fixtureManager} = require('../../../schema/fixtures');
3
3
  const models = require('../../../../models');
4
4
  const permissions = require('../../../../services/permissions');
5
5
  const logging = require('@tryghost/logging');
6
6
  const _private = {};
7
7
 
8
8
  _private.addRole = function addRole(options) {
9
- const contributorRole = utils.findModelFixtureEntry('Role', {name: 'Contributor'});
9
+ const contributorRole = fixtureManager.findModelFixtureEntry('Role', {name: 'Contributor'});
10
10
  const message = 'Adding "Contributor" role to roles table';
11
11
 
12
12
  return models.Role.findOne({name: contributorRole.name}, options)
13
13
  .then((role) => {
14
14
  if (!role) {
15
15
  logging.info(message);
16
- return utils.addFixturesForModel({name: 'Role', entries: [contributorRole]}, options);
16
+ return fixtureManager.addFixturesForModel({name: 'Role', entries: [contributorRole]}, options);
17
17
  }
18
18
 
19
19
  logging.warn(message);
@@ -22,10 +22,10 @@ _private.addRole = function addRole(options) {
22
22
  };
23
23
 
24
24
  _private.addContributorPermissions = function getPermissions(options) {
25
- const relations = utils.findRelationFixture('Role', 'Permission');
25
+ const relations = fixtureManager.findRelationFixture('Role', 'Permission');
26
26
  const message = 'Adding permissions_roles fixtures for the contributor role';
27
27
 
28
- return utils.addFixturesForRelation({
28
+ return fixtureManager.addFixturesForRelation({
29
29
  from: relations.from,
30
30
  to: relations.to,
31
31
  entries: {
@@ -1,7 +1,7 @@
1
1
  const logging = require('@tryghost/logging');
2
2
  const merge = require('lodash/merge');
3
3
  const models = require('../../../../models');
4
- const utils = require('../../../schema/fixtures/utils');
4
+ const {fixtureManager} = require('../../../schema/fixtures');
5
5
 
6
6
  const _private = {};
7
7
 
@@ -15,12 +15,12 @@ _private.printResult = function printResult(result, message) {
15
15
 
16
16
  _private.addZapierIntegration = (options) => {
17
17
  const message = 'Adding "Zapier" integration';
18
- const fixtureIntegration = utils.findModelFixtureEntry('Integration', {slug: 'zapier'});
18
+ const fixtureIntegration = fixtureManager.findModelFixtureEntry('Integration', {slug: 'zapier'});
19
19
 
20
20
  return models.Integration.findOne({slug: fixtureIntegration.slug}, options)
21
21
  .then((integration) => {
22
22
  if (!integration) {
23
- return utils.addFixturesForModel({
23
+ return fixtureManager.addFixturesForModel({
24
24
  name: 'Integration',
25
25
  entries: [fixtureIntegration]
26
26
  }, options).then(result => _private.printResult(result, message));
@@ -1,7 +1,7 @@
1
1
  const logging = require('@tryghost/logging');
2
2
  const merge = require('lodash/merge');
3
3
  const models = require('../../../../models');
4
- const utils = require('../../../schema/fixtures/utils');
4
+ const {fixtureManager} = require('../../../schema/fixtures');
5
5
 
6
6
  const _private = {};
7
7
 
@@ -15,12 +15,12 @@ _private.printResult = function printResult(result, message) {
15
15
 
16
16
  _private.addApiKeyRole = (options) => {
17
17
  const message = 'Adding "Admin Integration" role to roles table';
18
- const apiKeyRole = utils.findModelFixtureEntry('Role', {name: 'Admin Integration'});
18
+ const apiKeyRole = fixtureManager.findModelFixtureEntry('Role', {name: 'Admin Integration'});
19
19
 
20
20
  return models.Role.findOne({name: apiKeyRole.name}, options)
21
21
  .then((role) => {
22
22
  if (!role) {
23
- return utils.addFixturesForModel({
23
+ return fixtureManager.addFixturesForModel({
24
24
  name: 'Role',
25
25
  entries: [apiKeyRole]
26
26
  }, options).then(result => _private.printResult(result, message));
@@ -32,9 +32,9 @@ _private.addApiKeyRole = (options) => {
32
32
 
33
33
  _private.addApiKeyPermissions = (options) => {
34
34
  const message = 'Adding permissions for the "Admin Integration" role';
35
- const relations = utils.findRelationFixture('Role', 'Permission');
35
+ const relations = fixtureManager.findRelationFixture('Role', 'Permission');
36
36
 
37
- return utils.addFixturesForRelation({
37
+ return fixtureManager.addFixturesForRelation({
38
38
  from: relations.from,
39
39
  to: relations.to,
40
40
  entries: {
@@ -1,7 +1,7 @@
1
1
  const logging = require('@tryghost/logging');
2
2
  const merge = require('lodash/merge');
3
3
  const models = require('../../../../models');
4
- const utils = require('../../../schema/fixtures/utils');
4
+ const {fixtureManager} = require('../../../schema/fixtures');
5
5
 
6
6
  const _private = {};
7
7
 
@@ -15,12 +15,12 @@ _private.printResult = function printResult(result, message) {
15
15
 
16
16
  _private.addApiKeyRole = (options) => {
17
17
  const message = 'Adding "DB Backup Integration" role to roles table';
18
- const apiKeyRole = utils.findModelFixtureEntry('Role', {name: 'DB Backup Integration'});
18
+ const apiKeyRole = fixtureManager.findModelFixtureEntry('Role', {name: 'DB Backup Integration'});
19
19
 
20
20
  return models.Role.findOne({name: apiKeyRole.name}, options)
21
21
  .then((role) => {
22
22
  if (!role) {
23
- return utils.addFixturesForModel({
23
+ return fixtureManager.addFixturesForModel({
24
24
  name: 'Role',
25
25
  entries: [apiKeyRole]
26
26
  }, options).then(result => _private.printResult(result, message));
@@ -32,9 +32,9 @@ _private.addApiKeyRole = (options) => {
32
32
 
33
33
  _private.addApiKeyPermissions = (options) => {
34
34
  const message = 'Adding permissions for the "DB Backup Integration" role';
35
- const relations = utils.findRelationFixture('Role', 'Permission');
35
+ const relations = fixtureManager.findRelationFixture('Role', 'Permission');
36
36
 
37
- return utils.addFixturesForRelation({
37
+ return fixtureManager.addFixturesForRelation({
38
38
  from: relations.from,
39
39
  to: relations.to,
40
40
  entries: {
@@ -81,4 +81,3 @@ module.exports.down = (options) => {
81
81
 
82
82
  return _private.removeApiKeyPermissionsAndRole(localOptions);
83
83
  };
84
-
@@ -1,7 +1,7 @@
1
1
  const logging = require('@tryghost/logging');
2
2
  const merge = require('lodash/merge');
3
3
  const models = require('../../../../models');
4
- const utils = require('../../../schema/fixtures/utils');
4
+ const {fixtureManager} = require('../../../schema/fixtures');
5
5
 
6
6
  const _private = {};
7
7
 
@@ -15,12 +15,12 @@ _private.printResult = function printResult(result, message) {
15
15
 
16
16
  _private.addGhostBackupIntegration = (options) => {
17
17
  const message = 'Adding "Ghost Backup DB" integration';
18
- const fixtureIntegration = utils.findModelFixtureEntry('Integration', {slug: 'ghost-backup'});
18
+ const fixtureIntegration = fixtureManager.findModelFixtureEntry('Integration', {slug: 'ghost-backup'});
19
19
 
20
20
  return models.Integration.findOne({slug: fixtureIntegration.slug}, options)
21
21
  .then((integration) => {
22
22
  if (!integration) {
23
- return utils.addFixturesForModel({
23
+ return fixtureManager.addFixturesForModel({
24
24
  name: 'Integration',
25
25
  entries: [fixtureIntegration]
26
26
  }, options).then(result => _private.printResult(result, message));
@@ -67,4 +67,3 @@ module.exports.down = (options) => {
67
67
 
68
68
  return _private.removeGhostBackupIntegration(localOptions);
69
69
  };
70
-
@@ -1,7 +1,7 @@
1
1
  const logging = require('@tryghost/logging');
2
2
  const merge = require('lodash/merge');
3
3
  const models = require('../../../../models');
4
- const utils = require('../../../schema/fixtures/utils');
4
+ const {fixtureManager} = require('../../../schema/fixtures');
5
5
 
6
6
  const resource = 'post';
7
7
  const _private = {};
@@ -16,12 +16,12 @@ _private.printResult = function printResult(result, message) {
16
16
 
17
17
  _private.addSchedulerRole = (options) => {
18
18
  const message = 'Adding "Scheduler Integration" role to roles table';
19
- const apiKeyRole = utils.findModelFixtureEntry('Role', {name: 'Scheduler Integration'});
19
+ const apiKeyRole = fixtureManager.findModelFixtureEntry('Role', {name: 'Scheduler Integration'});
20
20
 
21
21
  return models.Role.findOne({name: apiKeyRole.name}, options)
22
22
  .then((role) => {
23
23
  if (!role) {
24
- return utils.addFixturesForModel({
24
+ return fixtureManager.addFixturesForModel({
25
25
  name: 'Role',
26
26
  entries: [apiKeyRole]
27
27
  }, options).then(result => _private.printResult(result, message));
@@ -32,19 +32,19 @@ _private.addSchedulerRole = (options) => {
32
32
  };
33
33
 
34
34
  _private.addPublishPermission = (options) => {
35
- const modelToAdd = utils.findModelFixtures('Permission', {object_type: resource, action_type: 'publish'});
35
+ const modelToAdd = fixtureManager.findModelFixtures('Permission', {object_type: resource, action_type: 'publish'});
36
36
 
37
- return utils.addFixturesForModel(modelToAdd, options)
37
+ return fixtureManager.addFixturesForModel(modelToAdd, options)
38
38
  .then(result => _private.printResult(result, `Adding "publish" permissions fixtures for ${resource}s`));
39
39
  };
40
40
 
41
41
  _private.removeApiKeyPermissionsAndRole = (options) => {
42
42
  const message = 'Rollback: Removing "Scheduler Integration" role and permissions';
43
43
 
44
- const modelToRemove = utils.findModelFixtures('Permission', {object_type: resource, action_type: 'publish'});
44
+ const modelToRemove = fixtureManager.findModelFixtures('Permission', {object_type: resource, action_type: 'publish'});
45
45
 
46
46
  // permission model automatically cleans up permissions_roles on .destroy()
47
- return utils.removeFixturesForModel(modelToRemove, options)
47
+ return fixtureManager.removeFixturesForModel(modelToRemove, options)
48
48
  .then(result => _private.printResult(result, `Removing "publish" permissions fixtures for ${resource}s`))
49
49
  .then(() => models.Role.findOne({name: 'Scheduler Integration'}, options))
50
50
  .then((role) => {
@@ -1,7 +1,7 @@
1
1
  const logging = require('@tryghost/logging');
2
2
  const merge = require('lodash/merge');
3
3
  const models = require('../../../../models');
4
- const utils = require('../../../schema/fixtures/utils');
4
+ const {fixtureManager} = require('../../../schema/fixtures');
5
5
 
6
6
  const _private = {};
7
7
 
@@ -15,12 +15,12 @@ _private.printResult = function printResult(result, message) {
15
15
 
16
16
  _private.addGhostSchedulerIntegration = (options) => {
17
17
  const message = 'Adding "Ghost Scheduler" integration';
18
- const fixtureIntegration = utils.findModelFixtureEntry('Integration', {slug: 'ghost-scheduler'});
18
+ const fixtureIntegration = fixtureManager.findModelFixtureEntry('Integration', {slug: 'ghost-scheduler'});
19
19
 
20
20
  return models.Integration.findOne({slug: fixtureIntegration.slug}, options)
21
21
  .then((integration) => {
22
22
  if (!integration) {
23
- return utils.addFixturesForModel({
23
+ return fixtureManager.addFixturesForModel({
24
24
  name: 'Integration',
25
25
  entries: [fixtureIntegration]
26
26
  }, options).then(result => _private.printResult(result, message));
@@ -0,0 +1,58 @@
1
+ const logging = require('@tryghost/logging');
2
+ const {createTransactionalMigration} = require('../../utils');
3
+
4
+ /**
5
+ * @param {(val: string) => boolean} exists
6
+ * @param {string} requested
7
+ * @param {string} attempt
8
+ * @param {number} n
9
+ *
10
+ * @returns {string}
11
+ */
12
+ function getUnique(exists, requested, attempt = requested, n = 1) {
13
+ if (!exists(attempt)) {
14
+ return attempt;
15
+ }
16
+ const newAttempt = requested.slice(0, -n.toString().length) + n;
17
+ return getUnique(exists, requested, newAttempt, n + 1);
18
+ }
19
+
20
+ module.exports = createTransactionalMigration(
21
+ async function up(knex) {
22
+ const allOffers = await knex
23
+ .select('id', 'name')
24
+ .from('offers');
25
+
26
+ const offersNeedingTrunctation = allOffers.filter((row) => {
27
+ return row.name.length >= 40;
28
+ });
29
+
30
+ if (offersNeedingTrunctation.length === 0) {
31
+ logging.warn('No Offers found needing truncation');
32
+ return;
33
+ } else {
34
+ logging.info(`Found ${offersNeedingTrunctation.length} Offers needing truncation`);
35
+ }
36
+
37
+ const truncatedOffers = offersNeedingTrunctation.reduce((offers, row) => {
38
+ function exists(name) {
39
+ return offers.find(offer => offer.name === name) !== undefined;
40
+ }
41
+
42
+ const updatedRow = {
43
+ id: row.id,
44
+ name: getUnique(exists, row.name.slice(0, 40))
45
+ };
46
+
47
+ return offers.concat(updatedRow);
48
+ }, []);
49
+
50
+ for (const truncatedOffer of truncatedOffers) {
51
+ await knex('offers')
52
+ .update('name', truncatedOffer.name)
53
+ .where('id', truncatedOffer.id);
54
+ }
55
+ },
56
+ // no-op we've lost the data required to roll this back
57
+ async function down() {}
58
+ );