Socket
Socket
Sign inDemoInstall

mailgun

Package Overview
Dependencies
Maintainers
0
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mailgun - npm Package Compare versions

Comparing version 0.1.6 to 0.4.0

.npmignore

362

mailgun.js

@@ -0,79 +1,315 @@

//
// Copyright (C) 2011 Patrick Stein
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// TODO - better error handling on requests
// Dirt simple includes. Nice that we can keep things simple :)
var http = require('http'),
querystring = require('querystring');
//constants
// Mailgun options constants. See Mailgun's API docs for details.
var MAILGUN_TAG = 'X-Mailgun-Tag',
CAMPAIGN_ID = 'X-Campaign-Id';
// Utility dumb XML parsing helper. Builds a regex of the form
// `<input>\s*(.*?)\s*</input>`, and memoizes for a slight optimization.
var xre = function() {
var cache = {};
return function(input) {
// Try to fetch the memoized version.
if (cache.hasOwnProperty(input)) return cache[input];
// Otherwise build it and return it.
var re = new RegExp('<' + input + '>\\s*(.*?)\\s*</' + input + '>', 'im');
cache[input] = re;
return re;
};
}();
// This class is used to tie functionality to an API key, rather than
// using a global initialization function that forces people to use
// only one API key per application.
var Mailgun = function(apiKey) {
//the docs use a base64 header for auth, but that doesn't seem to work...
//var apiKey64 = new Buffer(apiKey).toString('base64');
this.sendText = function(sender, recipients, subject, text, servername, options, callback) {
//defaults
servername = servername || '';
options = options || {};
//be flexible with recipients
if (typeof(recipients) == 'string')
// Authentication uses the api key in base64 form, so we cache that
// here.
this._apiKey64 = new Buffer('api:' + apiKey).toString('base64');
this._apiKey = apiKey;
};
Mailgun.prototype = {};
// Utility method to set up required http options.
Mailgun.prototype._createHttpOptions = function(resource, method, servername) {
return {
host: 'mailgun.net',
port: 80,
method: method,
path: '/api/' + resource + (servername ? '?servername=' + servername : ''),
headers: {
'Authorization': 'Basic ' + this._apiKey64
}
};
}
//
// Here be the email sending code.
//
Mailgun.prototype.sendText = function(sender, recipients, subject, text) {
// These are flexible arguments, so we define them here to make
// sure they're in scope.
var servername = '';
var options = {};
var callback = null;
// Less than 4 arguments means we're missing something that prevents
// us from even sending an email, so we fail.
if (arguments.length < 4)
throw new Error('Missing required argument');
// Flexible argument magic!
var args = Array.prototype.slice.call(arguments, 4);
// Pluck servername.
if (args[0] && typeof args[0] == 'string')
servername = args.shift() || servername;
// Pluck options.
if (args[0] && typeof args[0] == 'object')
options = args.shift() || options;
// Pluck callback.
if (args[0] && typeof args[0] == 'function')
callback = args.shift() || callback;
// Don't be messy.
delete args;
// We allow recipients to be passed as either a string or an array,
// but normalize to to an array for consistency later in the
// function.
if (typeof(recipients) == 'string')
recipients = [recipients];
//generate the body text
var body = querystring.stringify({
sender: sender,
recipients: recipients.join(', '),
subject: subject,
body: text,
options: JSON.stringify(options)
});
//prep http request
var httpOptions = {
host: 'mailgun.net',
port: 80,
method: 'POST',
path: '/api/messages.txt?api_key=' + apiKey + '&servername=' + servername,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(body)
// Build the HTTP POST body text.
var body = querystring.stringify({
sender: sender,
recipients: recipients.join(', '),
subject: subject,
body: text,
options: JSON.stringify(options)
});
// Prepare our API request.
var httpOptions = this._createHttpOptions('messages.txt', 'POST', servername);
httpOptions.headers['Content-Type'] = 'application/x-www-form-urlencoded';
httpOptions.headers['Content-Length'] = Buffer.byteLength(body);
// Fire the request to Mailgun's API.
var req = http.request(httpOptions, function(res) {
// If the user supplied a callback, fire it and set `err` to the
// status code of the request if it wasn't successful.
if (callback) callback(res.statusCode != 201 ? new Error(res.statusCode) : undefined);
});
// Wrap up the request by sending the body, which contains the
// actual email data we want to send.
req.end(body);
};
Mailgun.prototype.sendRaw = function(sender, recipients, rawBody) {
// These are flexible arguments, so we define them here to make
// sure they're in scope.
var servername = '';
var callback = null;
// Less than 3 arguments means we're missing something that prevents
// us from even sending an email, so we fail.
if (arguments.length < 3)
throw new Error('Missing required argument');
// Flexible argument magic!
var args = Array.prototype.slice.call(arguments, 3);
// Pluck servername.
if (args[0] && typeof args[0] == 'string')
servername = args.shift() || servername;
// Pluck callback.
if (args[0] && typeof args[0] == 'function')
callback = args.shift() || callback;
// Don't be messy.
delete args;
// We allow recipients to be passed as either a string or an array,
// but normalize to to an array for consistency later in the
// function.
if (typeof(recipients) == 'string')
recipients = [recipients];
// Mailgun wants its messages formatted in a special way. Why?
// Who knows.
var message = sender +
'\n' + recipients.join(', ') +
'\n\n' + rawBody;
// Prepare the APi request.
var httpOptions = this._createHttpOptions('messages.eml', 'POST', servername);
httpOptions.headers['Content-Type'] = 'text/plain; charset=utf-8';
httpOptions.headers['Content-Length'] = Buffer.byteLength(message);
// Fire it.
var req = http.request(httpOptions, function(res) {
// If the user supplied a callback, fire it and set `err` to the
// status code of the request if it wasn't successful.
if (callback) callback(res.statusCode != 201 ? new Error(res.statusCode) : undefined);
});
// Wrap up the request by sending the message, which contains the
// actual email data we want to send.
req.end(message);
};
//
// Here follows the routing code
//
Mailgun.prototype.createRoute = function(pattern, destination, callback) {
// Prep the request.
var httpOptions = this._createHttpOptions('routes.xml', 'POST');
// Create the HTTP POST data.
var data = '' +
'<route>' +
'<pattern>' + pattern + '</pattern>' +
'<destination>' + destination + '</destination>' +
'</route>';
// Prep the request.
var httpOptions = this._createHttpOptions('routes.xml', 'POST');
httpOptions.headers['Content-Type'] = 'text/xml';
httpOptions.headers['Content-Length'] = Buffer.byteLength(data);
// Fire it.
http.request(httpOptions, function(res) {
// Collect the data
var data = '';
res.on('data', function(c) { data += c });
res.on('close', function(err) { callback(err) });
res.on('end', function() { finish() });
// Handle the results
var finish = function() {
if (res.statusCode == 201) {
var id = xre('id').exec(data)[1];
callback(undefined, id);
} else {
var message = xre('message').exec(data);
callback(new Error(message ? message[1] : data));
}
};
}).end(data);
};
//fire the request
var req = http.request(httpOptions, function(res) {
if (callback) callback(res.statusCode != 201);
Mailgun.prototype.deleteRoute = function(id, callback) {
// Prep the request
var httpOptions = this._createHttpOptions('routes/' + id + '.xml', 'DELETE');
httpOptions.headers['Content-Type'] = 'text/xml';
httpOptions.headers['Content-Length'] = 0;
// Fire it.
http.request(httpOptions, function(res) {
if (res.statusCode == 200) {
callback(undefined);
} else {
var data = '';
res.on('data', function(c) { data += c });
res.on('close', function(err) { callback(err) });
res.on('end', function() {
var message = xre('message').exec(data);
callback(new Error(message ? message[1] : data))
});
}
}).end();
};
Mailgun.prototype.getRoutes = function(callback) {
// Prep the request.
var httpOptions = this._createHttpOptions('routes.xml', 'GET');
// Fire it.
http.request(httpOptions, function(res) {
// Check for failure
if (res.statusCode != 200)
return callback(res.statusCode);
// We're going to be a little lazy and just eat up all the data
// before parsing it.
var data = '';
res.on('data', function(c) {
data += c;
});
req.end(body);
};
this.sendRaw = function(sender, recipients, rawBody, servername, callback) {
//defaults
servername = servername || '';
//be flexible with recipients
if (typeof(recipients) == 'string')
recipients = [recipients];
//create the message
var message = sender +
'\n' + recipients.join(', ') +
'\n\n' + rawBody;
//prep http request
var httpOptions = {
host: 'mailgun.net',
port: 80,
method: 'POST',
path: '/api/messages.eml?api_key=' + apiKey + '&servername=' + servername,
headers: {
'Content-Type': 'text/plain; charset=utf-8',
'Content-Length': Buffer.byteLength(message)
// Handle catastrophic failures with an error
res.on('close', function(err) {
// FIXME - In some cases this could cause the callback to be called
// with an error, even after we called it successfully.
callback(err.code);
});
// Once the request is done, we have all the data and can parse it.
res.on('end', function() {
// Silly XML parsing because I don't want to include another
// dependency. Fortunately the structure is very simple and
// convenient to parse with this method.
var routes = data.replace(/\s/g, '').match(xre('route'));
var nroutes = [];
for (var i=0; i<routes.length; i++) {
// Pull the route out, since we're going to change it.
var route = routes[i];
// Pull the data.
var r = {};
r.pattern = xre('pattern').exec(route)[1];
r.destination = xre('destination').exec(route)[1];
r.id = xre('id').exec(route)[1];
nroutes.push(r);
}
};
//fire the request
var req = http.request(httpOptions, function(res) {
if (callback) callback(res.statusCode != 201);
// Send the data to the callback.
callback(undefined, nroutes);
});
req.end(message);
};
}).end();
};

@@ -80,0 +316,0 @@

4

package.json
{
"name": "mailgun",
"version": "0.1.6",
"description": "Mailgun API for Node.js",
"version": "0.4.0",
"description": "Node.js interface to the Mailgun API",
"author": "shz",

@@ -6,0 +6,0 @@ "repositories": [{

# node-mailgun
A mailgun API for node.js. At the moment, this really only includes
sending functionality.
This library provides simple access to Mailgun's API for node.js applications.
It's MIT licensed, and being used in production over at [Hipsell](http://hipsell.com).
## Installation
npm install mailgun
Or you can just throw `mailgun.js` into your application. There are
no dependendies outside of node's standard library.
**Note:** `master` on Github is going to be untested/unstable at times,
as this is a small enough library that I don't want to bother
with a more complicated repo structure. As such, you should
really only rely on the version of `mailgun` in `npm`, as
I'll only ever push stable and tested code there.
## Usage
At the time of writing, Mailgun's documentation is actually incorrect in places,
which is unfortunate. As such, I'm going to re-document everything in this README
according to the actual way it's implemented in `node-mailgun`, which itself
is based off the implementation from Mailgun's github account, and not the API
docs on the site.
## Initialization
Access to the API is done through the Mailgun object. It's instantiated
Access to the API is done through a Mailgun object. It's instantiated
like so:

@@ -18,7 +39,13 @@

`sendText(sender, recipients, subject, text, servername='', options={}, callback(err))`
### sendText
Sends a simple plain-text email. This also allows for slightly easier
sending of Mailgun options, since with `sendRaw` you have to set them
in the MIME body yourself.
`sendText(sender, recipients, subject, text, [servername=''], [options={}], [callback(err)])`
* `sender` - Sender of the message; this should be a full email address
(e.g. `example@example.com).
* `recipients` - An array (`['a@example.com', 'b@example.com']`) or string (`example@example.com`)
(e.g. `example@example.com`).
* `recipients` - A string (`example@example.com`) or array of strings (`['a@example.com', 'b@example.com']`)
of recipients; these can be email addresses *or* HTTP URLs.

@@ -28,3 +55,3 @@ * `subject` - Message subject

* `servername` - The name of the Mailgun server. If you only have
one server on your Mailgun account, this can be empty.
one server on your Mailgun account, this can be omitted.
Otherwise, it should be set to the server you want to

@@ -37,30 +64,74 @@ send from.

* `callback` - Callback to be fired when the email is done being sent. This
should take a single parameter, `err`, that will be set to
`true` if the email failed to send.
should take a single parameter, `err`, that will be set to
the status code of the API HTTP response code if the email
failed to send; on success, `err` will be `undefined`.
### Mailgun Headers
#### Example
Mailgun understands a couple headers from `options`. These are defined
below.
sendText('sender@example.com',
['recipient1@example.com', 'http://example.com/recipient2'],
'Behold the wonderous power of email!',
{'X-Campaign-Id': 'something'},
function(err) { err && console.log(err) });
* `X-Mailgun-Tag` - Used to tag sent emails (defined in `Mailgun.MAILGUN_TAG`)
* `X-Campaign-Id` - Used for tracking campaign data (defined in `Mailgun.CAMPAIGN_ID`)
### sendRaw
`sendRaw(sender, recipients, rawBody, servername, callback(err))`
Sends a raw MIME message. *Don't* just use this with text; instead,
you should either build a MIME message manually or by using some MIME
library (I've not been able to find one for node.js -- if you're aware
of one let me know and I'll link it here).
`sendRaw(sender, recipients, rawBody, [servername], [callback(err)])`
* `sender` - Sender of the message; this should be a full email address
(e.g. `example@example.com`)
* `recipients` - An array (`['a@example.com', 'b@example.com']`) or string (`example@example.com`)
* `recipients` - A string (`example@example.com`) or array of strings (`['a@example.com', 'b@example.com']`)
of recipients; these can be email addresses *or* HTTP URLs.
* `rawBody` - MIME message to send
* `servername` - The name of the Mailgun server. If you only have
one server on your Mailgun account, this can be empty.
one server on your Mailgun account, this can be omitted.
Otherwise, it should be set to the server you want to
send from.
* `callback` - Callback to be fired when the email has finished sending.
This function should take a single parameter, `err`, that
will be set to `true` if the email failed to send.
* `callback` - Callback to be fired when the email is done being sent. This
should take a single parameter, `err`, that will be set to
the status code of the API HTTP response code if the email
failed to send; on success, `err` will be `undefined`.
## Example
**Note:** Sending a message via raw MIME lets you use Mailgun's built-in
templating shinies. Check out the [Mailgun Docs](http://documentation.mailgun.net/Documentation/DetailedDocsAndAPIReference#Message_Templates)
for details.
#### Example
sendRaw('sender@example.com',
['recipient1@example.com', 'http://example.com/recipient2'],
'From: sender@example.com' +
'\nTo: ' + 'recipient1@example.com, http://example.com/recipient2' +
'\nContent-Type: text/html; charset=utf-8' +
'\nSubject: I Love Email' +
'\n\nBecause it's just so awesome',
function(err) { err && console.log(err) });
### Email Addresses
Mailgun allows sender and recipient email addresses to be formatted in
several different ways:
* `'John Doe' <john@example.com>`
* `"John Doe" <john@example.com>`
* `John Doe <john@example.com>`
* `<john@example.com>`
* `john@example.com`
### Mailgun Headers
Mailgun understands a couple special headers, specified via `options` when using
`sendText`, or in the MIME headers when using `sendRaw`. These are defined
below.
* `X-Mailgun-Tag` - Used to tag sent emails (defined in `Mailgun.MAILGUN_TAG`)
* `X-Campaign-Id` - Used for tracking campaign data (defined in `Mailgun.CAMPAIGN_ID`)
### Example
Here's a complete sending example.

@@ -70,4 +141,4 @@

var mg = new Mailgun('api-key');
mg.sendText('example@example.com', ['rec1@example.com', 'rec2@example.com'],
var mg = new Mailgun('some-api-key');
mg.sendText('example@example.com', ['Recipient 1 <rec1@example.com>', 'rec2@example.com'],
'This is the subject',

@@ -77,10 +148,47 @@ 'This is the text',

function(err) {
if (err) console.log('Oh noes');
if (err) console.log('Oh noes: ' + err);
else console.log('Success');
});
## TODO:
## Routing
* routes
* mailboxes
Mailgun lets you route incoming email to different destinations. TODO - more docs
### createRoute
TODO
### deleteRoute
Deletes a route if it exists, otherwise fails silently.
### getRoutes
Gets a list of all routes.
`getRoutes(callback(err, routes))`
* `callback` - Callback to be fired when the request has finished. This
should take two parameters: `err`, which will hold either an
HTTP error code, or an error string on failure; and `routes`,
which will be a list of routes on success. Routes returned
through this callback will be objects with three fields: `pattern`,
`destination`, and `id`.
#### Example
getRoutes(function(err, routes) {
if (err) console.log('Error:', err);
for (var i=0; i<routes.length; i++) {
console.log('Route');
console.log(' Pattern:', routes[i].pattern);
console.log(' Destination:', routes[i].destination);
console.log(' Id:', routes[i].id);
}
});
## Eventual Work:
* Mailboxes
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc