Render emails using NunjucksComponent's environment
This commit is contained in:
parent
cfb7bddca6
commit
4d0c714dbd
25
src/Mail.ts
25
src/Mail.ts
@ -1,7 +1,7 @@
|
||||
import nodemailer, {SentMessageInfo, Transporter} from "nodemailer";
|
||||
import config from "config";
|
||||
import {Options} from "nodemailer/lib/mailer";
|
||||
import nunjucks from 'nunjucks';
|
||||
import {Environment} from 'nunjucks';
|
||||
import * as util from "util";
|
||||
import {WrappingError} from "./Utils";
|
||||
import mjml2html from "mjml";
|
||||
@ -45,9 +45,14 @@ export default class Mail {
|
||||
if (this.transporter) this.transporter.close();
|
||||
}
|
||||
|
||||
public static parse(template: string, data: { [p: string]: unknown }, textOnly: boolean): string {
|
||||
public static parse(
|
||||
environment: Environment,
|
||||
template: string,
|
||||
data: { [p: string]: unknown },
|
||||
textOnly: boolean,
|
||||
): string {
|
||||
data.text = textOnly;
|
||||
const nunjucksResult = nunjucks.render(template, data);
|
||||
const nunjucksResult = environment.render(template, data);
|
||||
if (textOnly) return nunjucksResult;
|
||||
|
||||
const mjmlResult = mjml2html(nunjucksResult, {});
|
||||
@ -59,11 +64,13 @@ export default class Mail {
|
||||
return mjmlResult.html;
|
||||
}
|
||||
|
||||
private readonly template: MailTemplate;
|
||||
private readonly options: Options = {};
|
||||
private readonly data: ParsedUrlQueryInput;
|
||||
|
||||
public constructor(template: MailTemplate, data: ParsedUrlQueryInput = {}) {
|
||||
public constructor(
|
||||
private readonly environment: Environment,
|
||||
private readonly template: MailTemplate,
|
||||
private readonly data: ParsedUrlQueryInput = {},
|
||||
) {
|
||||
this.template = template;
|
||||
this.data = data;
|
||||
this.options.subject = this.template.getSubject(data);
|
||||
@ -106,8 +113,10 @@ export default class Mail {
|
||||
log.debug('Send mail', this.options);
|
||||
|
||||
// Render email
|
||||
this.options.html = Mail.parse('mails/' + this.template.template + '.mjml.njk', this.data, false);
|
||||
this.options.text = Mail.parse('mails/' + this.template.template + '.mjml.njk', this.data, true);
|
||||
this.options.html = Mail.parse(this.environment, 'mails/' + this.template.template + '.mjml.njk',
|
||||
this.data, false);
|
||||
this.options.text = Mail.parse(this.environment, 'mails/' + this.template.template + '.mjml.njk',
|
||||
this.data, true);
|
||||
|
||||
// Send email
|
||||
results.push(await Mail.getTransporter().sendMail(this.options));
|
||||
|
@ -7,8 +7,15 @@ import {PENDING_ACCOUNT_REVIEW_MAIL_TEMPLATE} from "../Mails";
|
||||
import Mail from "../Mail";
|
||||
import Controller from "../Controller";
|
||||
import config from "config";
|
||||
import Application from "../Application";
|
||||
import NunjucksComponent from "../components/NunjucksComponent";
|
||||
|
||||
export default abstract class AuthGuard<P extends AuthProof<User>> {
|
||||
public constructor(
|
||||
private readonly app: Application,
|
||||
) {
|
||||
}
|
||||
|
||||
protected abstract async getProofForSession(session: Express.Session): Promise<P | null>;
|
||||
|
||||
protected async getProofForRequest(_req: Request): Promise<P | null> {
|
||||
@ -77,7 +84,7 @@ export default abstract class AuthGuard<P extends AuthProof<User>> {
|
||||
}
|
||||
|
||||
if (!user.isApproved()) {
|
||||
await new Mail(PENDING_ACCOUNT_REVIEW_MAIL_TEMPLATE, {
|
||||
await new Mail(this.app.as(NunjucksComponent).getEnvironment(), PENDING_ACCOUNT_REVIEW_MAIL_TEMPLATE, {
|
||||
username: (await user.mainEmail.get())?.getOrFail('email'),
|
||||
link: config.get<string>('base_url') + Controller.route('accounts-approval'),
|
||||
}).send(config.get<string>('app.contact_email'));
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {Request, Response} from "express";
|
||||
import Controller from "../Controller";
|
||||
import Mail from "../Mail";
|
||||
import NunjucksComponent from "../components/NunjucksComponent";
|
||||
|
||||
export default class MailController extends Controller {
|
||||
public routes(): void {
|
||||
@ -9,6 +10,7 @@ export default class MailController extends Controller {
|
||||
|
||||
protected async getMail(request: Request, response: Response): Promise<void> {
|
||||
const template = request.params['template'];
|
||||
response.send(Mail.parse(`mails/${template}.mjml.njk`, request.query, false));
|
||||
response.send(Mail.parse(this.getApp().as(NunjucksComponent).getEnvironment(),
|
||||
`mails/${template}.mjml.njk`, request.query, false));
|
||||
}
|
||||
}
|
||||
|
@ -99,6 +99,7 @@ export default abstract class MagicLinkAuthController extends AuthController {
|
||||
// Register (email link)
|
||||
const geo = geoip.lookup(req.ip);
|
||||
await MagicLinkController.sendMagicLink(
|
||||
this.getApp(),
|
||||
req.getSession().id,
|
||||
isRegistration ? this.registerMagicLinkActionType : this.loginMagicLinkActionType,
|
||||
Controller.route('auth', undefined, {
|
||||
|
@ -8,9 +8,11 @@ import MagicLink from "../models/MagicLink";
|
||||
import config from "config";
|
||||
import Application from "../../Application";
|
||||
import {ParsedUrlQueryInput} from "querystring";
|
||||
import NunjucksComponent from "../../components/NunjucksComponent";
|
||||
|
||||
export default abstract class MagicLinkController<A extends Application> extends Controller {
|
||||
public static async sendMagicLink(
|
||||
app: Application,
|
||||
sessionId: string,
|
||||
actionType: string,
|
||||
original_url: string,
|
||||
@ -32,7 +34,7 @@ export default abstract class MagicLinkController<A extends Application> extends
|
||||
await link.save();
|
||||
|
||||
// Send email
|
||||
await new Mail(mailTemplate, Object.assign(data, {
|
||||
await new Mail(app.as(NunjucksComponent).getEnvironment(), mailTemplate, Object.assign(data, {
|
||||
link: `${config.get<string>('base_url')}${Controller.route('magic_link', undefined, {
|
||||
id: link.id,
|
||||
token: token,
|
||||
|
@ -13,7 +13,7 @@ import Middleware from "../Middleware";
|
||||
|
||||
export default class NunjucksComponent extends ApplicationComponent {
|
||||
private readonly viewsPath: string[];
|
||||
private env?: Environment;
|
||||
private environment?: Environment;
|
||||
|
||||
public constructor(viewsPath: string[] = ['views']) {
|
||||
super();
|
||||
@ -32,7 +32,7 @@ export default class NunjucksComponent extends ApplicationComponent {
|
||||
log.warn('Couldn\'t determine coreVersion.', e);
|
||||
}
|
||||
|
||||
this.env = new nunjucks.Environment([
|
||||
this.environment = new nunjucks.Environment([
|
||||
...this.viewsPath.map(path => new nunjucks.FileSystemLoader(path)),
|
||||
new nunjucks.FileSystemLoader(path.join(__dirname, '../../../views')),
|
||||
new nunjucks.FileSystemLoader(path.join(__dirname, '../views')),
|
||||
@ -60,7 +60,7 @@ export default class NunjucksComponent extends ApplicationComponent {
|
||||
.addFilter('hex', (v: number) => {
|
||||
return v.toString(16);
|
||||
});
|
||||
this.env.express(app);
|
||||
this.environment.express(app);
|
||||
app.set('view engine', 'njk');
|
||||
}
|
||||
|
||||
@ -68,8 +68,9 @@ export default class NunjucksComponent extends ApplicationComponent {
|
||||
this.use(NunjucksMiddleware);
|
||||
}
|
||||
|
||||
public getEnv(): Environment | undefined {
|
||||
return this.env;
|
||||
public getEnvironment(): Environment {
|
||||
if (!this.environment) throw new Error('Environment not initialized.');
|
||||
return this.environment;
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,7 +78,7 @@ export class NunjucksMiddleware extends Middleware {
|
||||
private env?: Environment;
|
||||
|
||||
protected async handle(req: Request, res: Response, next: NextFunction): Promise<void> {
|
||||
this.env = this.app.as(NunjucksComponent).getEnv();
|
||||
this.env = this.app.as(NunjucksComponent).getEnvironment();
|
||||
res.locals.url = req.url;
|
||||
res.locals.params = req.params;
|
||||
res.locals.query = req.query;
|
||||
|
@ -71,7 +71,7 @@ export default class WebSocketServerComponent extends ApplicationComponent {
|
||||
});
|
||||
});
|
||||
|
||||
const env = this.nunjucksComponent?.getEnv();
|
||||
const env = this.nunjucksComponent?.getEnvironment();
|
||||
if (env) {
|
||||
env.addGlobal('websocketUrl', config.get('public_websocket_url'));
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import {ACCOUNT_REVIEW_NOTICE_MAIL_TEMPLATE} from "../Mails";
|
||||
import UserEmail from "../auth/models/UserEmail";
|
||||
import UserApprovedComponent from "../auth/models/UserApprovedComponent";
|
||||
import {RequireAdminMiddleware, RequireAuthMiddleware} from "../auth/AuthComponent";
|
||||
import NunjucksComponent from "../components/NunjucksComponent";
|
||||
|
||||
export default class BackendController extends Controller {
|
||||
private static readonly menu: BackendMenuElement[] = [];
|
||||
@ -75,7 +76,7 @@ export default class BackendController extends Controller {
|
||||
await account.save();
|
||||
|
||||
if (email && email.email) {
|
||||
await new Mail(ACCOUNT_REVIEW_NOTICE_MAIL_TEMPLATE, {
|
||||
await new Mail(this.getApp().as(NunjucksComponent).getEnvironment(), ACCOUNT_REVIEW_NOTICE_MAIL_TEMPLATE, {
|
||||
approved: true,
|
||||
link: config.get<string>('base_url') + Controller.route('auth'),
|
||||
}).send(email.email);
|
||||
@ -91,7 +92,7 @@ export default class BackendController extends Controller {
|
||||
await account.delete();
|
||||
|
||||
if (email && email.email) {
|
||||
await new Mail(ACCOUNT_REVIEW_NOTICE_MAIL_TEMPLATE, {
|
||||
await new Mail(this.getApp().as(NunjucksComponent).getEnvironment(), ACCOUNT_REVIEW_NOTICE_MAIL_TEMPLATE, {
|
||||
approved: false,
|
||||
}).send(email.email);
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ export class TestApp extends Application {
|
||||
public async getProofForSession(session: Express.Session): Promise<MagicLink | null> {
|
||||
return await MagicLink.bySessionId(session.id, ['login', 'register']);
|
||||
}
|
||||
}));
|
||||
}(this)));
|
||||
|
||||
// Utils
|
||||
this.use(new FormHelperComponent());
|
||||
|
Loading…
Reference in New Issue
Block a user