@octonary/mailgun-js
Version:
Simple Node.js helper module for Mailgun API. Forked to fix IE issues with ` character.
213 lines (171 loc) • 5.73 kB
JavaScript
var tsscmp = require('tsscmp');
var crypto = require('crypto');
var Attachment = require('./attachment');
var Request = require('./request');
var builder = require('./build');
var resources = require('./schema').definitions;
var mailgunExpirey = 15 * 60 * 1000;
var mailgunHashType = 'sha256';
var mailgunSignatureEncoding = 'hex';
var Mailgun = function (options) {
if(!options.apiKey){
throw new Error('apiKey value must be defined!');
}
this.username = 'api';
this.apiKey = options.apiKey;
this.publicApiKey = options.publicApiKey;
this.domain = options.domain;
this.auth = [this.username, this.apiKey].join(':');
this.mute = options.mute || false;
this.timeout = options.timeout;
this.host = options.host || 'api.mailgun.net';
this.endpoint = options.endpoint || '/v3';
this.protocol = options.protocol || 'https:';
this.port = options.port || 443;
this.retry = options.retry || 1;
if (options.proxy) {
this.proxy = options.proxy;
}
this.options = {
host: this.host,
endpoint: this.endpoint,
protocol: this.protocol,
port: this.port,
auth: this.auth,
proxy: this.proxy,
timeout: this.timeout,
retry: this.retry
};
this.mailgunTokens = {};
};
Mailgun.prototype.getDomain = function (method, resource) {
var d = this.domain;
//filter out API calls that do not require a domain specified
if ((resource.indexOf('/routes') >= 0)
|| (resource.indexOf('/lists') >= 0)
|| (resource.indexOf('/address') >= 0)
|| (resource.indexOf('/domains') >= 0 )) {
d = '';
}
else if ((resource.indexOf('/messages') >= 0)
&& (method === 'GET' || method === 'DELETE')) {
d = 'domains/' + this.domain;
}
return d;
};
Mailgun.prototype.getRequestOptions = function (resource) {
var o = this.options;
// use public API key if we have it for the routes that require it
if ((resource.indexOf('/address/validate') >= 0
|| (resource.indexOf('/address/parse') >= 0))
&& this.publicApiKey) {
var copy = Object.assign({}, this.options);
copy.auth = [this.username, this.publicApiKey].join(':');
o = copy;
}
return o;
};
Mailgun.prototype.request = function (method, resource, data, fn) {
var fullpath = resource;
var domain = this.getDomain(method, resource);
if (domain) {
fullpath = '/'.concat(domain, resource);
}
var req = new Request(this.options);
return req.request(method, fullpath, data, fn);
};
Mailgun.prototype.post = function (path, data, fn) {
var req = new Request(this.options);
return req.request('POST', path, data, fn);
};
Mailgun.prototype.get = function (path, data, fn) {
var req = new Request(this.options);
return req.request('GET', path, data, fn);
};
Mailgun.prototype.delete = function (path, data, fn) {
var req = new Request(this.options);
return req.request('DELETE', path, data, fn);
};
Mailgun.prototype.put = function (path, data, fn) {
var req = new Request(this.options);
return req.request('PUT', path, data, fn);
};
Mailgun.prototype.validateWebhook = function (timestamp, token, signature) {
var self = this;
var adjustedTimestamp = parseInt(timestamp, 10) * 1000;
var fresh = (Math.abs(Date.now() - adjustedTimestamp) < mailgunExpirey);
if (!fresh) {
if (!this.mute) {
console.error('[mailgun] Stale Timestamp: this may be an attack');
console.error('[mailgun] However, this is most likely your fault\n');
console.error('[mailgun] run `ntpdate ntp.ubuntu.com` and check your system clock\n');
console.error('[mailgun] System Time: ' + new Date().toString());
console.error('[mailgun] Mailgun Time: ' + new Date(adjustedTimestamp).toString(), timestamp);
console.error('[mailgun] Delta: ' + (Date.now() - adjustedTimestamp));
}
return false;
}
if (this.mailgunTokens[token]) {
if (!this.mute) {
console.error('[mailgun] Replay Attack');
}
return false;
}
this.mailgunTokens[token] = true;
const tokenTimeout = setTimeout(function () {
delete self.mailgunTokens[token];
}, mailgunExpirey + (5 * 1000));
tokenTimeout.unref();
return tsscmp(
signature
, crypto.createHmac(mailgunHashType, self.apiKey)
.update(new Buffer(timestamp + token, 'utf-8'))
.digest(mailgunSignatureEncoding)
);
};
Mailgun.prototype.validate = function (address, isPrivate, opts, fn) {
if (typeof opts === 'function') {
fn = opts;
opts = {};
}
if (typeof isPrivate === 'object') {
opts = isPrivate;
isPrivate = false;
}
if (typeof isPrivate === 'function') {
fn = isPrivate;
isPrivate = false;
opts = {};
}
var resource = isPrivate ? '/address/private/validate' : '/address/validate';
var options = this.getRequestOptions(resource);
var req = new Request(options);
var data = Object.assign({}, { address: address }, opts);
return req.request('GET', resource, data, fn);
};
Mailgun.prototype.parse = function (addresses, isPrivate, opts, fn) {
if (typeof opts === 'function') {
fn = opts;
opts = {};
}
if (typeof isPrivate === 'object') {
opts = isPrivate;
isPrivate = false;
}
if (typeof isPrivate === 'function') {
fn = isPrivate;
isPrivate = false;
opts = {};
}
var resource = isPrivate ? '/address/private/parse' : '/address/parse';
var options = this.getRequestOptions(resource);
var req = new Request(options);
var data = Object.assign({}, { addresses: addresses }, opts);
return req.request('GET', resource, data, fn);
};
builder.build(Mailgun, resources);
Mailgun.prototype.Attachment = Attachment;
Mailgun.prototype.Mailgun = Mailgun;
module.exports = function (options) {
return new Mailgun(options);
};