const async = require('async'); const nodemailer = require('nodemailer'); const Email = require('email-templates'); const fs = require('mz/fs'); const path = require('path'); const getPaths = require('get-paths'); const AbstractIntervalWorker = require('./abstractintervalworker'); const NotificationType = require('../repository/notification').NotificationType; const _ = require('lodash'); function humanFileSize(bytes, si) { var thresh = si ? 1000 : 1024; if(Math.abs(bytes) < thresh) { return bytes + ' B'; } var units = si ? ['kB','MB','GB','TB','PB','EB','ZB','YB'] : ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB']; var u = -1; do { bytes /= thresh; ++u; } while(Math.abs(bytes) >= thresh && u < units.length - 1); return bytes.toFixed(1) + ' ' + units[u]; } class NotificationWorker extends AbstractIntervalWorker { constructor(repository) { super(); this.repository = repository; } async tick() { var self = this; var notifications = await self.repository.notifications.list(); return new Promise((resolve, reject) => { async.eachOfSeries(notifications, async (item) => { await self.handleNotification(item) }, (err) => { if (err) reject(err); else resolve(); }); }) } async handleNotification(notification) { let self = this; let user = await self.repository.users.get(notification.userId); if (user === null || !user.email) return; switch (notification.type) { case NotificationType.UploadComplete: await self.sendUploadNotification(notification, user); break; case NotificationType.CodeMoved: await self.sendCodeMovedNotification(notification, user); break; } } getDefaultLocals(user) { return { // Data user: user, // Configuration adminUrl: config.notifications.adminUrl, // Helper functions humanFileSize: humanFileSize } } async sendUploadNotification(notification, user) { let self = this; let locals = self.getDefaultLocals(user); locals.upload = await self.repository.uploads.get(notification.uploadId); if (locals.upload === null) return; await self.sendNotification(notification, user, locals, 'uploadnotification'); } async sendCodeMovedNotification(notification, user) { let self = this; let locals = self.getDefaultLocals(user); locals.uploads = await self.repository.uploads.listForCode(notification.codeId); if (locals.uploads === null || locals.uploads.length == 0) return; locals.fileCount = 0; _.forEach(locals.uploads, (upload) => { locals.fileCount += upload.files.length; }); locals.prevUser = { userId: null, name: '' }; if (notification.metadata !== null) { let resolveUser = async (userId) => { if (!userId) return null; let user = await self.repository.users.get(userId); if (user !== null) { return { userId: userId, name: user.name } } }; locals.prevUser = await resolveUser(notification.metadata.prevUserId); locals.assignUser = await resolveUser(notification.metadata.assignUserId); } await self.sendNotification(notification, user, locals, 'movednotification'); } async sendNotification(notification, user, locals, template) { let self = this; return new Promise((resolve, reject) => { const email = new Email({ message: { from: config.notifications.mail.from }, send: true, preview: false, transport: nodemailer.createTransport(config.notifications.mail.transport), views: { options: { extension: 'ejs' } } }); // Override the default template locator with one that checks // the custom folder first, then falls back to the configured folder let getTemplatePathFromRoot = function(root, view, ext) { return new Promise(async (resolve, reject) => { try { const paths = await getPaths( root, view, ext ); const filePath = path.resolve(root, paths.rel); resolve({ filePath, paths }); } catch (err) { reject(err); } }); }; email.getTemplatePath = async function(view) { try { return await getTemplatePathFromRoot(path.resolve('custom/emails'), view, this.config.views.options.extension); } catch (err) { return await getTemplatePathFromRoot(this.config.views.root, view, this.config.views.options.extension); } }; email .send({ template: template, message: { to: user.email }, locals: locals }) .then(async () => { await self.repository.notifications.delete(notification.id); console.log('Notification sent to: ' + user.email); resolve(); }) .catch(async (error) => { console.log(error); notification.attempt++; if (notification.attempt > config.notifications.maxAttempts) await self.repository.notifications.delete(notification.id); else await self.repository.notifications.update(notification); resolve(); }); }); } } module.exports = NotificationWorker;