0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-27 22:49:56 -05:00
ghost/core/server/services/email-analytics/providers/mailgun.js
Kevin Ansfield 08e1268aed
Added migration to remove surrounding <> in email_batches.provider_id (#12673)
refs https://github.com/TryGhost/Team/issues/221#issuecomment-759105424

- Mailgun responds to an email send with a provider id in the format `<x@y.com>` but everywhere else it's used in their API it uses the format `x@y.com`
- updates email batch save to strip the brackets, and migration removes brackets from existing records so we no longer have to add special handling for the stored id any time we use it
2021-02-23 08:48:21 +00:00

130 lines
4.5 KiB
JavaScript

const mailgunJs = require('mailgun-js');
const moment = require('moment');
const EventProcessingResult = require('../lib/event-processing-result');
const EVENT_FILTER = 'delivered OR opened OR failed OR unsubscribed OR complained';
const PAGE_LIMIT = 300;
const TRUST_THRESHOLD_S = 30 * 60; // 30 minutes
const DEFAULT_TAGS = ['bulk-email'];
class EmailAnalyticsMailgunProvider {
constructor({config, settings, mailgun, logging = console}) {
this.config = config;
this.settings = settings;
this.logging = logging;
this.tags = [...DEFAULT_TAGS];
this._mailgun = mailgun;
if (this.config.get('bulkEmail:mailgun:tag')) {
this.tags.push(this.config.get('bulkEmail:mailgun:tag'));
}
}
// unless an instance is passed in to the constructor, generate a new instance each
// time the getter is called to account for changes in config/settings over time
get mailgun() {
if (this._mailgun) {
return this._mailgun;
}
const bulkEmailConfig = this.config.get('bulkEmail');
const bulkEmailSetting = {
apiKey: this.settings.get('mailgun_api_key'),
domain: this.settings.get('mailgun_domain'),
baseUrl: this.settings.get('mailgun_base_url')
};
const hasMailgunConfig = !!(bulkEmailConfig && bulkEmailConfig.mailgun);
const hasMailgunSetting = !!(bulkEmailSetting && bulkEmailSetting.apiKey && bulkEmailSetting.baseUrl && bulkEmailSetting.domain);
if (!hasMailgunConfig && !hasMailgunSetting) {
this.logging.warn(`Bulk email service is not configured`);
return undefined;
}
const mailgunConfig = hasMailgunConfig ? bulkEmailConfig.mailgun : bulkEmailSetting;
const baseUrl = new URL(mailgunConfig.baseUrl);
return mailgunJs({
apiKey: mailgunConfig.apiKey,
domain: mailgunConfig.domain,
protocol: baseUrl.protocol,
host: baseUrl.hostname,
port: baseUrl.port,
endpoint: baseUrl.pathname,
retry: 5
});
}
// do not start from a particular time, grab latest then work back through
// pages until we get a blank response
fetchAll(batchHandler) {
const options = {
event: EVENT_FILTER,
limit: PAGE_LIMIT,
tags: this.tags.join(' AND ')
};
return this._fetchPages(options, batchHandler);
}
// fetch from the last known timestamp-TRUST_THRESHOLD then work forwards
// through pages until we get a blank response. This lets us get events
// quicker than the TRUST_THRESHOLD
fetchLatest(latestTimestamp, batchHandler, options) {
const beginDate = moment(latestTimestamp).subtract(TRUST_THRESHOLD_S, 's').toDate();
const mailgunOptions = {
limit: PAGE_LIMIT,
event: EVENT_FILTER,
tags: this.tags.join(' AND '),
begin: beginDate.toUTCString(),
ascending: 'yes'
};
return this._fetchPages(mailgunOptions, batchHandler, options);
}
async _fetchPages(mailgunOptions, batchHandler, {maxEvents = Infinity} = {}) {
const {mailgun} = this;
if (!mailgun) {
this.logging.warn(`Bulk email service is not configured`);
return new EventProcessingResult();
}
const result = new EventProcessingResult();
let page = await mailgun.events().get(mailgunOptions);
let events = page && page.items && page.items.map(this.normalizeEvent) || [];
pagesLoop:
while (events.length !== 0) {
const batchResult = await batchHandler(events);
result.merge(batchResult);
if (result.totalEvents >= maxEvents) {
break pagesLoop;
}
page = await mailgun.get(page.paging.next.replace('https://api.mailgun.net/v3', ''));
events = page && page.items && page.items.map(this.normalizeEvent) || [];
}
return result;
}
normalizeEvent(event) {
let providerId = event.message && event.message.headers && event.message.headers['message-id'];
return {
type: event.event,
severity: event.severity,
recipientEmail: event.recipient,
emailId: event['user-variables'] && event['user-variables']['email-id'],
providerId: providerId,
timestamp: new Date(event.timestamp * 1000)
};
}
}
module.exports = EmailAnalyticsMailgunProvider;