Skip to content
This repository was archived by the owner on Nov 24, 2021. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Email helper for KeystoneJS and Node.js Applications. Makes it easy to send dyna
Features include:

* Express-like template system (including support for any express-compatible template engine)
* Support for different email sending services (Mandrill and Mailgun are available now, more may be added)
* Support for different email sending services (Mandrill, Mailgun and AWS SES are available now, more may be added)
* Understands Keystone User models, making it easy to send emails to the results of a query
* Automatically transforms user variables into the correct format for each email service, for mail-merge style variable use
* CSS Stylesheets are automatically inlined for robust email client compatibility
Expand Down Expand Up @@ -101,6 +101,8 @@ See [the Mandrill API Docs](https://mandrillapp.com/api/docs/messages.nodejs.htm

## Usage with Nodemailer

Add a dependency for `nodemailer` to your project.

The following `send()` options are applicable when using `nodemailer` as the transport:

- `from` (String or Object) The name and email to send from (see below)
Expand All @@ -110,6 +112,24 @@ The following `send()` options are applicable when using `nodemailer` as the tra

See [the Nodemailer README](https://github.com/nodemailer/nodemailer) for more information about supported transports and plugins.

## Usage with AWS SES

Add a dependency for `aws-sdk`, i.e. add `"aws-sdk": "^2.20.0",` to package.json of your project.

The following `send()` options are applicable when using `awsses` as the transport:

- `apiKey` (required, String) Your API Key, defaults to `process.env.AWS_SES_API_KEY`
- `apiSecret` (required, String) Your Secret Key, defaults to `process.env.AWS_SES_SECRET_KEY`
- `region` (required, String) Your Sending Region i.e., us-east-1, defaults to `process.env.AWS_SES_REGION`
- `from` (String or Object) The name and email to send from (see below)
- `inline_css` (Boolean) inline CSS classes in your template, defaults to `true`
- `to` (String, Object or Array) The recipient(s) of the email (see below)
- `cc` (String, Object or Array) The CC recipient(s) of the email (same format as `to`, see below)
- `bcc` (String, Object or Array) The BCC recipient(s) of the email (same format as `to`, see below)
- `replyTo` (String, Object or Array) The Reply-To address(es) of the email (same format as `to`, see below)

See [the AWS SES API Docs](http://docs.aws.amazon.com/ses/latest/APIReference/API_SendEmail.html) for the full set of supported options.

## From option

The `from` option can be a String (email address), or Object containing `name` and `email`. In the object form, `name` can also be an object containing `first` and `last` (which will be concatenated with a space). This simplifies usage with `User` models in KeystoneJS. For example:
Expand Down Expand Up @@ -181,6 +201,10 @@ You could previously use a directory as the template name, and Keystone.Email wo

Keystone would previous return errors if the subject, contents, recipient(s) or the sender address were invalid. This is no longer handled by the library, please make sure you validate these options before sending the emails. If the transport validates these options, errors will be passed directly to the callback.

### AWS SES limitations

AWS SES does not support click tracking

## License

MIT Licensed. Copyright (c) 2016 Jed Watson.
Expand Down
2 changes: 1 addition & 1 deletion lib/Email.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ Email.prototype.send = function (renderOptions, sendOptions, callback) {
if (typeof sendOptions === 'function') {
throw new Error('[Email.send] Please pass renderOptions, sendOptions and a callback. See the documentation for more information!');
} else {
// callback is optional, we default it to a no-op so because itmakes it
// callback is optional, we default it to a no-op so because it makes it
// easier to always expect a valid function in the transport implementations
callback = function () {};
}
Expand Down
16 changes: 16 additions & 0 deletions lib/transports/awsses/getRecipients.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
var assign = require('object-assign');

var processAddress = require('../../util/processAddress');

function getRecipients(to) {
var recipients = [];
(Array.isArray(to) ? to : [to]).forEach(function (i) {
var rcpt = processAddress(i);
if (!rcpt.email) return;
recipients.push(rcpt.email);
});

return recipients;
};

module.exports = getRecipients;
35 changes: 35 additions & 0 deletions lib/transports/awsses/getSendOptions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
var assign = require('object-assign');

var getRecipients = require('./getRecipients');
var processAddress = require('../../util/processAddress');

var defaultOptions = {
apiKey: process.env.AWS_SES_API_KEY,
apiSecret: process.env.AWS_SES_SECRET_KEY,
region: process.env.AWS_SES_REGION || 'us-east-1',
};

function getSendOptions(options) {
// default options
options = assign({ Destination: {} }, defaultOptions, options);
// process from name and email
options.Source = processAddress(options.from).address;
// process recipients, including cc and bcc
options.Destination.ToAddresses = getRecipients(options.to);
// handle cc
if (options.cc && options.cc.length > 0) {
options.Destination.CcAddresses = getRecipients(options.cc);
}
// handle bcc
if (options.bcc && options.bcc.length > 0) {
options.Destination.BccAddresses = getRecipients(options.bcc);
}
// handle reply-to
if (options.replyTo && options.replyTo.length > 0) {
options.ReplyToAddresses = getRecipients(options.replyTo);
}
// return
return options;
}

module.exports = getSendOptions;
47 changes: 47 additions & 0 deletions lib/transports/awsses/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
var requireOptional = require('../../util/requireOptional');
var assign = require('object-assign');
var aws = requireOptional('aws-sdk', 'Please install the aws-sdk package to use this transport');
var juice = require('juice');
var getSendOptions = require('./getSendOptions');

function send(email, options, callback) {
// init options
options = assign({ Message: { Subject: {}, Body: { Html: {} } } }, getSendOptions(options), email);

// validate
if (!options.Destination.ToAddresses.length) {
return callback(new Error('No recipients to send to'));
}

// handle html body
options.Message.Body.Html.Data = options.inlineCSS ? juice(options.html) : options.html;

// add subject
if (options.subject) {
options.Message.Subject.Data = options.subject;
}

// init ses
var ses = new aws.SES({
apiVersion: '2010-12-01',
accessKeyId: options.apiKey,
secretAccessKey: options.apiSecret,
region: options.region
});

var params = {
Message: options.Message,
Destination: options.Destination,
Source: options.Source,
};

// add reply-to
if (options.ReplyToAddresses) {
params.ReplyToAddresses = options.ReplyToAddresses
}

// send emails
ses.sendEmail(params, callback);
}

module.exports = send;
2 changes: 1 addition & 1 deletion lib/transports/nodemailer/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
var requireOptional = require('../util/requireOptional');
var requireOptional = require('../../util/requireOptional');

var assign = require('object-assign');
var nodemailer = requireOptional('nodemailer', 'Please install the nodemailer package to use this transport');
Expand Down
2 changes: 1 addition & 1 deletion lib/util/requireOptional.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module.exports = function (pkg, msg) {
try {
return require('pkg');
return require(pkg);
} catch (e) {
if (e.code === 'MODULE_NOT_FOUND') {
throw new Error(msg);
Expand Down
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"html-to-text": "^2.1.3",
"juice": "^3.0.0",
"lodash": "^4.15.0",
"mailgun-js": "^0.7.12",
"mailgun-js": "^0.22.0",
"mandrill-api": "^1.0.45",
"object-assign": "^4.1.0"
},
Expand Down Expand Up @@ -37,8 +37,10 @@
"email",
"keystone",
"keystonejs",
"mandrill",
"mailgun"
"mailgun",
"ses",
"aws",
"mandrill"
],
"author": "Jed Watson",
"license": "MIT",
Expand Down
1 change: 1 addition & 0 deletions tests/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ describe('utils', function () {
it('should return transport if transport is found', function () {
var res1 = getTransport('mailgun');
var res2 = getTransport('mandrill');
// Note: No tests are possible for 'nodemailer' or 'awsses' as they rely on optional dependencies
assert.equal(typeof res1, 'function');
assert.equal(typeof res2, 'function');
});
Expand Down