add nodemailer + templates
This commit is contained in:
parent
12232dbe16
commit
b78395b851
|
@ -39,5 +39,12 @@ var config = {
|
||||||
url: 'https://test-insight.bitpay.com:443',
|
url: 'https://test-insight.bitpay.com:443',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
email: {
|
||||||
|
service: 'Gmail',
|
||||||
|
auth: {
|
||||||
|
user: '',
|
||||||
|
pass: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
module.exports = config;
|
module.exports = config;
|
||||||
|
|
|
@ -6,28 +6,25 @@ var async = require('async');
|
||||||
var log = require('npmlog');
|
var log = require('npmlog');
|
||||||
log.debug = log.verbose;
|
log.debug = log.verbose;
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
|
var nodemailer = require('nodemailer');
|
||||||
|
|
||||||
var Model = require('./model');
|
var Model = require('./model');
|
||||||
|
|
||||||
var EMAIL_TYPES = {
|
var EMAIL_TYPES = {
|
||||||
'NewTxProposal': {
|
'NewTxProposal': {
|
||||||
filename: 'new_tx_proposal',
|
filename: 'new_tx_proposal',
|
||||||
notifyCreator: false,
|
|
||||||
notifyDoer: false,
|
notifyDoer: false,
|
||||||
},
|
},
|
||||||
'NewOutgoingTx': {
|
'NewOutgoingTx': {
|
||||||
filename: 'new_outgoing_tx',
|
filename: 'new_outgoing_tx',
|
||||||
notifyCreator: true,
|
|
||||||
notifyDoer: true,
|
notifyDoer: true,
|
||||||
},
|
},
|
||||||
'NewIncomingTx': {
|
'NewIncomingTx': {
|
||||||
filename: 'new_incoming_tx',
|
filename: 'new_incoming_tx',
|
||||||
notifyCreator: true,
|
|
||||||
notifyDoer: true,
|
notifyDoer: true,
|
||||||
},
|
},
|
||||||
'TxProposalFinallyRejected': {
|
'TxProposalFinallyRejected': {
|
||||||
filename: 'txp_finally_rejected',
|
filename: 'txp_finally_rejected',
|
||||||
notifyCreator: true,
|
|
||||||
notifyDoer: false,
|
notifyDoer: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -36,10 +33,17 @@ var EMAIL_TYPES = {
|
||||||
function EmailService(opts) {
|
function EmailService(opts) {
|
||||||
this.storage = opts.storage;
|
this.storage = opts.storage;
|
||||||
this.lock = opts.lock;
|
this.lock = opts.lock;
|
||||||
|
this.mailer = opts.mailer || nodemailer.createTransport(opts.email);
|
||||||
|
$.checkState(this.mailer);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: cache for X minutes
|
||||||
EmailService.prototype._readTemplate = function(filename, cb) {
|
EmailService.prototype._readTemplate = function(filename, cb) {
|
||||||
fs.readFile(__dirname + '/templates/' + filename + '.plain', 'utf8', function(err, template) {
|
fs.readFile(__dirname + '/templates/' + filename + '.plain', 'utf8', function(err, template) {
|
||||||
|
if (err) {
|
||||||
|
log.error('Could not read template file ' + filename, err);
|
||||||
|
return cb(err);
|
||||||
|
}
|
||||||
var lines = template.split('\n');
|
var lines = template.split('\n');
|
||||||
return cb(null, {
|
return cb(null, {
|
||||||
subject: _.template(lines[0]),
|
subject: _.template(lines[0]),
|
||||||
|
@ -50,20 +54,20 @@ EmailService.prototype._readTemplate = function(filename, cb) {
|
||||||
|
|
||||||
EmailService.prototype._applyTemplate = function(template, data, cb) {
|
EmailService.prototype._applyTemplate = function(template, data, cb) {
|
||||||
var result = _.mapValues(template, function(t) {
|
var result = _.mapValues(template, function(t) {
|
||||||
// TODO: If this throws exception, log and abort email generation
|
try {
|
||||||
return t(data);
|
return t(data);
|
||||||
|
} catch (e) {
|
||||||
|
log.error('Could not apply data to template', e);
|
||||||
|
return cb(e);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return cb(null, result);
|
return cb(null, result);
|
||||||
};
|
};
|
||||||
|
|
||||||
EmailService.prototype._generateFromNotification = function(notification, cb) {
|
EmailService.prototype._getEmailAddresses = function(walletId, cb) {
|
||||||
var self = this;
|
self.storage.fetchPreferences(walletId, null, function(err, preferences) {
|
||||||
|
if (err) return cb(err);
|
||||||
var emailType = EMAIL_TYPES[notification.type];
|
if (_.isEmpty(preferences)) return cb(null, {});
|
||||||
if (!emailType) return cb();
|
|
||||||
|
|
||||||
self.storage.fetchPreferences(notification.walletId, null, function(err, preferences) {
|
|
||||||
if (_.isEmpty(preferences)) return cb();
|
|
||||||
|
|
||||||
var addressesByCopayer = _.reduce(preferences, function(memo, p) {
|
var addressesByCopayer = _.reduce(preferences, function(memo, p) {
|
||||||
if (p.email) {
|
if (p.email) {
|
||||||
|
@ -72,40 +76,70 @@ EmailService.prototype._generateFromNotification = function(notification, cb) {
|
||||||
return memo;
|
return memo;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
if (_.isEmpty(addressesByCopayer)) return cb();
|
return cb(null, addressesByCopayer);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
self._readTemplate(emailType.filename, function(err, template) {
|
EmailService.prototype._send = function(email, cb) {
|
||||||
if (err) return cb(err);
|
var self = this;
|
||||||
|
|
||||||
self._applyTemplate(template, notification.data, function(err, content) {
|
var mailOptions = {
|
||||||
if (err) return cb(err);
|
from: email.from,
|
||||||
|
to: email.to,
|
||||||
|
subject: email.subject,
|
||||||
|
text: email.body,
|
||||||
|
};
|
||||||
|
self.mailer.sendMail(mailOptions, function(err, result) {
|
||||||
|
if (err) {
|
||||||
|
log.error('An error occurred when trying to send email to ' + email.to, err);
|
||||||
|
return cb(err);
|
||||||
|
}
|
||||||
|
log.debug('Message sent: ', result || '');
|
||||||
|
return cb(err, result);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
_.each(addressesByCopayer, function(emailAddress, copayerId) {
|
EmailService.prototype._generateFromNotification = function(notification, cb) {
|
||||||
var email = Model.Email.create({
|
var self = this;
|
||||||
walletId: notification.walletId,
|
|
||||||
copayerId: copayerId,
|
var emailType = EMAIL_TYPES[notification.type];
|
||||||
to: emailAddress,
|
if (!emailType) return cb();
|
||||||
subject: content.subject,
|
|
||||||
body: content.body,
|
var emailByCopayer;
|
||||||
});
|
|
||||||
self.storage.storeEmail(email, function(err) {
|
async.waterfall([
|
||||||
return cb(err);
|
|
||||||
});
|
function(next) {
|
||||||
|
self._getEmailAddresses(notification.walletId, next);
|
||||||
|
},
|
||||||
|
function(emailAddresses, next) {
|
||||||
|
if (_.isEmpty(emailAddresses)) return cb();
|
||||||
|
emailByCopayer = emailAddresses;
|
||||||
|
self._readTemplate(emailType.filename, next);
|
||||||
|
},
|
||||||
|
function(template, next) {
|
||||||
|
self._applyTemplate(template, notification.data, next);
|
||||||
|
},
|
||||||
|
function(content, next) {
|
||||||
|
_.each(emailByCopayer, function(address, copayerId) {
|
||||||
|
var email = Model.Email.create({
|
||||||
|
walletId: notification.walletId,
|
||||||
|
copayerId: copayerId,
|
||||||
|
to: address,
|
||||||
|
subject: content.subject,
|
||||||
|
body: content.body,
|
||||||
|
});
|
||||||
|
self.storage.storeEmail(email, function(err) {
|
||||||
|
return next(err, email);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
},
|
||||||
});
|
function(email, next) {
|
||||||
|
self._send(email, next);
|
||||||
|
},
|
||||||
|
], cb);
|
||||||
|
|
||||||
return cb();
|
return cb();
|
||||||
};
|
};
|
||||||
|
|
||||||
EmailService.prototype._send = function(cb) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
this.lock.runLocked('emails', cb, function() {
|
|
||||||
//self._fetchUnsentEmails();
|
|
||||||
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = EmailService;
|
module.exports = EmailService;
|
||||||
|
|
|
@ -12,6 +12,7 @@ Email.create = function(opts) {
|
||||||
x.createdOn = Math.floor(Date.now() / 1000);
|
x.createdOn = Math.floor(Date.now() / 1000);
|
||||||
x.walletId = opts.walletId;
|
x.walletId = opts.walletId;
|
||||||
x.copayerId = opts.copayerId;
|
x.copayerId = opts.copayerId;
|
||||||
|
x.from = opts.from;
|
||||||
x.to = opts.to;
|
x.to = opts.to;
|
||||||
x.subject = opts.subject;
|
x.subject = opts.subject;
|
||||||
x.body = opts.body;
|
x.body = opts.body;
|
||||||
|
@ -27,6 +28,7 @@ Email.fromObj = function(obj) {
|
||||||
x.createdOn = obj.createdOn;
|
x.createdOn = obj.createdOn;
|
||||||
x.walletId = obj.walletId;
|
x.walletId = obj.walletId;
|
||||||
x.copayerId = obj.copayerId;
|
x.copayerId = obj.copayerId;
|
||||||
|
x.from = obj.from;
|
||||||
x.to = obj.to;
|
x.to = obj.to;
|
||||||
x.subject = obj.subject;
|
x.subject = obj.subject;
|
||||||
x.body = obj.body;
|
x.body = obj.body;
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
copay@copay.io
|
||||||
|
[Copay] Funds received!
|
||||||
|
Funds received on your wallet.
|
|
@ -0,0 +1,3 @@
|
||||||
|
copay@copay.io
|
||||||
|
[Copay] Transaction broadcasted!
|
||||||
|
A transaction has been broadcasted.
|
|
@ -1,2 +1,3 @@
|
||||||
New transaction proposal!
|
copay@copay.io
|
||||||
|
[Copay] New transaction proposal!
|
||||||
A new transaction proposal has been created by another copayer.
|
A new transaction proposal has been created by another copayer.
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
copay@copay.io
|
||||||
|
[Copay] A transaction proposal was rejected
|
||||||
|
A transaction proposal was rejected by your copayers.
|
|
@ -32,6 +32,7 @@
|
||||||
"mocha-lcov-reporter": "0.0.1",
|
"mocha-lcov-reporter": "0.0.1",
|
||||||
"mongodb": "^2.0.27",
|
"mongodb": "^2.0.27",
|
||||||
"morgan": "*",
|
"morgan": "*",
|
||||||
|
"nodemailer": "^1.3.4",
|
||||||
"npmlog": "^0.1.1",
|
"npmlog": "^0.1.1",
|
||||||
"preconditions": "^1.0.7",
|
"preconditions": "^1.0.7",
|
||||||
"read": "^1.0.5",
|
"read": "^1.0.5",
|
||||||
|
|
Loading…
Reference in New Issue