|
|
@ -7,6 +7,7 @@ const { getSettingsTemplateContext } = require("./templates") |
|
|
const { processString } = require("@budibase/string-templates") |
|
|
const { processString } = require("@budibase/string-templates") |
|
|
const { getResetPasswordCode, getInviteCode } = require("../utilities/redis") |
|
|
const { getResetPasswordCode, getInviteCode } = require("../utilities/redis") |
|
|
|
|
|
|
|
|
|
|
|
const TEST_MODE = false |
|
|
const GLOBAL_DB = StaticDatabases.GLOBAL.name |
|
|
const GLOBAL_DB = StaticDatabases.GLOBAL.name |
|
|
const TYPE = TemplateTypes.EMAIL |
|
|
const TYPE = TemplateTypes.EMAIL |
|
|
|
|
|
|
|
|
@ -14,18 +15,32 @@ const FULL_EMAIL_PURPOSES = [ |
|
|
EmailTemplatePurpose.INVITATION, |
|
|
EmailTemplatePurpose.INVITATION, |
|
|
EmailTemplatePurpose.PASSWORD_RECOVERY, |
|
|
EmailTemplatePurpose.PASSWORD_RECOVERY, |
|
|
EmailTemplatePurpose.WELCOME, |
|
|
EmailTemplatePurpose.WELCOME, |
|
|
|
|
|
EmailTemplatePurpose.CUSTOM, |
|
|
] |
|
|
] |
|
|
|
|
|
|
|
|
function createSMTPTransport(config) { |
|
|
function createSMTPTransport(config) { |
|
|
const options = { |
|
|
let options |
|
|
port: config.port, |
|
|
if (!TEST_MODE) { |
|
|
host: config.host, |
|
|
options = { |
|
|
secure: config.secure || false, |
|
|
port: config.port, |
|
|
auth: config.auth, |
|
|
host: config.host, |
|
|
} |
|
|
secure: config.secure || false, |
|
|
if (config.selfSigned) { |
|
|
auth: config.auth, |
|
|
options.tls = { |
|
|
} |
|
|
rejectUnauthorized: false, |
|
|
if (config.selfSigned) { |
|
|
|
|
|
options.tls = { |
|
|
|
|
|
rejectUnauthorized: false, |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
options = { |
|
|
|
|
|
port: 587, |
|
|
|
|
|
host: "smtp.ethereal.email", |
|
|
|
|
|
secure: false, |
|
|
|
|
|
auth: { |
|
|
|
|
|
user: "don.bahringer@ethereal.email", |
|
|
|
|
|
pass: "yCKSH8rWyUPbnhGYk9", |
|
|
|
|
|
}, |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
return nodemailer.createTransport(options) |
|
|
return nodemailer.createTransport(options) |
|
|
@ -46,10 +61,12 @@ async function getLinkCode(purpose, email, user) { |
|
|
* Builds an email using handlebars and the templates found in the system (default or otherwise). |
|
|
* Builds an email using handlebars and the templates found in the system (default or otherwise). |
|
|
* @param {string} purpose the purpose of the email being built, e.g. invitation, password reset. |
|
|
* @param {string} purpose the purpose of the email being built, e.g. invitation, password reset. |
|
|
* @param {string} email the address which it is being sent to for contextual purposes. |
|
|
* @param {string} email the address which it is being sent to for contextual purposes. |
|
|
* @param {object|null} user If being sent to an existing user then the object can be provided for context. |
|
|
* @param {object} context the context which is being used for building the email (hbs context). |
|
|
|
|
|
* @param {object|null} user if being sent to an existing user then the object can be provided for context. |
|
|
|
|
|
* @param {string|null} contents if using a custom template can supply contents for context. |
|
|
* @return {Promise<string>} returns the built email HTML if all provided parameters were valid. |
|
|
* @return {Promise<string>} returns the built email HTML if all provided parameters were valid. |
|
|
*/ |
|
|
*/ |
|
|
async function buildEmail(purpose, email, user) { |
|
|
async function buildEmail(purpose, email, context, { user, contents } = {}) { |
|
|
// this isn't a full email
|
|
|
// this isn't a full email
|
|
|
if (FULL_EMAIL_PURPOSES.indexOf(purpose) === -1) { |
|
|
if (FULL_EMAIL_PURPOSES.indexOf(purpose) === -1) { |
|
|
throw `Unable to build an email of type ${purpose}` |
|
|
throw `Unable to build an email of type ${purpose}` |
|
|
@ -63,11 +80,9 @@ async function buildEmail(purpose, email, user) { |
|
|
} |
|
|
} |
|
|
base = base.contents |
|
|
base = base.contents |
|
|
body = body.contents |
|
|
body = body.contents |
|
|
|
|
|
context = { |
|
|
// if there is a link code needed this will retrieve it
|
|
|
...context, |
|
|
const code = await getLinkCode(purpose, email, user) |
|
|
contents, |
|
|
const context = { |
|
|
|
|
|
...(await getSettingsTemplateContext(purpose, code)), |
|
|
|
|
|
email, |
|
|
email, |
|
|
user: user || {}, |
|
|
user: user || {}, |
|
|
} |
|
|
} |
|
|
@ -116,27 +131,35 @@ exports.isEmailConfigured = async (groupId = null) => { |
|
|
* @param {object|undefined} user If sending to an existing user the object can be provided, this is used in the context. |
|
|
* @param {object|undefined} user If sending to an existing user the object can be provided, this is used in the context. |
|
|
* @param {string|undefined} from If sending from an address that is not what is configured in the SMTP config. |
|
|
* @param {string|undefined} from If sending from an address that is not what is configured in the SMTP config. |
|
|
* @param {string|undefined} contents If sending a custom email then can supply contents which will be added to it. |
|
|
* @param {string|undefined} contents If sending a custom email then can supply contents which will be added to it. |
|
|
|
|
|
* @param {string|undefined} subject A custom subject can be specified if the config one is not desired. |
|
|
* @return {Promise<object>} returns details about the attempt to send email, e.g. if it is successful; based on |
|
|
* @return {Promise<object>} returns details about the attempt to send email, e.g. if it is successful; based on |
|
|
* nodemailer response. |
|
|
* nodemailer response. |
|
|
*/ |
|
|
*/ |
|
|
exports.sendEmail = async ( |
|
|
exports.sendEmail = async ( |
|
|
email, |
|
|
email, |
|
|
purpose, |
|
|
purpose, |
|
|
{ groupId, user, from, contents } = {} |
|
|
{ groupId, user, from, contents, subject } = {} |
|
|
) => { |
|
|
) => { |
|
|
const db = new CouchDB(GLOBAL_DB) |
|
|
const db = new CouchDB(GLOBAL_DB) |
|
|
const config = await getSmtpConfiguration(db, groupId) |
|
|
let config = await getSmtpConfiguration(db, groupId) || {} |
|
|
if (!config) { |
|
|
if (Object.keys(config).length === 0 && !TEST_MODE) { |
|
|
throw "Unable to find SMTP configuration." |
|
|
throw "Unable to find SMTP configuration." |
|
|
} |
|
|
} |
|
|
const transport = createSMTPTransport(config) |
|
|
const transport = createSMTPTransport(config) |
|
|
|
|
|
// if there is a link code needed this will retrieve it
|
|
|
|
|
|
const code = await getLinkCode(purpose, email, user) |
|
|
|
|
|
const context = await getSettingsTemplateContext(purpose, code) |
|
|
const message = { |
|
|
const message = { |
|
|
from: from || config.from, |
|
|
from: from || config.from, |
|
|
subject: config.subject, |
|
|
subject: await processString(subject || config.subject, context), |
|
|
to: email, |
|
|
to: email, |
|
|
html: await buildEmail(purpose, email, user), |
|
|
html: await buildEmail(purpose, email, context, { user, contents }), |
|
|
|
|
|
} |
|
|
|
|
|
const response = await transport.sendMail(message) |
|
|
|
|
|
if (TEST_MODE) { |
|
|
|
|
|
console.log("Test email URL: " + nodemailer.getTestMessageUrl(response)) |
|
|
} |
|
|
} |
|
|
return transport.sendMail(message) |
|
|
return response |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
|