ghost 4.13.0 → 4.16.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/content/themes/casper/assets/built/screen.css +1 -1
- package/content/themes/casper/assets/built/screen.css.map +1 -1
- package/content/themes/casper/assets/css/screen.css +1 -1
- package/content/themes/casper/default.hbs +2 -2
- package/content/themes/casper/package.json +1 -1
- package/content/themes/casper/page.hbs +28 -26
- package/content/themes/casper/partials/post-card.hbs +2 -2
- package/content/themes/casper/post.hbs +67 -65
- package/content/themes/casper/tag.hbs +2 -2
- package/core/built/assets/{chunk.3.f80c7fbb7573ce508a05.js → chunk.3.4b1d9e20e57164ac9c29.js} +31 -29
- package/core/built/assets/ghost-dark-bb2831fc27fcb02893ed0a761207dc63.css +1 -0
- package/core/built/assets/{ghost.min-ba7f03a78d7d98444af386b8ae9347a7.js → ghost.min-d1d99f3ed6e0f427874b2a11e7078475.js} +1777 -1685
- package/core/built/assets/ghost.min-e7612edfa72b0fe2c201b387923e6fc7.css +1 -0
- package/core/built/assets/icons/check-2.svg +1 -0
- package/core/built/assets/icons/discount-bubble.svg +1 -0
- package/core/built/assets/icons/no-data-line-chart.svg +1 -0
- package/core/built/assets/icons/no-data-list.svg +9 -8
- package/core/built/assets/icons/no-data-subscription.svg +1 -0
- package/core/built/assets/{vendor.min-29784d514390cb5abc74ae660cb2fbc7.js → vendor.min-3660ec7864887f1496fe7a27fd23ab76.js} +1570 -1289
- package/core/frontend/helpers/ghost_head.js +7 -1
- package/core/frontend/helpers/match.js +19 -4
- package/core/frontend/helpers/products.js +68 -0
- package/core/frontend/helpers/tpl/content-cta.hbs +1 -1
- package/core/frontend/services/routing/controllers/email-post.js +3 -2
- package/core/frontend/services/settings/loader.js +2 -2
- package/core/frontend/services/sitemap/base-generator.js +12 -8
- package/core/frontend/services/sitemap/manager.js +1 -1
- package/core/frontend/services/theme-engine/handlebars/helpers.js +1 -0
- package/core/frontend/services/theme-engine/middleware.js +4 -1
- package/core/server/api/canary/email-preview.js +15 -33
- package/core/server/api/canary/integrations.js +7 -30
- package/core/server/api/canary/labels.js +8 -9
- package/core/server/api/canary/members.js +13 -9
- package/core/server/api/canary/schedules.js +9 -57
- package/core/server/api/canary/settings.js +20 -158
- package/core/server/api/canary/themes.js +5 -59
- package/core/server/api/canary/utils/serializers/output/members.js +2 -14
- package/core/server/api/canary/utils/validators/input/settings.js +23 -1
- package/core/server/api/canary/webhooks.js +6 -24
- package/core/server/api/v2/schedules.js +9 -57
- package/core/server/api/v3/email-preview.js +15 -28
- package/core/server/api/v3/integrations.js +7 -30
- package/core/server/api/v3/labels.js +8 -9
- package/core/server/api/v3/members.js +4 -1
- package/core/server/api/v3/schedules.js +9 -57
- package/core/server/api/v3/settings.js +13 -132
- package/core/server/api/v3/utils/validators/input/settings.js +23 -1
- package/core/server/api/v3/webhooks.js +6 -28
- package/core/server/data/exporter/table-lists.js +1 -0
- package/core/server/data/importer/import-manager.js +398 -0
- package/core/server/data/importer/importers/data/data-importer.js +162 -0
- package/core/server/data/importer/importers/data/index.js +1 -162
- package/core/server/data/importer/index.js +1 -379
- package/core/server/data/migrations/versions/4.14/01-fix-comped-member-statuses.js +70 -0
- package/core/server/data/migrations/versions/4.14/02-fix-free-members-status-events.js +60 -0
- package/core/server/data/migrations/versions/4.15/01-add-temp-members-analytic-events-table.js +12 -0
- package/core/server/data/migrations/versions/4.16/01-add-custom-theme-settings-table.js +9 -0
- package/core/server/data/schema/fixtures/utils.js +6 -1
- package/core/server/data/schema/schema.js +26 -0
- package/core/server/lib/request-external.js +3 -2
- package/core/server/models/action.js +1 -1
- package/core/server/models/api-key.js +1 -1
- package/core/server/models/base/bookshelf.js +0 -3
- package/core/server/models/base/index.js +2 -0
- package/core/server/models/base/plugins/events.js +2 -2
- package/core/server/models/base/plugins/raw-knex.js +10 -10
- package/core/server/models/custom-theme-setting.js +9 -0
- package/core/server/models/email.js +2 -2
- package/core/server/models/index.js +2 -0
- package/core/server/models/integration.js +1 -1
- package/core/server/models/label.js +2 -2
- package/core/server/models/member-analytic-event.js +9 -0
- package/core/server/models/member.js +2 -2
- package/core/server/models/post.js +2 -2
- package/core/server/models/settings.js +2 -2
- package/core/server/models/tag.js +2 -2
- package/core/server/models/user.js +2 -2
- package/core/server/models/webhook.js +2 -2
- package/core/server/services/bulk-email/bulk-email-processor.js +1 -4
- package/core/server/services/custom-theme-settings.js +8 -0
- package/core/server/services/integrations/integrations-service.js +61 -0
- package/core/server/services/mail/GhostMailer.js +29 -37
- package/core/server/services/mega/email-preview.js +41 -0
- package/core/server/services/mega/index.js +4 -0
- package/core/server/services/mega/mega.js +27 -23
- package/core/server/services/mega/post-email-serializer.js +28 -21
- package/core/server/services/mega/template.js +11 -0
- package/core/server/services/members/api.js +1 -0
- package/core/server/services/members/emails/signup.js +1 -1
- package/core/server/services/oembed.js +7 -2
- package/core/server/services/posts/post-scheduling-service.js +100 -0
- package/core/server/services/settings/index.js +13 -16
- package/core/server/services/settings/settings-bread-service.js +188 -0
- package/core/server/services/settings/settings-utils.js +32 -0
- package/core/server/services/themes/ThemeStorage.js +5 -4
- package/core/server/services/themes/activation-bridge.js +14 -0
- package/core/server/services/themes/index.js +2 -0
- package/core/server/services/themes/installer.js +72 -0
- package/core/server/services/themes/validate.js +5 -2
- package/core/server/services/webhooks/webhooks-service.js +55 -0
- package/core/server/web/admin/views/default-prod.html +4 -4
- package/core/server/web/admin/views/default.html +4 -4
- package/core/server/web/members/app.js +3 -0
- package/core/shared/config/defaults.json +2 -2
- package/core/shared/custom-theme-settings-cache.js +3 -0
- package/core/shared/i18n/translations/en.json +1 -6
- package/core/shared/labs.js +5 -7
- package/package.json +64 -62
- package/yarn.lock +1490 -1055
- package/core/built/assets/ghost-dark-98d56e4973a502750748090f9dbc8280.css +0 -1
- package/core/built/assets/ghost.min-6932a664a1cb92a8e4a15f540cae3ad8.css +0 -1
- package/core/server/services/mega/template-labs.js +0 -1024
|
@@ -125,7 +125,10 @@ module.exports = {
|
|
|
125
125
|
member = await membersService.api.members.create(frame.data.members[0], frame.options);
|
|
126
126
|
|
|
127
127
|
if (frame.data.members[0].stripe_customer_id) {
|
|
128
|
-
await membersService.api.members.linkStripeCustomer(
|
|
128
|
+
await membersService.api.members.linkStripeCustomer({
|
|
129
|
+
customer_id: frame.data.members[0].stripe_customer_id,
|
|
130
|
+
member_id: member.id
|
|
131
|
+
});
|
|
129
132
|
}
|
|
130
133
|
|
|
131
134
|
if (frame.data.members[0].comped) {
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
const _ = require('lodash');
|
|
2
|
-
const moment = require('moment');
|
|
3
|
-
const config = require('../../../shared/config');
|
|
4
1
|
const models = require('../../models');
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const errors = require('@tryghost/errors');
|
|
8
|
-
const api = require('./index');
|
|
2
|
+
|
|
3
|
+
const postSchedulingService = require('../../services/posts/post-scheduling-service')('v3');
|
|
9
4
|
|
|
10
5
|
module.exports = {
|
|
11
6
|
docName: 'schedules',
|
|
@@ -32,11 +27,8 @@ module.exports = {
|
|
|
32
27
|
permissions: {
|
|
33
28
|
docName: 'posts'
|
|
34
29
|
},
|
|
35
|
-
query(frame) {
|
|
36
|
-
let resource;
|
|
30
|
+
async query(frame) {
|
|
37
31
|
const resourceType = frame.options.resource;
|
|
38
|
-
const publishAPostBySchedulerToleranceInMinutes = config.get('times').publishAPostBySchedulerToleranceInMinutes;
|
|
39
|
-
|
|
40
32
|
const options = {
|
|
41
33
|
status: 'scheduled',
|
|
42
34
|
id: frame.options.id,
|
|
@@ -45,53 +37,13 @@ module.exports = {
|
|
|
45
37
|
}
|
|
46
38
|
};
|
|
47
39
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const publishedAtMoment = moment(resource.published_at);
|
|
52
|
-
|
|
53
|
-
if (publishedAtMoment.diff(moment(), 'minutes') > publishAPostBySchedulerToleranceInMinutes) {
|
|
54
|
-
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.api.job.notFound')}));
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (publishedAtMoment.diff(moment(), 'minutes') < publishAPostBySchedulerToleranceInMinutes * -1 && frame.data.force !== true) {
|
|
58
|
-
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.api.job.publishInThePast')}));
|
|
59
|
-
}
|
|
40
|
+
const {scheduledResource, preScheduledResource} = await postSchedulingService.publish(resourceType, frame.options.id, frame.data.force, options);
|
|
41
|
+
const cacheInvalidate = postSchedulingService.handleCacheInvalidation(scheduledResource, preScheduledResource);
|
|
42
|
+
this.headers.cacheInvalidate = cacheInvalidate;
|
|
60
43
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
updated_at: moment(resource.updated_at).toISOString(true)
|
|
65
|
-
}];
|
|
66
|
-
|
|
67
|
-
return api[resourceType].edit(
|
|
68
|
-
editedResource,
|
|
69
|
-
_.pick(options, ['context', 'id', 'transacting', 'forUpdate'])
|
|
70
|
-
);
|
|
71
|
-
})
|
|
72
|
-
.then((result) => {
|
|
73
|
-
const scheduledResource = result[resourceType][0];
|
|
74
|
-
|
|
75
|
-
if (
|
|
76
|
-
(scheduledResource.status === 'published' && resource.status !== 'published') ||
|
|
77
|
-
(scheduledResource.status === 'draft' && resource.status === 'published')
|
|
78
|
-
) {
|
|
79
|
-
this.headers.cacheInvalidate = true;
|
|
80
|
-
} else if (
|
|
81
|
-
(scheduledResource.status === 'draft' && resource.status !== 'published') ||
|
|
82
|
-
(scheduledResource.status === 'scheduled' && resource.status !== 'scheduled')
|
|
83
|
-
) {
|
|
84
|
-
this.headers.cacheInvalidate = {
|
|
85
|
-
value: urlUtils.urlFor({
|
|
86
|
-
relativeUrl: urlUtils.urlJoin('/p', scheduledResource.uuid, '/')
|
|
87
|
-
})
|
|
88
|
-
};
|
|
89
|
-
} else {
|
|
90
|
-
this.headers.cacheInvalidate = false;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return result;
|
|
94
|
-
});
|
|
44
|
+
const response = {};
|
|
45
|
+
response[resourceType] = [scheduledResource];
|
|
46
|
+
return response;
|
|
95
47
|
}
|
|
96
48
|
},
|
|
97
49
|
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
const Promise = require('bluebird');
|
|
2
2
|
const _ = require('lodash');
|
|
3
|
-
const validator = require('@tryghost/validator');
|
|
4
3
|
const models = require('../../models');
|
|
5
4
|
const routeSettings = require('../../services/route-settings');
|
|
6
5
|
const frontendSettings = require('../../../frontend/services/settings');
|
|
7
6
|
const i18n = require('../../../shared/i18n');
|
|
8
|
-
const {BadRequestError, NoPermissionError
|
|
7
|
+
const {BadRequestError, NoPermissionError} = require('@tryghost/errors');
|
|
9
8
|
const settingsService = require('../../services/settings');
|
|
10
|
-
const settingsCache = require('../../../shared/settings-cache');
|
|
11
9
|
const membersService = require('../../services/members');
|
|
12
10
|
|
|
11
|
+
const settingsBREADService = settingsService.getSettingsBREADServiceInstance();
|
|
12
|
+
|
|
13
13
|
module.exports = {
|
|
14
14
|
docName: 'settings',
|
|
15
15
|
|
|
@@ -17,26 +17,7 @@ module.exports = {
|
|
|
17
17
|
options: ['type', 'group'],
|
|
18
18
|
permissions: true,
|
|
19
19
|
query(frame) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
// CASE: no context passed (functional call)
|
|
23
|
-
if (!frame.options.context) {
|
|
24
|
-
return Promise.resolve(settings.filter((setting) => {
|
|
25
|
-
return setting.group === 'site';
|
|
26
|
-
}));
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (!frame.options.context.internal) {
|
|
30
|
-
// CASE: omit core settings unless internal request
|
|
31
|
-
settings = _.filter(settings, (setting) => {
|
|
32
|
-
const isCore = setting.group === 'core';
|
|
33
|
-
return !isCore;
|
|
34
|
-
});
|
|
35
|
-
// CASE: omit secret settings unless internal request
|
|
36
|
-
settings = settings.map(settingsService.hideValueIfSecret);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return settings;
|
|
20
|
+
return settingsBREADService.browse(frame.options.context);
|
|
40
21
|
}
|
|
41
22
|
},
|
|
42
23
|
|
|
@@ -55,41 +36,7 @@ module.exports = {
|
|
|
55
36
|
}
|
|
56
37
|
},
|
|
57
38
|
query(frame) {
|
|
58
|
-
|
|
59
|
-
if (frame.options.key === 'slack') {
|
|
60
|
-
const slackURL = settingsCache.get('slack_url', {resolve: false});
|
|
61
|
-
const slackUsername = settingsCache.get('slack_username', {resolve: false});
|
|
62
|
-
|
|
63
|
-
setting = slackURL || slackUsername;
|
|
64
|
-
setting.key = 'slack';
|
|
65
|
-
setting.value = [{
|
|
66
|
-
url: slackURL && slackURL.value,
|
|
67
|
-
username: slackUsername && slackUsername.value
|
|
68
|
-
}];
|
|
69
|
-
} else {
|
|
70
|
-
setting = settingsCache.get(frame.options.key, {resolve: false});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (!setting) {
|
|
74
|
-
return Promise.reject(new NotFoundError({
|
|
75
|
-
message: i18n.t('errors.api.settings.problemFindingSetting', {
|
|
76
|
-
key: frame.options.key
|
|
77
|
-
})
|
|
78
|
-
}));
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// @TODO: handle in settings model permissible fn
|
|
82
|
-
if (setting.group === 'core' && !(frame.options.context && frame.options.context.internal)) {
|
|
83
|
-
return Promise.reject(new NoPermissionError({
|
|
84
|
-
message: i18n.t('errors.api.settings.accessCoreSettingFromExtReq')
|
|
85
|
-
}));
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
setting = settingsService.hideValueIfSecret(setting);
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
[frame.options.key]: setting
|
|
92
|
-
};
|
|
39
|
+
return settingsBREADService.read(frame.options.key, frame.options.context);
|
|
93
40
|
}
|
|
94
41
|
},
|
|
95
42
|
|
|
@@ -153,17 +100,7 @@ module.exports = {
|
|
|
153
100
|
],
|
|
154
101
|
async query(frame) {
|
|
155
102
|
const {email, type} = frame.data;
|
|
156
|
-
if (typeof email !== 'string' || !validator.isEmail(email)) {
|
|
157
|
-
throw new BadRequestError({
|
|
158
|
-
message: i18n.t('errors.api.settings.invalidEmailReceived')
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
103
|
|
|
162
|
-
if (!type || !['fromAddressUpdate', 'supportAddressUpdate'].includes(type)) {
|
|
163
|
-
throw new BadRequestError({
|
|
164
|
-
message: 'Invalid email type recieved'
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
104
|
try {
|
|
168
105
|
// Send magic link to update fromAddress
|
|
169
106
|
await membersService.settings.sendEmailAddressUpdateMagicLink({
|
|
@@ -232,76 +169,20 @@ module.exports = {
|
|
|
232
169
|
}
|
|
233
170
|
},
|
|
234
171
|
async query(frame) {
|
|
172
|
+
let stripeConnectData;
|
|
235
173
|
const stripeConnectIntegrationToken = frame.data.settings.find(setting => setting.key === 'stripe_connect_integration_token');
|
|
236
174
|
|
|
237
|
-
const settings = frame.data.settings.filter((setting) => {
|
|
238
|
-
// The `stripe_connect_integration_token` "setting" is only used to set the `stripe_connect_*` settings.
|
|
239
|
-
return ![
|
|
240
|
-
'stripe_connect_integration_token',
|
|
241
|
-
'stripe_connect_publishable_key',
|
|
242
|
-
'stripe_connect_secret_key',
|
|
243
|
-
'stripe_connect_livemode',
|
|
244
|
-
'stripe_connect_account_id',
|
|
245
|
-
'stripe_connect_display_name'
|
|
246
|
-
].includes(setting.key)
|
|
247
|
-
// Remove obfuscated settings
|
|
248
|
-
&& !(setting.value === settingsService.obfuscatedSetting && settingsService.isSecretSetting(setting));
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
const getSetting = setting => settingsCache.get(setting.key, {resolve: false});
|
|
252
|
-
|
|
253
|
-
const firstUnknownSetting = settings.find(setting => !getSetting(setting));
|
|
254
|
-
|
|
255
|
-
if (firstUnknownSetting) {
|
|
256
|
-
throw new NotFoundError({
|
|
257
|
-
message: i18n.t('errors.api.settings.problemFindingSetting', {
|
|
258
|
-
key: firstUnknownSetting.key
|
|
259
|
-
})
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
if (!(frame.options.context && frame.options.context.internal)) {
|
|
264
|
-
const firstCoreSetting = settings.find(setting => getSetting(setting).group === 'core');
|
|
265
|
-
if (firstCoreSetting) {
|
|
266
|
-
throw new NoPermissionError({
|
|
267
|
-
message: i18n.t('errors.api.settings.accessCoreSettingFromExtReq')
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
175
|
if (stripeConnectIntegrationToken && stripeConnectIntegrationToken.value) {
|
|
273
176
|
const getSessionProp = prop => frame.original.session[prop];
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
settings.push({
|
|
281
|
-
key: 'stripe_connect_secret_key',
|
|
282
|
-
value: data.secret_key
|
|
283
|
-
});
|
|
284
|
-
settings.push({
|
|
285
|
-
key: 'stripe_connect_livemode',
|
|
286
|
-
value: data.livemode
|
|
287
|
-
});
|
|
288
|
-
settings.push({
|
|
289
|
-
key: 'stripe_connect_display_name',
|
|
290
|
-
value: data.display_name
|
|
291
|
-
});
|
|
292
|
-
settings.push({
|
|
293
|
-
key: 'stripe_connect_account_id',
|
|
294
|
-
value: data.account_id
|
|
295
|
-
});
|
|
296
|
-
} catch (err) {
|
|
297
|
-
throw new BadRequestError({
|
|
298
|
-
err,
|
|
299
|
-
message: 'The Stripe Connect token could not be parsed.'
|
|
300
|
-
});
|
|
301
|
-
}
|
|
177
|
+
|
|
178
|
+
stripeConnectData = await settingsBREADService.getStripeConnectData(
|
|
179
|
+
stripeConnectIntegrationToken,
|
|
180
|
+
getSessionProp,
|
|
181
|
+
membersService.stripeConnect.getStripeConnectTokenData
|
|
182
|
+
);
|
|
302
183
|
}
|
|
303
184
|
|
|
304
|
-
return
|
|
185
|
+
return await settingsBREADService.edit(frame.data.settings, frame.options, stripeConnectData);
|
|
305
186
|
}
|
|
306
187
|
},
|
|
307
188
|
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
const Promise = require('bluebird');
|
|
2
2
|
const _ = require('lodash');
|
|
3
3
|
const i18n = require('../../../../../../shared/i18n');
|
|
4
|
-
const {NotFoundError, ValidationError} = require('@tryghost/errors');
|
|
4
|
+
const {NotFoundError, ValidationError, BadRequestError} = require('@tryghost/errors');
|
|
5
|
+
const validator = require('@tryghost/validator');
|
|
6
|
+
|
|
7
|
+
const messages = {
|
|
8
|
+
invalidEmailReceived: 'Please send a valid email',
|
|
9
|
+
invalidEmailTypeReceived: 'Invalid email type received'
|
|
10
|
+
};
|
|
5
11
|
|
|
6
12
|
module.exports = {
|
|
7
13
|
read(apiConfig, frame) {
|
|
@@ -62,5 +68,21 @@ module.exports = {
|
|
|
62
68
|
if (errors.length) {
|
|
63
69
|
return Promise.reject(errors[0]);
|
|
64
70
|
}
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
updateMembersEmail(apiConfig, frame) {
|
|
74
|
+
const {email, type} = frame.data;
|
|
75
|
+
|
|
76
|
+
if (typeof email !== 'string' || !validator.isEmail(email)) {
|
|
77
|
+
throw new BadRequestError({
|
|
78
|
+
message: messages.invalidEmailReceived
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!type || !['fromAddressUpdate', 'supportAddressUpdate'].includes(type)) {
|
|
83
|
+
throw new BadRequestError({
|
|
84
|
+
message: messages.invalidEmailTypeReceived
|
|
85
|
+
});
|
|
86
|
+
}
|
|
65
87
|
}
|
|
66
88
|
};
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
const models = require('../../models');
|
|
2
2
|
const i18n = require('../../../shared/i18n');
|
|
3
3
|
const errors = require('@tryghost/errors');
|
|
4
|
+
const getWebhooksServiceInstance = require('../../services/webhooks/webhooks-service');
|
|
5
|
+
|
|
6
|
+
const webhooksService = getWebhooksServiceInstance({
|
|
7
|
+
WebhookModel: models.Webhook
|
|
8
|
+
});
|
|
4
9
|
|
|
5
10
|
module.exports = {
|
|
6
11
|
docName: 'webhooks',
|
|
@@ -15,34 +20,7 @@ module.exports = {
|
|
|
15
20
|
data: [],
|
|
16
21
|
permissions: true,
|
|
17
22
|
async query(frame) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// NOTE: this check can be removed once `webhooks.integration_id` gets foreigh ke constraint (Ghost 4.0)
|
|
21
|
-
if (!isIntegrationRequest && frame.data.webhooks[0].integration_id) {
|
|
22
|
-
const integration = await models.Integration.findOne({id: frame.data.webhooks[0].integration_id}, {context: {internal: true}});
|
|
23
|
-
|
|
24
|
-
if (!integration) {
|
|
25
|
-
throw new errors.ValidationError({
|
|
26
|
-
message: i18n.t('notices.data.validation.index.schemaValidationFailed', {
|
|
27
|
-
key: 'integration_id'
|
|
28
|
-
}),
|
|
29
|
-
context: i18n.t('errors.api.webhooks.nonExistingIntegrationIdProvided.context'),
|
|
30
|
-
help: i18n.t('errors.api.webhooks.nonExistingIntegrationIdProvided.help')
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const webhook = await models.Webhook.getByEventAndTarget(
|
|
36
|
-
frame.data.webhooks[0].event,
|
|
37
|
-
frame.data.webhooks[0].target_url,
|
|
38
|
-
frame.options
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
if (webhook) {
|
|
42
|
-
throw new errors.ValidationError({message: i18n.t('errors.api.webhooks.webhookAlreadyExists')});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return models.Webhook.add(frame.data.webhooks[0], frame.options);
|
|
23
|
+
return await webhooksService.add(frame.data, frame.options);
|
|
46
24
|
}
|
|
47
25
|
},
|
|
48
26
|
|