2021-06-15 17:01:22 +01:00
|
|
|
const debug = require('@tryghost/debug')('services:webhooks:trigger');
|
2021-06-15 15:36:27 +01:00
|
|
|
const logging = require('@tryghost/logging');
|
2022-05-12 11:47:09 +08:00
|
|
|
const ghostVersion = require('@tryghost/version');
|
2022-04-15 10:55:52 +08:00
|
|
|
|
|
|
|
class WebhookTrigger {
|
2022-05-12 11:35:15 +08:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param {Object} options
|
|
|
|
* @param {Object} options.models - Ghost models
|
|
|
|
* @param {Function} options.payload - Function to generate payload
|
|
|
|
* @param {Object} [options.request] - HTTP request handling library
|
|
|
|
*/
|
|
|
|
constructor({models, payload, request}){
|
2022-04-15 10:55:52 +08:00
|
|
|
this.models = models;
|
|
|
|
this.payload = payload;
|
2022-05-12 11:35:15 +08:00
|
|
|
|
|
|
|
this.request = request ?? require('@tryghost/request');
|
2022-04-15 10:55:52 +08:00
|
|
|
}
|
2018-10-19 00:01:30 +05:30
|
|
|
|
2019-02-03 13:36:08 +01:00
|
|
|
getAll(event) {
|
2022-04-15 10:55:52 +08:00
|
|
|
return this.models
|
2019-02-03 13:36:08 +01:00
|
|
|
.Webhook
|
|
|
|
.findAllByEvent(event, {context: {internal: true}});
|
2022-04-15 10:55:52 +08:00
|
|
|
}
|
2018-10-19 00:01:30 +05:30
|
|
|
|
2019-02-03 13:36:08 +01:00
|
|
|
update(webhook, data) {
|
2022-04-15 10:55:52 +08:00
|
|
|
this.models
|
2019-02-03 13:36:08 +01:00
|
|
|
.Webhook
|
|
|
|
.edit({
|
|
|
|
last_triggered_at: Date.now(),
|
|
|
|
last_triggered_status: data.statusCode,
|
|
|
|
last_triggered_error: data.error || null
|
|
|
|
}, {id: webhook.id})
|
|
|
|
.catch(() => {
|
2020-04-30 20:26:12 +01:00
|
|
|
logging.warn(`Unable to update "last_triggered" for webhook: ${webhook.id}`);
|
2019-02-03 13:36:08 +01:00
|
|
|
});
|
2022-04-15 10:55:52 +08:00
|
|
|
}
|
2018-10-19 00:01:30 +05:30
|
|
|
|
2019-02-03 13:36:08 +01:00
|
|
|
destroy(webhook) {
|
2022-04-15 10:55:52 +08:00
|
|
|
return this.models
|
2019-02-03 13:36:08 +01:00
|
|
|
.Webhook
|
|
|
|
.destroy({id: webhook.id}, {context: {internal: true}})
|
|
|
|
.catch(() => {
|
2020-04-30 20:26:12 +01:00
|
|
|
logging.warn(`Unable to destroy webhook ${webhook.id}.`);
|
2018-10-19 00:01:30 +05:30
|
|
|
});
|
2019-02-03 13:36:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
onSuccess(webhook) {
|
|
|
|
return (res) => {
|
2022-04-15 10:55:52 +08:00
|
|
|
this.update(webhook, {
|
2019-02-03 13:36:08 +01:00
|
|
|
statusCode: res.statusCode
|
|
|
|
});
|
|
|
|
};
|
2022-04-15 10:55:52 +08:00
|
|
|
}
|
2019-02-03 13:36:08 +01:00
|
|
|
|
|
|
|
onError(webhook) {
|
|
|
|
return (err) => {
|
2018-10-19 00:01:30 +05:30
|
|
|
if (err.statusCode === 410) {
|
2020-04-30 20:26:12 +01:00
|
|
|
logging.info(`Webhook destroyed (410 response) for "${webhook.get('event')}" with url "${webhook.get('target_url')}".`);
|
2019-02-03 13:36:08 +01:00
|
|
|
|
2022-04-15 10:55:52 +08:00
|
|
|
return this.destroy(webhook);
|
2018-10-19 00:01:30 +05:30
|
|
|
}
|
|
|
|
|
2022-04-15 10:55:52 +08:00
|
|
|
this.update(webhook, {
|
2019-02-03 13:36:08 +01:00
|
|
|
statusCode: err.statusCode,
|
|
|
|
error: `Request failed: ${err.code || 'unknown'}`
|
|
|
|
});
|
2018-10-19 00:01:30 +05:30
|
|
|
|
2020-04-30 20:26:12 +01:00
|
|
|
logging.warn(`Request to ${webhook.get('target_url') || null} failed because of: ${err.code || ''}.`);
|
2019-02-03 13:36:08 +01:00
|
|
|
};
|
2018-10-19 00:01:30 +05:30
|
|
|
}
|
2022-04-15 10:55:52 +08:00
|
|
|
|
|
|
|
async trigger(event, model) {
|
|
|
|
const response = {
|
|
|
|
onSuccess: this.onSuccess.bind(this),
|
|
|
|
onError: this.onError.bind(this)
|
|
|
|
};
|
|
|
|
|
|
|
|
const hooks = await this.getAll(event);
|
|
|
|
|
|
|
|
debug(`${hooks.models.length} webhooks found for ${event}.`);
|
|
|
|
|
2022-05-12 11:35:15 +08:00
|
|
|
for (const webhook of hooks.models) {
|
2022-04-15 10:55:52 +08:00
|
|
|
const hookPayload = await this.payload(webhook.get('event'), model);
|
|
|
|
|
|
|
|
const reqPayload = JSON.stringify(hookPayload);
|
|
|
|
const url = webhook.get('target_url');
|
|
|
|
const opts = {
|
|
|
|
body: reqPayload,
|
|
|
|
headers: {
|
|
|
|
'Content-Length': Buffer.byteLength(reqPayload),
|
2022-05-12 11:47:09 +08:00
|
|
|
'Content-Type': 'application/json',
|
|
|
|
'Content-Version': `v${ghostVersion.safe}`
|
2022-04-15 10:55:52 +08:00
|
|
|
},
|
|
|
|
timeout: 2 * 1000,
|
|
|
|
retry: 5
|
|
|
|
};
|
|
|
|
|
|
|
|
logging.info(`Triggering webhook for "${webhook.get('event')}" with url "${url}"`);
|
|
|
|
|
2022-05-12 11:35:15 +08:00
|
|
|
await this.request(url, opts)
|
2022-04-15 10:55:52 +08:00
|
|
|
.then(response.onSuccess(webhook))
|
|
|
|
.catch(response.onError(webhook));
|
2022-05-12 11:35:15 +08:00
|
|
|
}
|
2022-04-15 10:55:52 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = WebhookTrigger;
|