ghost 5.119.3 → 5.120.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 (129) hide show
  1. package/components/tryghost-i18n-5.120.0.tgz +0 -0
  2. package/core/boot.js +0 -2
  3. package/core/built/admin/assets/admin-x-activitypub/admin-x-activitypub.js +7555 -7216
  4. package/core/built/admin/assets/admin-x-settings/{CodeEditorView-60ce658c.mjs → CodeEditorView-1c5b0683.mjs} +2 -2
  5. package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +2 -2
  6. package/core/built/admin/assets/admin-x-settings/{index-8480baa8.mjs → index-14e518a7.mjs} +3 -3
  7. package/core/built/admin/assets/admin-x-settings/{index-a2648c61.mjs → index-fc9f985b.mjs} +2 -2
  8. package/core/built/admin/assets/admin-x-settings/{modals-6900c1d5.mjs → modals-15bc6a0f.mjs} +7192 -6656
  9. package/core/built/admin/assets/{chunk.137.c9bf40f01afeeadb4660.js → chunk.383.25fca2f09b4896656125.js} +76 -59
  10. package/core/built/admin/assets/chunk.524.1657b12c0ab25dd9fb79.js +28 -0
  11. package/core/built/admin/assets/{chunk.582.2697b46a5652693fc674.js → chunk.582.09869b1f1a3cc0ab81f6.js} +19 -26
  12. package/core/built/admin/assets/{ghost-843572e9507d099162ae744d791daba1.js → ghost-b3b44421acca3b3eec76bfbb6ba0e81b.js} +3 -3
  13. package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +12578 -12352
  14. package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +423 -211
  15. package/core/built/admin/assets/posts/posts.js +13680 -13671
  16. package/core/built/admin/assets/stats/stats.js +16457 -16635
  17. package/core/built/admin/assets/{vendor-8f805740fee4db959a5b2119001a56b1.js → vendor-4ce6d282a2a00fe486a0951e0591da19.js} +11 -9
  18. package/core/built/admin/index.html +5 -5
  19. package/core/frontend/helpers/match.js +6 -0
  20. package/core/frontend/services/routing/ParentRouter.js +1 -1
  21. package/core/frontend/services/routing/controllers/email-post.js +0 -2
  22. package/core/frontend/services/routing/controllers/previews.js +0 -3
  23. package/core/frontend/web/middleware/frontend-caching.js +2 -2
  24. package/core/server/api/endpoints/authentication.js +37 -73
  25. package/core/server/api/endpoints/authors-public.js +8 -9
  26. package/core/server/api/endpoints/db.js +34 -35
  27. package/core/server/api/endpoints/emails.js +8 -10
  28. package/core/server/api/endpoints/integrations.js +20 -18
  29. package/core/server/api/endpoints/invites.js +8 -10
  30. package/core/server/api/endpoints/labels.js +19 -23
  31. package/core/server/api/endpoints/notifications.js +3 -4
  32. package/core/server/api/endpoints/pages-public.js +8 -10
  33. package/core/server/api/endpoints/pages.js +14 -18
  34. package/core/server/api/endpoints/posts-public.js +8 -10
  35. package/core/server/api/endpoints/posts.js +6 -8
  36. package/core/server/api/endpoints/previews.js +8 -10
  37. package/core/server/api/endpoints/redirects.js +7 -8
  38. package/core/server/api/endpoints/schedules.js +5 -7
  39. package/core/server/api/endpoints/slugs.js +7 -9
  40. package/core/server/api/endpoints/snippets.js +16 -20
  41. package/core/server/api/endpoints/tags-public.js +8 -10
  42. package/core/server/api/endpoints/tags.js +19 -23
  43. package/core/server/api/endpoints/themes.js +6 -8
  44. package/core/server/api/endpoints/users.js +31 -36
  45. package/core/server/api/endpoints/utils/permissions.js +10 -10
  46. package/core/server/api/endpoints/utils/serializers/output/roles.js +9 -10
  47. package/core/server/api/endpoints/utils/validators/input/images.js +43 -52
  48. package/core/server/api/endpoints/utils/validators/input/invites.js +6 -8
  49. package/core/server/api/endpoints/webhooks.js +38 -42
  50. package/core/server/data/migrations/versions/5.120/2025-05-07-14-57-38-add-newsletters-button-corners-column.js +8 -0
  51. package/core/server/data/migrations/versions/5.120/2025-05-13-17-36-56-add-newsletters-button-style-column.js +8 -0
  52. package/core/server/data/migrations/versions/5.120/2025-05-14-20-00-15-add-newsletters-setting-columns.js +22 -0
  53. package/core/server/data/schema/schema.js +6 -1
  54. package/core/server/lib/image/Gravatar.js +12 -13
  55. package/core/server/lib/lexical.js +3 -1
  56. package/core/server/models/newsletter.js +6 -1
  57. package/core/server/services/api-version-compatibility/index.js +1 -33
  58. package/core/server/services/auth/session/emails/signin.js +3 -3
  59. package/core/server/services/email-address/EmailAddressParser.js +52 -0
  60. package/core/server/services/email-address/EmailAddressParser.js.d.ts +13 -0
  61. package/core/server/services/email-address/EmailAddressService.js +142 -0
  62. package/core/server/services/email-address/EmailAddressService.ts +183 -0
  63. package/core/server/services/email-address/EmailAddressServiceWrapper.js +2 -4
  64. package/core/server/services/email-analytics/EmailAnalyticsService.js +1 -1
  65. package/core/server/services/email-analytics/EmailAnalyticsServiceWrapper.js +2 -1
  66. package/core/server/services/email-service/BatchSendingService.js +703 -0
  67. package/core/server/services/email-service/EmailBodyCache.js +20 -0
  68. package/core/server/services/email-service/EmailController.js +94 -0
  69. package/core/server/services/email-service/EmailEventProcessor.js +267 -0
  70. package/core/server/services/email-service/EmailEventStorage.js +187 -0
  71. package/core/server/services/email-service/EmailRenderer.js +1263 -0
  72. package/core/server/services/email-service/EmailSegmenter.js +74 -0
  73. package/core/server/services/email-service/EmailService.js +310 -0
  74. package/core/server/services/email-service/EmailServiceWrapper.js +9 -2
  75. package/core/server/services/email-service/MailgunEmailProvider.js +191 -0
  76. package/core/server/services/email-service/SendingService.js +173 -0
  77. package/core/server/services/email-service/email-templates/partials/feedback-button.hbs +7 -0
  78. package/core/server/services/email-service/email-templates/partials/latest-posts.hbs +39 -0
  79. package/core/server/services/email-service/email-templates/partials/paywall.hbs +20 -0
  80. package/core/server/services/email-service/email-templates/partials/styles.hbs +2348 -0
  81. package/core/server/services/email-service/email-templates/template.hbs +238 -0
  82. package/core/server/services/email-service/events/EmailBouncedEvent.js +63 -0
  83. package/core/server/services/email-service/events/EmailDeliveredEvent.js +49 -0
  84. package/core/server/services/email-service/events/EmailOpenedEvent.js +49 -0
  85. package/core/server/services/email-service/events/EmailTemporaryBouncedEvent.js +63 -0
  86. package/core/server/services/email-service/events/EmailUnsubscribedEvent.js +42 -0
  87. package/core/server/services/email-service/events/SpamComplaintEvent.js +42 -0
  88. package/core/server/services/email-service/helpers/register-helpers.js +59 -0
  89. package/core/server/services/email-suppression-list/MailgunEmailSuppressionList.js +2 -1
  90. package/core/server/services/explore-ping/index.js +2 -1
  91. package/core/server/services/mail/GhostMailer.js +1 -1
  92. package/core/server/services/media-inliner/ExternalMediaInliner.js +2 -1
  93. package/core/server/services/members/api.js +15 -15
  94. package/core/server/services/members/emails/signin.js +4 -4
  95. package/core/server/services/members/emails/signup-paid.js +3 -4
  96. package/core/server/services/members/emails/signup.js +3 -3
  97. package/core/server/services/members/emails/subscribe.js +3 -3
  98. package/core/server/services/members/members-api/repositories/MemberRepository.js +92 -92
  99. package/core/server/services/members-events/LastSeenAtUpdater.js +1 -1
  100. package/core/server/services/settings-helpers/SettingsHelpers.js +1 -1
  101. package/core/server/services/staff/StaffServiceEmails.js +1 -1
  102. package/core/server/services/stats/PostsStatsService.js +28 -7
  103. package/core/server/web/api/app.js +0 -1
  104. package/core/server/web/api/endpoints/admin/app.js +0 -2
  105. package/core/server/web/api/endpoints/content/app.js +0 -2
  106. package/core/server/web/api/middleware/upload.js +2 -2
  107. package/core/shared/custom-theme-settings-cache/CustomThemeSettingsService.js +2 -1
  108. package/package.json +39 -97
  109. package/tsconfig.tsbuildinfo +1 -1
  110. package/yarn.lock +385 -517
  111. package/components/tryghost-api-framework-5.119.3.tgz +0 -0
  112. package/components/tryghost-custom-fonts-5.119.3.tgz +0 -0
  113. package/components/tryghost-domain-events-5.119.3.tgz +0 -0
  114. package/components/tryghost-email-addresses-5.119.3.tgz +0 -0
  115. package/components/tryghost-email-service-5.119.3.tgz +0 -0
  116. package/components/tryghost-html-to-plaintext-5.119.3.tgz +0 -0
  117. package/components/tryghost-i18n-5.119.3.tgz +0 -0
  118. package/components/tryghost-job-manager-5.119.3.tgz +0 -0
  119. package/components/tryghost-members-csv-5.119.3.tgz +0 -0
  120. package/components/tryghost-mw-error-handler-5.119.3.tgz +0 -0
  121. package/components/tryghost-mw-vhost-5.119.3.tgz +0 -0
  122. package/components/tryghost-prometheus-metrics-5.119.3.tgz +0 -0
  123. package/components/tryghost-security-5.119.3.tgz +0 -0
  124. package/core/built/admin/assets/chunk.524.c86e2e1b3e94d7cb1e4c.js +0 -35
  125. package/core/server/services/api-version-compatibility/APIVersionCompatibilityService.js +0 -99
  126. package/core/server/services/api-version-compatibility/VersionNotificationsDataService.js +0 -80
  127. package/core/server/services/api-version-compatibility/extract-api-key.js +0 -57
  128. package/core/server/services/api-version-compatibility/mw-api-version-mismatch.js +0 -31
  129. /package/core/built/admin/assets/{chunk.137.c9bf40f01afeeadb4660.js.LICENSE.txt → chunk.383.25fca2f09b4896656125.js.LICENSE.txt} +0 -0
@@ -39,13 +39,12 @@ const controller = {
39
39
  });
40
40
 
41
41
  if (notificationsToAdd.length){
42
- return await settingsBREADService.edit([{
42
+ await settingsBREADService.edit([{
43
43
  key: 'notifications',
44
44
  // @NOTE: We always need to store all notifications!
45
45
  value: allNotifications.concat(notificationsToAdd)
46
- }], internalContext).then(() => {
47
- return notificationsToAdd;
48
- });
46
+ }], internalContext);
47
+ return notificationsToAdd;
49
48
  }
50
49
  }
51
50
  },
@@ -86,21 +86,19 @@ const controller = {
86
86
  }
87
87
  },
88
88
  permissions: true,
89
- query(frame) {
89
+ async query(frame) {
90
90
  const options = {
91
91
  ...frame.options,
92
92
  mongoTransformer: rejectPrivateFieldsTransformer
93
93
  };
94
- return models.Post.findOne(frame.data, options)
95
- .then((model) => {
96
- if (!model) {
97
- throw new errors.NotFoundError({
98
- message: tpl(messages.pageNotFound)
99
- });
100
- }
101
-
102
- return model;
94
+ const model = await models.Post.findOne(frame.data, options);
95
+ if (!model) {
96
+ throw new errors.NotFoundError({
97
+ message: tpl(messages.pageNotFound)
103
98
  });
99
+ }
100
+
101
+ return model;
104
102
  }
105
103
  }
106
104
  };
@@ -81,17 +81,15 @@ const controller = {
81
81
  docName: 'posts',
82
82
  unsafeAttrs: UNSAFE_ATTRS
83
83
  },
84
- query(frame) {
85
- return models.Post.findOne(frame.data, frame.options)
86
- .then((model) => {
87
- if (!model) {
88
- throw new errors.NotFoundError({
89
- message: tpl(messages.pageNotFound)
90
- });
91
- }
92
-
93
- return model;
84
+ async query(frame) {
85
+ const model = await models.Post.findOne(frame.data, frame.options);
86
+ if (!model) {
87
+ throw new errors.NotFoundError({
88
+ message: tpl(messages.pageNotFound)
94
89
  });
90
+ }
91
+
92
+ return model;
95
93
  }
96
94
  },
97
95
 
@@ -119,15 +117,13 @@ const controller = {
119
117
  docName: 'posts',
120
118
  unsafeAttrs: UNSAFE_ATTRS
121
119
  },
122
- query(frame) {
123
- return models.Post.add(frame.data.pages[0], frame.options)
124
- .then((model) => {
125
- if (model.get('status') === 'published') {
126
- frame.setHeader('X-Cache-Invalidate', '/*');
127
- }
120
+ async query(frame) {
121
+ const model = await models.Post.add(frame.data.pages[0], frame.options);
122
+ if (model.get('status') === 'published') {
123
+ frame.setHeader('X-Cache-Invalidate', '/*');
124
+ }
128
125
 
129
- return model;
130
- });
126
+ return model;
131
127
  }
132
128
  },
133
129
 
@@ -162,21 +162,19 @@ const controller = {
162
162
  }
163
163
  },
164
164
  permissions: true,
165
- query(frame) {
165
+ async query(frame) {
166
166
  const options = {
167
167
  ...frame.options,
168
168
  mongoTransformer: rejectPrivateFieldsTransformer
169
169
  };
170
- return models.Post.findOne(frame.data, options)
171
- .then((model) => {
172
- if (!model) {
173
- throw new errors.NotFoundError({
174
- message: tpl(messages.postNotFound)
175
- });
176
- }
177
-
178
- return model;
170
+ const model = await models.Post.findOne(frame.data, options);
171
+ if (!model) {
172
+ throw new errors.NotFoundError({
173
+ message: tpl(messages.postNotFound)
179
174
  });
175
+ }
176
+
177
+ return model;
180
178
  }
181
179
  }
182
180
  };
@@ -171,15 +171,13 @@ const controller = {
171
171
  permissions: {
172
172
  unsafeAttrs: unsafeAttrs
173
173
  },
174
- query(frame) {
175
- return models.Post.add(frame.data.posts[0], frame.options)
176
- .then((model) => {
177
- if (model.get('status') === 'published') {
178
- frame.setHeader('X-Cache-Invalidate', '/*');
179
- }
174
+ async query(frame) {
175
+ const model = await models.Post.add(frame.data.posts[0], frame.options);
176
+ if (model.get('status') === 'published') {
177
+ frame.setHeader('X-Cache-Invalidate', '/*');
178
+ }
180
179
 
181
- return model;
182
- });
180
+ return model;
183
181
  }
184
182
  },
185
183
 
@@ -66,19 +66,17 @@ const controller = {
66
66
  }
67
67
  }
68
68
  },
69
- query(frame) {
69
+ async query(frame) {
70
70
  _addMemberContextToFrame(frame);
71
71
 
72
- return models.Post.findOne(Object.assign({status: 'all'}, frame.data), frame.options)
73
- .then((model) => {
74
- if (!model) {
75
- throw new errors.NotFoundError({
76
- message: tpl(messages.postNotFound)
77
- });
78
- }
79
-
80
- return model;
72
+ const model = await models.Post.findOne(Object.assign({status: 'all'}, frame.data), frame.options);
73
+ if (!model) {
74
+ throw new errors.NotFoundError({
75
+ message: tpl(messages.postNotFound)
81
76
  });
77
+ }
78
+
79
+ return model;
82
80
  }
83
81
  }
84
82
  };
@@ -10,14 +10,13 @@ const controller = {
10
10
  headers: {
11
11
  disposition: {
12
12
  type: 'file',
13
- value() {
14
- return customRedirects.api.getRedirectsFilePath()
15
- .then((filePath) => {
16
- // @deprecated: .json was deprecated in v4.0 but is still the default for backwards compat
17
- return filePath === null || path.extname(filePath) === '.json'
18
- ? 'redirects.json'
19
- : 'redirects.yaml';
20
- });
13
+ async value() {
14
+ const filePath = await customRedirects.api.getRedirectsFilePath();
15
+
16
+ // @deprecated: .json was deprecated in v4.0 but is still the default for backwards compat
17
+ return filePath === null || path.extname(filePath) === '.json'
18
+ ? 'redirects.json'
19
+ : 'redirects.yaml';
21
20
  }
22
21
  },
23
22
  cacheInvalidate: false
@@ -69,19 +69,17 @@ const controller = {
69
69
  }
70
70
  }
71
71
  },
72
- query(frame) {
72
+ async query(frame) {
73
73
  const resourceModel = 'Post';
74
74
  const resourceType = (frame.options.resource === 'post') ? 'post' : 'page';
75
75
  const cleanOptions = {};
76
76
  cleanOptions.filter = `status:scheduled+type:${resourceType}`;
77
77
  cleanOptions.columns = ['id', 'published_at', 'created_at', 'type'];
78
78
 
79
- return models[resourceModel].findAll(cleanOptions)
80
- .then((result) => {
81
- let response = {};
82
- response[resourceType] = result;
83
- return response;
84
- });
79
+ const result = await models[resourceModel].findAll(cleanOptions);
80
+ let response = {};
81
+ response[resourceType] = result;
82
+ return response;
85
83
  }
86
84
  }
87
85
  };
@@ -44,16 +44,14 @@ const controller = {
44
44
  }
45
45
  }
46
46
  },
47
- query(frame) {
48
- return models.Base.Model.generateSlug(allowedTypes[frame.options.type], frame.data.name, {status: 'all', modelId: frame.options.id})
49
- .then((slug) => {
50
- if (!slug) {
51
- return Promise.reject(new errors.InternalServerError({
52
- message: tpl(messages.couldNotGenerateSlug)
53
- }));
54
- }
55
- return slug;
47
+ async query(frame) {
48
+ const slug = await models.Base.Model.generateSlug(allowedTypes[frame.options.type], frame.data.name, {status: 'all', modelId: frame.options.id});
49
+ if (!slug) {
50
+ throw new errors.InternalServerError({
51
+ message: tpl(messages.couldNotGenerateSlug)
56
52
  });
53
+ }
54
+ return slug;
57
55
  }
58
56
  }
59
57
  };
@@ -46,17 +46,15 @@ const controller = {
46
46
  'id'
47
47
  ],
48
48
  permissions: true,
49
- query(frame) {
50
- return models.Snippet.findOne(frame.data, frame.options)
51
- .then((model) => {
52
- if (!model) {
53
- return Promise.reject(new errors.NotFoundError({
54
- message: tpl(messages.snippetNotFound)
55
- }));
56
- }
57
-
58
- return model;
49
+ async query(frame) {
50
+ const model = await models.Snippet.findOne(frame.data, frame.options);
51
+ if (!model) {
52
+ throw new errors.NotFoundError({
53
+ message: tpl(messages.snippetNotFound)
59
54
  });
55
+ }
56
+
57
+ return model;
60
58
  }
61
59
  },
62
60
 
@@ -97,17 +95,15 @@ const controller = {
97
95
  }
98
96
  },
99
97
  permissions: true,
100
- query(frame) {
101
- return models.Snippet.edit(frame.data.snippets[0], frame.options)
102
- .then((model) => {
103
- if (!model) {
104
- return Promise.reject(new errors.NotFoundError({
105
- message: tpl(messages.snippetNotFound)
106
- }));
107
- }
108
-
109
- return model;
98
+ async query(frame) {
99
+ const model = await models.Snippet.edit(frame.data.snippets[0], frame.options);
100
+ if (!model) {
101
+ throw new errors.NotFoundError({
102
+ message: tpl(messages.snippetNotFound)
110
103
  });
104
+ }
105
+
106
+ return model;
111
107
  }
112
108
  },
113
109
 
@@ -63,17 +63,15 @@ const controller = {
63
63
  }
64
64
  },
65
65
  permissions: true,
66
- query(frame) {
67
- return models.TagPublic.findOne(frame.data, frame.options)
68
- .then((model) => {
69
- if (!model) {
70
- return Promise.reject(new errors.NotFoundError({
71
- message: tpl(messages.tagNotFound)
72
- }));
73
- }
74
-
75
- return model;
66
+ async query(frame) {
67
+ const model = await models.TagPublic.findOne(frame.data, frame.options);
68
+ if (!model) {
69
+ throw new errors.NotFoundError({
70
+ message: tpl(messages.tagNotFound)
76
71
  });
72
+ }
73
+
74
+ return model;
77
75
  }
78
76
  }
79
77
  };
@@ -61,17 +61,15 @@ const controller = {
61
61
  }
62
62
  },
63
63
  permissions: true,
64
- query(frame) {
65
- return models.Tag.findOne(frame.data, frame.options)
66
- .then((model) => {
67
- if (!model) {
68
- return Promise.reject(new errors.NotFoundError({
69
- message: tpl(messages.tagNotFound)
70
- }));
71
- }
72
-
73
- return model;
64
+ async query(frame) {
65
+ const model = await models.Tag.findOne(frame.data, frame.options);
66
+ if (!model) {
67
+ throw new errors.NotFoundError({
68
+ message: tpl(messages.tagNotFound)
74
69
  });
70
+ }
71
+
72
+ return model;
75
73
  }
76
74
  },
77
75
 
@@ -115,21 +113,19 @@ const controller = {
115
113
  }
116
114
  },
117
115
  permissions: true,
118
- query(frame) {
119
- return models.Tag.edit(frame.data.tags[0], frame.options)
120
- .then((model) => {
121
- if (!model) {
122
- return Promise.reject(new errors.NotFoundError({
123
- message: tpl(messages.tagNotFound)
124
- }));
125
- }
116
+ async query(frame) {
117
+ const model = await models.Tag.edit(frame.data.tags[0], frame.options);
118
+ if (!model) {
119
+ throw new errors.NotFoundError({
120
+ message: tpl(messages.tagNotFound)
121
+ });
122
+ }
126
123
 
127
- if (model.wasChanged()) {
128
- frame.setHeader('X-Cache-Invalidate', '/*');
129
- }
124
+ if (model.wasChanged()) {
125
+ frame.setHeader('X-Cache-Invalidate', '/*');
126
+ }
130
127
 
131
- return model;
132
- });
128
+ return model;
133
129
  }
134
130
  },
135
131
 
@@ -123,14 +123,12 @@ const controller = {
123
123
  name: frame.file.originalname
124
124
  };
125
125
 
126
- return themeService.api.setFromZip(zip)
127
- .then(({theme, themeOverridden}) => {
128
- if (themeOverridden) {
129
- frame.setHeader('X-Cache-Invalidate', '/*');
130
- }
131
- events.emit('theme.uploaded', {name: theme.name});
132
- return theme;
133
- });
126
+ const {theme, themeOverridden} = await themeService.api.setFromZip(zip);
127
+ if (themeOverridden) {
128
+ frame.setHeader('X-Cache-Invalidate', '/*');
129
+ }
130
+ events.emit('theme.uploaded', {name: theme.name});
131
+ return theme;
134
132
  }
135
133
  },
136
134
 
@@ -134,17 +134,15 @@ const controller = {
134
134
  }
135
135
  },
136
136
  permissions: true,
137
- query(frame) {
138
- return models.User.findOne(frame.data, frame.options)
139
- .then((model) => {
140
- if (!model) {
141
- return Promise.reject(new errors.NotFoundError({
142
- message: tpl(messages.userNotFound)
143
- }));
144
- }
145
-
146
- return model;
137
+ async query(frame) {
138
+ const model = await models.User.findOne(frame.data, frame.options);
139
+ if (!model) {
140
+ throw new errors.NotFoundError({
141
+ message: tpl(messages.userNotFound)
147
142
  });
143
+ }
144
+
145
+ return model;
148
146
  }
149
147
  },
150
148
 
@@ -169,21 +167,19 @@ const controller = {
169
167
  permissions: {
170
168
  unsafeAttrs: UNSAFE_ATTRS
171
169
  },
172
- query(frame) {
173
- return models.User.edit(frame.data.users[0], frame.options)
174
- .then((model) => {
175
- if (!model) {
176
- return Promise.reject(new errors.NotFoundError({
177
- message: tpl(messages.userNotFound)
178
- }));
179
- }
170
+ async query(frame) {
171
+ const model = await models.User.edit(frame.data.users[0], frame.options);
172
+ if (!model) {
173
+ throw new errors.NotFoundError({
174
+ message: tpl(messages.userNotFound)
175
+ });
176
+ }
180
177
 
181
- if (shouldInvalidateCacheAfterChange(model)) {
182
- frame.setHeader('X-Cache-Invalidate', '/*');
183
- }
178
+ if (shouldInvalidateCacheAfterChange(model)) {
179
+ frame.setHeader('X-Cache-Invalidate', '/*');
180
+ }
184
181
 
185
- return model;
186
- });
182
+ return model;
187
183
  }
188
184
  },
189
185
 
@@ -203,11 +199,13 @@ const controller = {
203
199
  },
204
200
  permissions: true,
205
201
  async query(frame) {
206
- return userService.destroyUser(frame.options).catch((err) => {
207
- return Promise.reject(new errors.NoPermissionError({
202
+ try {
203
+ return userService.destroyUser(frame.options);
204
+ } catch (err) {
205
+ throw new errors.NoPermissionError({
208
206
  err: err
209
- }));
210
- });
207
+ });
208
+ }
211
209
  }
212
210
  },
213
211
 
@@ -240,11 +238,9 @@ const controller = {
240
238
  headers: {
241
239
  cacheInvalidate: false
242
240
  },
243
- permissions(frame) {
244
- return models.Role.findOne({name: 'Owner'})
245
- .then((ownerRole) => {
246
- return permissionsService.canThis(frame.options.context).assign.role(ownerRole);
247
- });
241
+ async permissions(frame) {
242
+ const ownerRole = await models.Role.findOne({name: 'Owner'});
243
+ return permissionsService.canThis(frame.options.context).assign.role(ownerRole);
248
244
  },
249
245
  query(frame) {
250
246
  return models.User.transferOwnership(frame.data.owner[0], frame.options);
@@ -287,11 +283,10 @@ const controller = {
287
283
  }
288
284
  },
289
285
  permissions: permissionOnlySelf,
290
- query(frame) {
286
+ async query(frame) {
291
287
  const targetId = getTargetId(frame);
292
- return fetchOrCreatePersonalToken(targetId).then((model) => {
293
- return models.ApiKey.refreshSecret(model.toJSON(), Object.assign({}, {id: model.id}));
294
- });
288
+ const model = await fetchOrCreatePersonalToken(targetId);
289
+ return models.ApiKey.refreshSecret(model.toJSON(), Object.assign({}, {id: model.id}));
295
290
  }
296
291
  }
297
292
  };
@@ -15,7 +15,7 @@ const messages = {
15
15
  * @param {import('@tryghost/api-framework').Frame} frame
16
16
  * @return {Promise}
17
17
  */
18
- const nonePublicAuth = (apiConfig, frame) => {
18
+ const nonePublicAuth = async (apiConfig, frame) => {
19
19
  debug('check admin permissions');
20
20
 
21
21
  let singular;
@@ -40,9 +40,9 @@ const nonePublicAuth = (apiConfig, frame) => {
40
40
  unsafeAttrObject = apiConfig.unsafeAttrsObject(frame);
41
41
  }
42
42
 
43
- const permsPromise = permissions.canThis(frame.options.context)[apiConfig.method][singular](permissionIdentifier, unsafeAttrObject);
43
+ try {
44
+ const result = await permissions.canThis(frame.options.context)[apiConfig.method][singular](permissionIdentifier, unsafeAttrObject);
44
45
 
45
- return permsPromise.then((result) => {
46
46
  /*
47
47
  * Allow the permissions function to return a list of excluded attributes.
48
48
  * If it does, omit those attrs from the data passed through
@@ -56,23 +56,23 @@ const nonePublicAuth = (apiConfig, frame) => {
56
56
  if (result && result.excludedAttrs && _.has(frame, `data.[${apiConfig.docName}][0]`)) {
57
57
  frame.data[apiConfig.docName][0] = _.omit(frame.data[apiConfig.docName][0], result.excludedAttrs);
58
58
  }
59
- }).catch((err) => {
59
+ } catch (err) {
60
60
  if (err instanceof errors.NoPermissionError) {
61
61
  err.message = tpl(messages.noPermissionToCall, {
62
62
  method: apiConfig.method,
63
63
  docName: apiConfig.docName
64
64
  });
65
- return Promise.reject(err);
65
+ throw err;
66
66
  }
67
67
 
68
68
  if (errors.utils.isGhostError(err)) {
69
- return Promise.reject(err);
69
+ throw err;
70
70
  }
71
71
 
72
- return Promise.reject(new errors.InternalServerError({
73
- err: err
74
- }));
75
- });
72
+ throw new errors.InternalServerError({
73
+ err
74
+ });
75
+ }
76
76
  };
77
77
 
78
78
  // @TODO: https://github.com/TryGhost/Ghost/issues/10735
@@ -2,7 +2,7 @@ const debug = require('@tryghost/debug')('api:endpoints:utils:serializers:output
2
2
  const canThis = require('../../../../../services/permissions').canThis;
3
3
 
4
4
  module.exports = {
5
- browse(models, apiConfig, frame) {
5
+ async browse(models, apiConfig, frame) {
6
6
  debug('browse');
7
7
 
8
8
  const roles = models.toJSON(frame.options);
@@ -12,7 +12,7 @@ module.exports = {
12
12
  roles: roles
13
13
  };
14
14
  } else {
15
- return Promise.all(
15
+ const results = await Promise.all(
16
16
  roles.map(async (role) => {
17
17
  let permissionResult;
18
18
  try {
@@ -21,14 +21,13 @@ module.exports = {
21
21
  } catch (err) {
22
22
  permissionResult = {};
23
23
  }
24
- return permissionResult && permissionResult.name && (permissionResult.name !== 'Owner');
25
- }))
26
- .then(results => roles.filter((_v, index) => results[index]))
27
- .then((filteredRoles) => {
28
- return frame.response = {
29
- roles: filteredRoles
30
- };
31
- });
24
+ return permissionResult && permissionResult.name && (permissionResult.name !== 'Owner');
25
+ })
26
+ );
27
+ const filteredRoles = roles.filter((_v, index) => results[index]);
28
+ return frame.response = {
29
+ roles: filteredRoles
30
+ };
32
31
  }
33
32
  }
34
33
  };