${user.name} moved ` + + `${card.name} ` + + `from ${action.data.fromList.name} to ${action.data.toList.name} ` + + `on ${board.name}
`, + }; + break; + case Action.Types.COMMENT_CARD: + emailData = { + subject: `${user.name} left a new comment to ${card.name} on ${board.name}`, + html: + `${user.name} left a new comment to ` + + `${card.name} ` + + `on ${board.name}
` + + `${action.data.text}
`, + }; + break; + default: + return; + } + + await sails.helpers.utils.sendEmail.with({ + ...emailData, + to: notifiableUser.email, + }); +}; + module.exports = { inputs: { values: { @@ -21,6 +55,18 @@ module.exports = { custom: valuesValidator, required: true, }, + user: { + type: 'ref', + required: true, + }, + board: { + type: 'ref', + required: true, + }, + card: { + type: 'ref', + required: true, + }, }, async fn(inputs) { @@ -40,6 +86,17 @@ module.exports = { item: notification, }); + if (sails.hooks.smtp.isActive()) { + let notifiableUser; + if (values.user) { + notifiableUser = values.user; + } else { + notifiableUser = await sails.helpers.users.getOne(notification.userId); + } + + buildAndSendEmail(inputs.user, inputs.board, inputs.card, values.action, notifiableUser); + } + return notification; }, }; diff --git a/server/api/helpers/utils/send-email.js b/server/api/helpers/utils/send-email.js new file mode 100644 index 0000000..5d9be72 --- /dev/null +++ b/server/api/helpers/utils/send-email.js @@ -0,0 +1,31 @@ +module.exports = { + inputs: { + to: { + type: 'string', + required: true, + }, + subject: { + type: 'string', + required: true, + }, + html: { + type: 'string', + required: true, + }, + }, + + async fn(inputs) { + const transporter = sails.hooks.smtp.getTransporter(); // TODO: check if active? + + try { + const info = await transporter.sendMail({ + ...inputs, + from: sails.config.custom.smtpFrom, + }); + + sails.log.info('Email sent: %s', info.messageId); + } catch (error) { + sails.log.error(error); + } + }, +}; diff --git a/server/api/hooks/smtp/index.js b/server/api/hooks/smtp/index.js new file mode 100644 index 0000000..4cd0503 --- /dev/null +++ b/server/api/hooks/smtp/index.js @@ -0,0 +1,35 @@ +const nodemailer = require('nodemailer'); + +module.exports = function smtpServiceHook(sails) { + let transporter = null; + + return { + /** + * Runs when this Sails app loads/lifts. + */ + + async initialize() { + if (sails.config.custom.smtpHost) { + transporter = nodemailer.createTransport({ + pool: true, + host: sails.config.custom.smtpHost, + port: sails.config.custom.smtpPort, + secure: sails.config.custom.smtpSecure, + auth: sails.config.custom.smtpUser && { + user: sails.config.custom.smtpUser, + pass: sails.config.custom.smtpPassword, + }, + }); + sails.log.info('SMTP hook has been loaded successfully'); + } + }, + + getTransporter() { + return transporter; + }, + + isActive() { + return transporter !== null; + }, + }; +}; diff --git a/server/config/custom.js b/server/config/custom.js index f074575..7647324 100644 --- a/server/config/custom.js +++ b/server/config/custom.js @@ -34,6 +34,13 @@ module.exports.custom = { defaultAdminEmail: process.env.DEFAULT_ADMIN_EMAIL && process.env.DEFAULT_ADMIN_EMAIL.toLowerCase(), + smtpHost: process.env.SMTP_HOST, + smtpPort: process.env.SMTP_PORT || 587, + smtpSecure: process.env.SMTP_SECURE === 'true', + smtpUser: process.env.SMTP_USER, + smtpPassword: process.env.SMTP_PASSWORD, + smtpFrom: process.env.SMTP_FROM, + oidcIssuer: process.env.OIDC_ISSUER, oidcClientId: process.env.OIDC_CLIENT_ID, oidcClientSecret: process.env.OIDC_CLIENT_SECRET, diff --git a/server/package-lock.json b/server/package-lock.json index 05527a7..93453a8 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -15,6 +15,7 @@ "lodash": "^4.17.21", "moment": "^2.29.4", "move-file": "^2.1.0", + "nodemailer": "^6.9.12", "openid-client": "^5.6.1", "rimraf": "^5.0.5", "sails": "^1.5.7", @@ -5257,6 +5258,14 @@ } } }, + "node_modules/nodemailer": { + "version": "6.9.12", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.12.tgz", + "integrity": "sha512-pnLo7g37Br3jXbF0bl5DekBJihm2q+3bB3l2o/B060sWmb5l+VqeScAQCBqaQ+5ezRZFzW5SciZNGdRDEbq89w==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/nodemon": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.1.tgz", @@ -12852,6 +12861,11 @@ "whatwg-url": "^5.0.0" } }, + "nodemailer": { + "version": "6.9.12", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.12.tgz", + "integrity": "sha512-pnLo7g37Br3jXbF0bl5DekBJihm2q+3bB3l2o/B060sWmb5l+VqeScAQCBqaQ+5ezRZFzW5SciZNGdRDEbq89w==" + }, "nodemon": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.1.tgz", diff --git a/server/package.json b/server/package.json index 59bb2f7..67fc5e0 100644 --- a/server/package.json +++ b/server/package.json @@ -36,6 +36,7 @@ "lodash": "^4.17.21", "moment": "^2.29.4", "move-file": "^2.1.0", + "nodemailer": "^6.9.12", "openid-client": "^5.6.1", "rimraf": "^5.0.5", "sails": "^1.5.7",