import fs from "fs"; import Handlebars from 'handlebars'; import i18next from "i18next"; import Backend from 'i18next-fs-backend'; import nodemailer from 'nodemailer'; import { MailOptions } from 'nodemailer/lib/json-transport'; import Mail from 'nodemailer/lib/mailer'; import path from 'path'; import { config } from './config'; import { MailServerConfigError } from './errors/MailErrors'; /** * This class is responsible for all mail sending. * This uses the html and plaintext templates from src/templates. */ export class Mailer { private transport: Mail; private static interpolations = { copyright_owner: config.copyright_owner, event_name: config.event_name, contact_mail: config.contact_mail } /** * Main constructor. * Initializes i18n(ext), Handlebars and puppeteer. */ constructor() { this.init(); } /** * Main constructor. * Initializes i18n(ext), Handlebars and puppeteer. */ public async init() { await i18next .use(Backend) .init({ fallbackLng: 'en', lng: 'en', backend: { loadPath: path.join(__dirname, '/locales/{{lng}}.json') } }); await Handlebars.registerHelper('__', function (str) { return i18next.t(str, Mailer.interpolations).toString(); } ); this.transport = nodemailer.createTransport({ host: config.mail_server, port: config.mail_port, auth: { user: config.mail_user, pass: config.mail_password } }); this.transport.verify(function (error, success) { if (error) { throw new MailServerConfigError(); } }); } /** * Function for sending a reset mail from the reset mail template. * @param to_address The address the mail will be sent to. Should always get pulled from a user object. * @param token The requested password reset token - will be combined with the app_url to generate a password reset link. */ public async sendResetMail(to_address: string, token: string, locale: string = "en") { await i18next.changeLanguage(locale); const replacements = { recipient_mail: to_address, copyright_owner: config.copyright_owner, link_imprint: `${config.app_url}/imprint`, link_privacy: `${config.app_url}/privacy`, reset_link: `${config.app_url}/reset/${(Buffer.from(token)).toString("base64")}` } const template_html = Handlebars.compile(fs.readFileSync(__dirname + '/templates/pw-reset.html', { encoding: 'utf8' })); const template_txt = Handlebars.compile(fs.readFileSync(__dirname + '/templates/pw-reset.txt', { encoding: 'utf8' })); const body_html = template_html(replacements); const body_txt = template_txt(replacements); const mail: MailOptions = { to: to_address, subject: i18next.t("lfk-password-reset", Mailer.interpolations).toString(), text: body_txt, html: body_html }; await this.sendMail(mail); } /** * Function for sending a test mail from the test mail template. * @param to_address The address the mail will be sent to - usually the FROM address. */ public async sendTestMail(locale: string = "en") { await i18next.changeLanguage(locale); const to_address: string = config.mail_from; const replacements = { recipient_mail: to_address, copyright_owner: config.copyright_owner, link_imprint: `${config.app_url}/imprint`, link_privacy: `${config.app_url}/privacy` } const template_html = Handlebars.compile(fs.readFileSync(__dirname + '/templates/test.html', { encoding: 'utf8' })); const template_txt = Handlebars.compile(fs.readFileSync(__dirname + '/templates/test.txt', { encoding: 'utf8' })); const body_html = template_html(replacements); const body_txt = template_txt(replacements); const mail: MailOptions = { to: to_address, subject: i18next.t("test-mail", Mailer.interpolations).toString(), text: body_txt, html: body_html }; await this.sendMail(mail); } /** * Function for sending a reset mail from the reset mail template. * @param to_address The address the mail will be sent to. Should always get pulled from a user object. * @param token The requested password reset token - will be combined with the app_url to generate a password reset link. */ public async sendWelcomeMail(to_address: string, token: string, locale: string = "en") { await i18next.changeLanguage(locale); const replacements = { recipient_mail: to_address, copyright_owner: config.copyright_owner, link_imprint: `${config.app_url}/imprint`, link_privacy: `${config.app_url}/privacy`, selfservice_link: `${config.app_url}/selfservice/profile/${token}`, forgot_link: `${config.app_url}/selfservice`, contact_mail: config.contact_mail, event_name: config.event_name } const template_html = Handlebars.compile(fs.readFileSync(__dirname + '/templates/welcome_runner.html', { encoding: 'utf8' })); const template_txt = Handlebars.compile(fs.readFileSync(__dirname + '/templates/welcome_runner.txt', { encoding: 'utf8' })); const body_html = template_html(replacements); const body_txt = template_txt(replacements); const mail: MailOptions = { to: to_address, subject: i18next.t("event_name-registration", Mailer.interpolations).toString(), text: body_txt, html: body_html }; await this.sendMail(mail); } /** * Wrapper function for sending a mail via this object's transporter. * @param mail MailOptions object containing the */ public async sendMail(mail: MailOptions) { mail.from = config.mail_from; await this.transport.sendMail(mail); } }