ghost 5.119.2 → 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.
- package/components/tryghost-i18n-5.120.0.tgz +0 -0
- package/core/boot.js +0 -2
- package/core/built/admin/assets/admin-x-activitypub/admin-x-activitypub.js +7555 -7216
- package/core/built/admin/assets/admin-x-settings/{CodeEditorView-60ce658c.mjs → CodeEditorView-1c5b0683.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-8480baa8.mjs → index-14e518a7.mjs} +3 -3
- package/core/built/admin/assets/admin-x-settings/{index-a2648c61.mjs → index-fc9f985b.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{modals-6900c1d5.mjs → modals-15bc6a0f.mjs} +7192 -6656
- package/core/built/admin/assets/{chunk.137.c9bf40f01afeeadb4660.js → chunk.383.25fca2f09b4896656125.js} +76 -59
- package/core/built/admin/assets/chunk.524.1657b12c0ab25dd9fb79.js +28 -0
- package/core/built/admin/assets/{chunk.582.98a820cbc4bb65f2e685.js → chunk.582.09869b1f1a3cc0ab81f6.js} +19 -26
- package/core/built/admin/assets/{ghost-843572e9507d099162ae744d791daba1.js → ghost-b3b44421acca3b3eec76bfbb6ba0e81b.js} +3 -3
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +12578 -12352
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +423 -211
- package/core/built/admin/assets/posts/posts.js +13680 -13671
- package/core/built/admin/assets/stats/stats.js +16457 -16635
- package/core/built/admin/assets/{vendor-8f805740fee4db959a5b2119001a56b1.js → vendor-4ce6d282a2a00fe486a0951e0591da19.js} +11 -9
- package/core/built/admin/index.html +5 -5
- package/core/frontend/helpers/match.js +6 -0
- package/core/frontend/services/routing/ParentRouter.js +1 -1
- package/core/frontend/services/routing/controllers/email-post.js +0 -2
- package/core/frontend/services/routing/controllers/previews.js +0 -3
- package/core/frontend/web/middleware/frontend-caching.js +2 -2
- package/core/server/api/endpoints/authentication.js +37 -73
- package/core/server/api/endpoints/authors-public.js +8 -9
- package/core/server/api/endpoints/db.js +34 -35
- package/core/server/api/endpoints/emails.js +8 -10
- package/core/server/api/endpoints/integrations.js +20 -18
- package/core/server/api/endpoints/invites.js +8 -10
- package/core/server/api/endpoints/labels.js +19 -23
- package/core/server/api/endpoints/notifications.js +3 -4
- package/core/server/api/endpoints/pages-public.js +8 -10
- package/core/server/api/endpoints/pages.js +14 -18
- package/core/server/api/endpoints/posts-public.js +8 -10
- package/core/server/api/endpoints/posts.js +6 -8
- package/core/server/api/endpoints/previews.js +8 -10
- package/core/server/api/endpoints/redirects.js +7 -8
- package/core/server/api/endpoints/schedules.js +5 -7
- package/core/server/api/endpoints/slugs.js +7 -9
- package/core/server/api/endpoints/snippets.js +16 -20
- package/core/server/api/endpoints/tags-public.js +8 -10
- package/core/server/api/endpoints/tags.js +19 -23
- package/core/server/api/endpoints/themes.js +6 -8
- package/core/server/api/endpoints/users.js +31 -36
- package/core/server/api/endpoints/utils/permissions.js +10 -10
- package/core/server/api/endpoints/utils/serializers/output/roles.js +9 -10
- package/core/server/api/endpoints/utils/validators/input/images.js +43 -52
- package/core/server/api/endpoints/utils/validators/input/invites.js +6 -8
- package/core/server/api/endpoints/webhooks.js +38 -42
- package/core/server/data/migrations/versions/5.120/2025-05-07-14-57-38-add-newsletters-button-corners-column.js +8 -0
- package/core/server/data/migrations/versions/5.120/2025-05-13-17-36-56-add-newsletters-button-style-column.js +8 -0
- package/core/server/data/migrations/versions/5.120/2025-05-14-20-00-15-add-newsletters-setting-columns.js +22 -0
- package/core/server/data/schema/schema.js +6 -1
- package/core/server/lib/image/Gravatar.js +12 -13
- package/core/server/lib/lexical.js +3 -1
- package/core/server/models/newsletter.js +6 -1
- package/core/server/services/api-version-compatibility/index.js +1 -33
- package/core/server/services/auth/session/emails/signin.js +3 -3
- package/core/server/services/email-address/EmailAddressParser.js +52 -0
- package/core/server/services/email-address/EmailAddressParser.js.d.ts +13 -0
- package/core/server/services/email-address/EmailAddressService.js +142 -0
- package/core/server/services/email-address/EmailAddressService.ts +183 -0
- package/core/server/services/email-address/EmailAddressServiceWrapper.js +2 -4
- package/core/server/services/email-analytics/EmailAnalyticsService.js +1 -1
- package/core/server/services/email-analytics/EmailAnalyticsServiceWrapper.js +2 -1
- package/core/server/services/email-service/BatchSendingService.js +703 -0
- package/core/server/services/email-service/EmailBodyCache.js +20 -0
- package/core/server/services/email-service/EmailController.js +94 -0
- package/core/server/services/email-service/EmailEventProcessor.js +267 -0
- package/core/server/services/email-service/EmailEventStorage.js +187 -0
- package/core/server/services/email-service/EmailRenderer.js +1263 -0
- package/core/server/services/email-service/EmailSegmenter.js +74 -0
- package/core/server/services/email-service/EmailService.js +310 -0
- package/core/server/services/email-service/EmailServiceWrapper.js +9 -2
- package/core/server/services/email-service/MailgunEmailProvider.js +191 -0
- package/core/server/services/email-service/SendingService.js +173 -0
- package/core/server/services/email-service/email-templates/partials/feedback-button.hbs +7 -0
- package/core/server/services/email-service/email-templates/partials/latest-posts.hbs +39 -0
- package/core/server/services/email-service/email-templates/partials/paywall.hbs +20 -0
- package/core/server/services/email-service/email-templates/partials/styles.hbs +2348 -0
- package/core/server/services/email-service/email-templates/template.hbs +238 -0
- package/core/server/services/email-service/events/EmailBouncedEvent.js +63 -0
- package/core/server/services/email-service/events/EmailDeliveredEvent.js +49 -0
- package/core/server/services/email-service/events/EmailOpenedEvent.js +49 -0
- package/core/server/services/email-service/events/EmailTemporaryBouncedEvent.js +63 -0
- package/core/server/services/email-service/events/EmailUnsubscribedEvent.js +42 -0
- package/core/server/services/email-service/events/SpamComplaintEvent.js +42 -0
- package/core/server/services/email-service/helpers/register-helpers.js +59 -0
- package/core/server/services/email-suppression-list/MailgunEmailSuppressionList.js +2 -1
- package/core/server/services/explore-ping/index.js +2 -1
- package/core/server/services/mail/GhostMailer.js +1 -1
- package/core/server/services/media-inliner/ExternalMediaInliner.js +2 -1
- package/core/server/services/members/api.js +15 -15
- package/core/server/services/members/emails/signin.js +4 -4
- package/core/server/services/members/emails/signup-paid.js +3 -4
- package/core/server/services/members/emails/signup.js +3 -3
- package/core/server/services/members/emails/subscribe.js +3 -3
- package/core/server/services/members/members-api/controllers/RouterController.js +50 -36
- package/core/server/services/members/members-api/repositories/MemberRepository.js +92 -92
- package/core/server/services/members-events/LastSeenAtUpdater.js +1 -1
- package/core/server/services/settings-helpers/SettingsHelpers.js +1 -1
- package/core/server/services/staff/StaffServiceEmails.js +1 -1
- package/core/server/services/stats/PostsStatsService.js +28 -7
- package/core/server/web/api/app.js +0 -1
- package/core/server/web/api/endpoints/admin/app.js +0 -2
- package/core/server/web/api/endpoints/content/app.js +0 -2
- package/core/server/web/api/middleware/upload.js +2 -2
- package/core/shared/custom-theme-settings-cache/CustomThemeSettingsService.js +2 -1
- package/package.json +39 -97
- package/tsconfig.tsbuildinfo +1 -1
- package/yarn.lock +385 -517
- package/components/tryghost-api-framework-5.119.2.tgz +0 -0
- package/components/tryghost-custom-fonts-5.119.2.tgz +0 -0
- package/components/tryghost-domain-events-5.119.2.tgz +0 -0
- package/components/tryghost-email-addresses-5.119.2.tgz +0 -0
- package/components/tryghost-email-service-5.119.2.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.119.2.tgz +0 -0
- package/components/tryghost-i18n-5.119.2.tgz +0 -0
- package/components/tryghost-job-manager-5.119.2.tgz +0 -0
- package/components/tryghost-members-csv-5.119.2.tgz +0 -0
- package/components/tryghost-mw-error-handler-5.119.2.tgz +0 -0
- package/components/tryghost-mw-vhost-5.119.2.tgz +0 -0
- package/components/tryghost-prometheus-metrics-5.119.2.tgz +0 -0
- package/components/tryghost-security-5.119.2.tgz +0 -0
- package/core/built/admin/assets/chunk.524.b8545af3bb714bc4f820.js +0 -35
- package/core/server/services/api-version-compatibility/APIVersionCompatibilityService.js +0 -99
- package/core/server/services/api-version-compatibility/VersionNotificationsDataService.js +0 -80
- package/core/server/services/api-version-compatibility/extract-api-key.js +0 -57
- package/core/server/services/api-version-compatibility/mw-api-version-mismatch.js +0 -31
- /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
|
-
|
|
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)
|
|
47
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
124
|
+
if (model.wasChanged()) {
|
|
125
|
+
frame.setHeader('X-Cache-Invalidate', '/*');
|
|
126
|
+
}
|
|
130
127
|
|
|
131
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
|
|
182
|
-
|
|
183
|
-
|
|
178
|
+
if (shouldInvalidateCacheAfterChange(model)) {
|
|
179
|
+
frame.setHeader('X-Cache-Invalidate', '/*');
|
|
180
|
+
}
|
|
184
181
|
|
|
185
|
-
|
|
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
|
-
|
|
207
|
-
return
|
|
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
|
-
|
|
245
|
-
|
|
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
|
-
|
|
293
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
65
|
+
throw err;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
if (errors.utils.isGhostError(err)) {
|
|
69
|
-
|
|
69
|
+
throw err;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
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
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
};
|