email service
This commit is contained in:
parent
39b254b163
commit
12232dbe16
|
@ -0,0 +1,111 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var _ = require('lodash');
|
||||||
|
var $ = require('preconditions').singleton();
|
||||||
|
var async = require('async');
|
||||||
|
var log = require('npmlog');
|
||||||
|
log.debug = log.verbose;
|
||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
|
var Model = require('./model');
|
||||||
|
|
||||||
|
var EMAIL_TYPES = {
|
||||||
|
'NewTxProposal': {
|
||||||
|
filename: 'new_tx_proposal',
|
||||||
|
notifyCreator: false,
|
||||||
|
notifyDoer: false,
|
||||||
|
},
|
||||||
|
'NewOutgoingTx': {
|
||||||
|
filename: 'new_outgoing_tx',
|
||||||
|
notifyCreator: true,
|
||||||
|
notifyDoer: true,
|
||||||
|
},
|
||||||
|
'NewIncomingTx': {
|
||||||
|
filename: 'new_incoming_tx',
|
||||||
|
notifyCreator: true,
|
||||||
|
notifyDoer: true,
|
||||||
|
},
|
||||||
|
'TxProposalFinallyRejected': {
|
||||||
|
filename: 'txp_finally_rejected',
|
||||||
|
notifyCreator: true,
|
||||||
|
notifyDoer: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function EmailService(opts) {
|
||||||
|
this.storage = opts.storage;
|
||||||
|
this.lock = opts.lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
EmailService.prototype._readTemplate = function(filename, cb) {
|
||||||
|
fs.readFile(__dirname + '/templates/' + filename + '.plain', 'utf8', function(err, template) {
|
||||||
|
var lines = template.split('\n');
|
||||||
|
return cb(null, {
|
||||||
|
subject: _.template(lines[0]),
|
||||||
|
body: _.template(_.rest(lines).join('\n')),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
EmailService.prototype._applyTemplate = function(template, data, cb) {
|
||||||
|
var result = _.mapValues(template, function(t) {
|
||||||
|
// TODO: If this throws exception, log and abort email generation
|
||||||
|
return t(data);
|
||||||
|
});
|
||||||
|
return cb(null, result);
|
||||||
|
};
|
||||||
|
|
||||||
|
EmailService.prototype._generateFromNotification = function(notification, cb) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
var emailType = EMAIL_TYPES[notification.type];
|
||||||
|
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) {
|
||||||
|
if (p.email) {
|
||||||
|
memo[p.copayerId] = p.email;
|
||||||
|
}
|
||||||
|
return memo;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
if (_.isEmpty(addressesByCopayer)) return cb();
|
||||||
|
|
||||||
|
self._readTemplate(emailType.filename, function(err, template) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
|
||||||
|
self._applyTemplate(template, notification.data, function(err, content) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
|
||||||
|
_.each(addressesByCopayer, function(emailAddress, copayerId) {
|
||||||
|
var email = Model.Email.create({
|
||||||
|
walletId: notification.walletId,
|
||||||
|
copayerId: copayerId,
|
||||||
|
to: emailAddress,
|
||||||
|
subject: content.subject,
|
||||||
|
body: content.body,
|
||||||
|
});
|
||||||
|
self.storage.storeEmail(email, function(err) {
|
||||||
|
return cb(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return cb();
|
||||||
|
};
|
||||||
|
|
||||||
|
EmailService.prototype._send = function(cb) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.lock.runLocked('emails', cb, function() {
|
||||||
|
//self._fetchUnsentEmails();
|
||||||
|
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = EmailService;
|
|
@ -18,13 +18,19 @@ var Lock = require('./lock');
|
||||||
var Storage = require('./storage');
|
var Storage = require('./storage');
|
||||||
var MessageBroker = require('./messagebroker');
|
var MessageBroker = require('./messagebroker');
|
||||||
var BlockchainExplorer = require('./blockchainexplorer');
|
var BlockchainExplorer = require('./blockchainexplorer');
|
||||||
|
var EmailService = require('./emailservice');
|
||||||
|
|
||||||
var Model = require('./model');
|
var Model = require('./model');
|
||||||
var Wallet = Model.Wallet;
|
var Wallet = Model.Wallet;
|
||||||
|
|
||||||
var initialized = false;
|
var initialized = false;
|
||||||
var lock, storage, blockchainExplorer, blockchainExplorerOpts;
|
|
||||||
|
var lock;
|
||||||
|
var storage;
|
||||||
|
var blockchainExplorer;
|
||||||
|
var blockchainExplorerOpts;
|
||||||
var messageBroker;
|
var messageBroker;
|
||||||
|
var emailService;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,6 +47,7 @@ function WalletService() {
|
||||||
this.blockchainExplorerOpts = blockchainExplorerOpts;
|
this.blockchainExplorerOpts = blockchainExplorerOpts;
|
||||||
this.messageBroker = messageBroker;
|
this.messageBroker = messageBroker;
|
||||||
this.notifyTicker = 0;
|
this.notifyTicker = 0;
|
||||||
|
this.emailService = emailService;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,20 +65,28 @@ WalletService.initialize = function(opts, cb) {
|
||||||
blockchainExplorer = opts.blockchainExplorer;
|
blockchainExplorer = opts.blockchainExplorer;
|
||||||
blockchainExplorerOpts = opts.blockchainExplorerOpts;
|
blockchainExplorerOpts = opts.blockchainExplorerOpts;
|
||||||
|
|
||||||
if (initialized)
|
if (initialized) return cb();
|
||||||
return cb();
|
|
||||||
|
|
||||||
function initStorage(cb) {
|
function initStorage(cb) {
|
||||||
if (opts.storage) {
|
if (opts.storage) {
|
||||||
storage = opts.storage;
|
storage = opts.storage;
|
||||||
return cb();
|
return cb();
|
||||||
}
|
} else {
|
||||||
var newStorage = new Storage();
|
var newStorage = new Storage();
|
||||||
newStorage.connect(opts.storageOpts, function(err) {
|
newStorage.connect(opts.storageOpts, function(err) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
storage = newStorage;
|
storage = newStorage;
|
||||||
return cb();
|
return cb();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function initEmailService(cb) {
|
||||||
|
emailService = new EmailService({
|
||||||
|
lock: lock,
|
||||||
|
storage: storage,
|
||||||
|
});
|
||||||
|
return cb();
|
||||||
};
|
};
|
||||||
|
|
||||||
function initMessageBroker(cb) {
|
function initMessageBroker(cb) {
|
||||||
|
@ -91,11 +106,15 @@ WalletService.initialize = function(opts, cb) {
|
||||||
function(next) {
|
function(next) {
|
||||||
initMessageBroker(next);
|
initMessageBroker(next);
|
||||||
},
|
},
|
||||||
|
function(next) {
|
||||||
|
initEmailService(next);
|
||||||
|
},
|
||||||
], function(err) {
|
], function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
log.error('Could not initialize', err);
|
log.error('Could not initialize', err);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
], function() {
|
||||||
initialized = true;
|
initialized = true;
|
||||||
return cb();
|
return cb();
|
||||||
});
|
});
|
||||||
|
@ -331,10 +350,13 @@ WalletService.prototype._notify = function(type, data, opts, cb) {
|
||||||
creatorId: opts.isGlobal ? null : copayerId,
|
creatorId: opts.isGlobal ? null : copayerId,
|
||||||
walletId: walletId,
|
walletId: walletId,
|
||||||
});
|
});
|
||||||
this.storage.storeNotification(walletId, n, function() {
|
|
||||||
self.messageBroker.send(n);
|
this.storage.storeNotification(walletId, notification, function() {
|
||||||
|
self.messageBroker.send(notification);
|
||||||
|
self.emailService._generateFromNotification(notification, function() {
|
||||||
if (cb) return cb();
|
if (cb) return cb();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -356,13 +356,25 @@ Storage.prototype.fetchAddress = function(address, cb) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Storage.prototype.fetchPreferences = function(walletId, copayerId, cb) {
|
Storage.prototype.fetchPreferences = function(walletId, copayerId, cb) {
|
||||||
this.db.collection(collections.PREFERENCES).findOne({
|
this.db.collection(collections.PREFERENCES).find({
|
||||||
walletId: walletId,
|
walletId: walletId,
|
||||||
copayerId: copayerId,
|
}).toArray(function(err, result) {
|
||||||
}, function(err, result) {
|
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
|
if (copayerId) {
|
||||||
|
result = _.find(result, {
|
||||||
|
copayerId: copayerId
|
||||||
|
});
|
||||||
|
}
|
||||||
if (!result) return cb();
|
if (!result) return cb();
|
||||||
return cb(null, Model.Preferences.fromObj(result));
|
|
||||||
|
var preferences = _.map([].concat(result), function(r) {
|
||||||
|
return Model.Preferences.fromObj(r);
|
||||||
|
});
|
||||||
|
if (copayerId) {
|
||||||
|
preferences = preferences[0];
|
||||||
|
}
|
||||||
|
return cb(null, preferences);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -379,7 +391,7 @@ Storage.prototype.storePreferences = function(preferences, cb) {
|
||||||
Storage.prototype.storeEmail = function(email, cb) {
|
Storage.prototype.storeEmail = function(email, cb) {
|
||||||
this.db.collection(collections.EMAIL_QUEUE).update({
|
this.db.collection(collections.EMAIL_QUEUE).update({
|
||||||
id: email.id,
|
id: email.id,
|
||||||
}, txp, {
|
}, email, {
|
||||||
w: 1,
|
w: 1,
|
||||||
upsert: true,
|
upsert: true,
|
||||||
}, cb);
|
}, cb);
|
||||||
|
@ -388,7 +400,7 @@ Storage.prototype.storeEmail = function(email, cb) {
|
||||||
Storage.prototype.fetchUnsentEmails = function(cb) {
|
Storage.prototype.fetchUnsentEmails = function(cb) {
|
||||||
this.db.collection(collections.EMAIL_QUEUE).find({
|
this.db.collection(collections.EMAIL_QUEUE).find({
|
||||||
status: 'pending',
|
status: 'pending',
|
||||||
}, function(err, result) {
|
}).toArray(function(err, result) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
if (!result) return cb();
|
if (!result) return cb();
|
||||||
return cb(null, Model.Email.fromObj(result));
|
return cb(null, Model.Email.fromObj(result));
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
New transaction proposal!
|
||||||
|
A new transaction proposal has been created by another copayer.
|
Loading…
Reference in New Issue