Use nodemailer template engine

This commit is contained in:
Tankred Hase 2016-05-29 17:51:10 +02:00
parent 2d510038cb
commit 3a6842c296
3 changed files with 68 additions and 63 deletions

View File

@ -19,6 +19,7 @@
const log = require('npmlog'); const log = require('npmlog');
const util = require('../service/util'); const util = require('../service/util');
const message = require('./message.json');
/** /**
* A simple wrapper around Nodemailer to send verification emails * A simple wrapper around Nodemailer to send verification emails
@ -35,12 +36,12 @@ class Email {
/** /**
* Create an instance of the reusable nodemailer SMTP transport. * Create an instance of the reusable nodemailer SMTP transport.
* @param {string} host The SMTP server's hostname e.g. 'smtp.gmail.com' * @param {string} host SMTP server's hostname: 'smtp.gmail.com'
* @param {Object} auth Auth credential e.g. { user:'user@gmail.com', pass:'pass' } * @param {Object} auth Auth credential: { user:'user@gmail.com', pass:'pass' }
* @param {Object} sender The message 'FROM' field e.g. { name:'Your Support', email:'noreply@exmple.com' } * @param {Object} sender message 'FROM' field: { name:'Your Support', email:'noreply@exmple.com' }
* @param {string} port (optional) The SMTP server's SMTP port. Defaults to 465. * @param {string} port (optional) SMTP server's SMTP port. Defaults to 465.
* @param {boolean} secure (optional) If TSL should be used. Defaults to true. * @param {boolean} secure (optional) if TSL should be used. Defaults to true.
* @param {boolean} requireTLS (optional) If TSL is mandatory. Defaults to true. * @param {boolean} requireTLS (optional) if TSL is mandatory. Defaults to true.
*/ */
init(options) { init(options) {
this._transport = this._mailer.createTransport({ this._transport = this._mailer.createTransport({
@ -54,31 +55,38 @@ class Email {
} }
/** /**
* A generic method to send an email message via nodemail. * A generic method to send an email message via nodemailer.
* @param {Object} from The sender user id object e.g. { name:'Jon Smith', email:'j@smith.com' } * @param {Object} from sender user id object: { name:'Jon Smith', email:'j@smith.com' }
* @param {Object} to The recipient user id object e.g. { name:'Jon Smith', email:'j@smith.com' } * @param {Object} to recipient user id object: { name:'Jon Smith', email:'j@smith.com' }
* @param {string} subject The message subject * @param {string} subject message subject
* @param {string} text The message plaintext body * @param {string} text message plaintext body template
* @param {string} html The message html body * @param {string} html message html body template
* @yield {Object} The reponse object containing SMTP info * @param {Object} params (optional) nodermailer template parameters
* @yield {Object} reponse object containing SMTP info
*/ */
*send(options) { *send(options) {
let mailOptions = { let template = {
from: {
name: options.from.name,
address: options.from.email
},
to: {
name: options.to.name,
address: options.to.email
},
subject: options.subject, subject: options.subject,
text: options.text, text: options.text,
html: options.html html: options.html
}; };
let sender = {
from: {
name: options.from.name,
address: options.from.email
}
};
let recipient = {
to: {
name: options.to.name,
address: options.to.email
}
};
let params = options.params || {};
try { try {
let info = yield this._transport.sendMail(mailOptions); let sendFn = this._transport.templateSender(template, sender);
let info = yield sendFn(recipient, params);
log.silly('email', 'Email sent.', info); log.silly('email', 'Email sent.', info);
return info; return info;
} catch(error) { } catch(error) {
@ -92,32 +100,45 @@ class Email {
* ownership. If the primary email address is provided, only one email * ownership. If the primary email address is provided, only one email
* will be sent out. Otherwise all of the PGP key's user IDs will be * will be sent out. Otherwise all of the PGP key's user IDs will be
* verified, resulting in an email sent per user ID. * verified, resulting in an email sent per user ID.
* @param {Array} userIds The user id documents containing the nonces * @param {Array} userIds user id documents containing the nonces
* @param {Array} primaryEmail (optional) The user's primary email address * @param {Array} primaryEmail (optional) user's primary email address
* @param {Object} origin Required for links to the keyserver e.g. { protocol:'https', host:'openpgpkeys@example.com' } * @param {Object} origin Required for links to the keyserver: { protocol:'https', host:'openpgpkeys@example.com' }
* @yield {undefined} * @yield {undefined}
*/ */
*sendVerification(options) { *sendVerifyKey(options) {
let primaryEmail = options.primaryEmail, userIds = options.userIds, origin = options.origin; let primaryEmail = options.primaryEmail, userIds = options.userIds, origin = options.origin;
let primaryUserId = userIds.find(uid => uid.email === primaryEmail); let primaryUserId = userIds.find(uid => uid.email === primaryEmail);
if (primaryUserId) { // send only one email to the primary user id if (primaryUserId) { // send only one email to the primary user id
return yield this._sendVerificationHelper(primaryUserId, origin); return yield this._sendVerifyKeyHelper(primaryUserId, origin);
} }
for (let uid of userIds) { for (let uid of userIds) {
yield this._sendVerificationHelper(uid, origin); yield this._sendVerifyKeyHelper(uid, origin);
} }
} }
/** /**
* Help method to send a verification message * Helper function to send a verification message
* @param {Object} userId The user id document * @param {Object} userId user id document
* @param {Object} origin The origin of the server * @param {Object} origin origin of the server
* @yield {Object} The send response from the SMTP server * @yield {Object} send response from the SMTP server
*/ */
*_sendVerificationHelper(userId, origin) { *_sendVerifyKeyHelper(userId, origin) {
let message = this._createVerifyMessage(userId, origin); let msg = {
from: this._sender,
to: userId,
subject: message.verifyKey.subject,
text: message.verifyKey.text,
html: message.verifyKey.html,
params: {
name: userId.name,
protocol: origin.protocol,
host: origin.host,
keyid: encodeURIComponent(userId.keyid),
nonce: encodeURIComponent(userId.nonce)
}
};
try { try {
let info = yield this.send(message); let info = yield this.send(msg);
if (!this._checkResponse(info)) { if (!this._checkResponse(info)) {
log.warn('email', 'Verification mail may not have been received.', info); log.warn('email', 'Verification mail may not have been received.', info);
} }
@ -127,34 +148,11 @@ class Email {
} }
} }
/**
* Helper function to create a verification message object.
* @param {Object} userId The user id document
* @param {Object} origin The origin of the server
* @return {Object} The message object
*/
_createVerifyMessage(userId, origin) {
let verifyLink = origin.protocol + '://' + origin.host +
'/api/v1/verify/?keyid=' + encodeURIComponent(userId.keyid) +
'&nonce=' + encodeURIComponent(userId.nonce);
let text = `Hey${userId.name ? ' ' + userId.name : ''},
please click here to verify your key: ${verifyLink}
`;
return {
from: this._sender,
to: userId,
subject: 'Verify Your Key',
text: text
};
}
/** /**
* Check if the message was sent successfully according to SMTP * Check if the message was sent successfully according to SMTP
* reply codes: http://www.supermailer.de/smtp_reply_codes.htm * reply codes: http://www.supermailer.de/smtp_reply_codes.htm
* @param {Object} info The info object return from nodemailer * @param {Object} info info object return from nodemailer
* @return {boolean} If the message was received by the user * @return {boolean} if the message was received by the user
*/ */
_checkResponse(info) { _checkResponse(info) {
return /^2/.test(info.response); return /^2/.test(info.response);

7
src/dao/message.json Normal file
View File

@ -0,0 +1,7 @@
{
"verifyKey": {
"subject": "Verify Your Key",
"text": "Hello {{name}},\n\nplease click here to verify your key: {{protocol}}://{{host}}/api/v1/verify/?keyid={{keyid}}&nonce={{nonce}}",
"html": ""
}
}

View File

@ -48,7 +48,7 @@ describe('Email Integration Tests', function() {
}); });
}); });
describe("sendVerification", function() { describe("sendVerifyKey", function() {
it('should work', function *() { it('should work', function *() {
let options = { let options = {
userIds: [{ userIds: [{
@ -63,7 +63,7 @@ describe('Email Integration Tests', function() {
host: 'localhost:' + config.server.port host: 'localhost:' + config.server.port
} }
}; };
yield email.sendVerification(options); yield email.sendVerifyKey(options);
}); });
}); });