import ApplicationComponent from "../ApplicationComponent"; import {Express} from "express"; import config from "config"; import SecurityError from "../SecurityError"; import nodemailer, {SentMessageInfo, Transporter} from "nodemailer"; import MailError from "../mail/MailError"; import util from "util"; import {logger} from "../Logger"; import Controller from "../Controller"; import Mail from "../mail/Mail"; import MailViewEngine from "../frontend/MailViewEngine"; import ViewEngine from "../frontend/ViewEngine"; export default class MailComponent extends ApplicationComponent { private transporter?: Transporter; public constructor( private readonly viewEngine: MailViewEngine, ) { super(); } public async checkSecuritySettings(): Promise { if (!config.get('mail.secure')) { throw new SecurityError('Cannot set mail.secure (starttls) to false'); } if (config.get('mail.allow_invalid_tls')) { throw new SecurityError('Cannot set mail.allow_invalid_tls (ignore tls failure) to true'); } } public async start(app: Express): Promise { await this.prepare('Mail connection', async () => { const transporter = nodemailer.createTransport({ host: config.get('mail.host'), port: config.get('mail.port'), requireTLS: config.get('mail.secure'), // STARTTLS auth: { user: config.get('mail.username'), pass: config.get('mail.password'), }, tls: { rejectUnauthorized: !config.get('mail.allow_invalid_tls'), }, }); try { await util.promisify(transporter.verify)(); this.transporter = transporter; } catch (e) { throw new MailError('Connection to mail service unsuccessful.', e); } logger.info(`Mail ready to be distributed via ${config.get('mail.host')}:${config.get('mail.port')}`); }); await this.viewEngine.setup(app, false); } public async stop(): Promise { if (this.transporter) { this.transporter.close(); this.transporter = undefined; } await this.viewEngine.stop(); } public async sendMail(mail: Mail, ...to: string[]): Promise { if (to.length === 0) throw new Error('Cannot send an email to recipient. (to is empty)'); const results = []; for (const destEmail of to) { const template = mail.getTemplate(); const locals = mail.getData(); const options = mail.getOptions(); // Reset options options.html = options.text = undefined; // Set options options.to = destEmail; options.from = { name: config.get('mail.from_name'), address: config.get('mail.from'), }; // Set locals locals.mail_subject = options.subject; locals.mail_to = options.to; locals.mail_link = config.get('public_url') + Controller.route('mail', [template.template], locals); Object.assign(locals, ViewEngine.getGlobals()); // Log logger.debug(`Send mail from ${options.from.address} to ${options.to}`); // Render email options.html = await this.viewEngine.render('mails/' + template.template + '.mnjk', locals, false); options.text = await this.viewEngine.render('mails/' + template.template + '.mnjk', locals, true); // Send email results.push(await this.getTransporter().sendMail(options)); } return results; } private getTransporter(): Transporter { if (!this.transporter) throw new MailError('Mail system was not prepared.'); return this.transporter; } }