import { AbstractModule } from 'adapt-authoring-core'
import nodemailer from 'nodemailer'
/**
* Mailer Module
* @memberof mailer
* @extends {AbstractModule}
*/
class MailerModule extends AbstractModule {
/** @override */
async init () {
/**
* Reference to the isEnabled config value
* @type {Boolean}
*/
this.isEnabled = this.getConfig('isEnabled')
/**
* Reference to the connectionUrl config value
* @type {String}
*/
this.connectionUrl = this.getConfig('connectionUrl')
/**
* The Nodemailer SMTP transport instance
* @type {Nodemailer~Transport}
*/
this.transporter = undefined
if (this.isEnabled) {
this.transporter = nodemailer.createTransport(this.connectionUrl)
await this.testConnection()
}
// note we still enable the API route if mailer is disabled to allow for testing
const [auth, server] = await this.app.waitForModule('auth', 'server')
const router = server.api.createChildRouter('mailer')
router.addRoute({
route: '/test',
internal: true,
handlers: { post: this.testEmailHandler.bind(this) },
meta: {
post: {
summary: 'Send test email',
responses: { 200: {} }
}
}
})
auth.unsecureRoute(`${router.path}/test`, 'post')
}
/**
* Checks the provided SMTP settings using nodemailer.verify.
* @return {Promise}
*/
async testConnection () {
try {
await this.transporter.verify()
this.log('info', 'SMTP connection verified successfully')
} catch (e) {
this.log('warn', `SMTP connection test failed, ${e}`)
}
}
/**
* Sends an email
* @param {object} data The message data
* @param {object} data.to Comma separated list or an array of recipients email addresses that will appear on the To: field
* @param {object} data.subject The subject of the email
* @param {object} data.text The plain text version of the message as an unicode string
* @param {object} data.html The HTML version of the message as a unicode string
* @return {Promise}
*/
async send (data, options = {}) {
if (!this.isEnabled) {
if (options.strict) throw this.app.errors.MAIL_NOT_ENABLED
else return this.log('warn', 'could not send email, SMTP is not enabled')
}
if (!data.from) {
data.from = this.getConfig('defaultSenderAddress')
}
try {
const jsonschema = await this.app.waitForModule('jsonschema')
const schema = await jsonschema.getSchema('maildata')
await schema.validate(data)
await this.transporter.sendMail(data)
this.log('info', 'email sent successfully')
} catch (e) {
throw this.app.errors.MAIL_SEND_FAILED.setData({ email: data.to, error: e })
}
}
/**
* Sends a test email. Can only be called from localhost
* @param {external:ExpressRequest} req The client request object
* @param {external:ExpressResponse} res The server response object
* @param {Function} next The callback function
*/
async testEmailHandler (req, res, next) {
if (!this.isEnabled) {
throw this.app.errors.MAIL_NOT_ENABLED
}
const appUrl = (await this.app.waitForModule('server')).getConfig('url')
try {
await this.send({
to: req.body.email,
subject: 'Adapt authoring tool: email test',
text: `Hello world!\n\nThis is a test email from ${appUrl}.\n\nRegards,\nTeam Adapt.`
}, { strict: true })
res.status(200).end()
} catch (e) {
return next(e)
}
}
}
export default MailerModule