From 827002989ee6e3f0d776b5b55b8fb6d65f10c898 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 27 Jan 2021 11:28:02 +0100 Subject: [PATCH 1/6] Added test mail templates ref #124 --- src/static/mail_templates/test.html | 369 ++++++++++++++++++++++++++++ src/static/mail_templates/test.txt | 8 + 2 files changed, 377 insertions(+) create mode 100644 src/static/mail_templates/test.html create mode 100644 src/static/mail_templates/test.txt diff --git a/src/static/mail_templates/test.html b/src/static/mail_templates/test.html new file mode 100644 index 0000000..35be644 --- /dev/null +++ b/src/static/mail_templates/test.html @@ -0,0 +1,369 @@ + + + + + LfK! - Mail test + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ LfK! - Mail test +
+ + + + +
+ + \ No newline at end of file diff --git a/src/static/mail_templates/test.txt b/src/static/mail_templates/test.txt new file mode 100644 index 0000000..423da10 --- /dev/null +++ b/src/static/mail_templates/test.txt @@ -0,0 +1,8 @@ +LfK! - Mail test. + +This is a test mail triggered by an admin in the LfK! backend. + + +Copyright © {{copyright_owner}}. All rights reserved. +Imprint: {{link_imprint}} | Privacy: {{link_privacy}} +This mail was sent to {{recipient_mail}} because someone requested a mail test for this mail address. \ No newline at end of file From b94179e3caaf4be0654ca3372f57a490fb32e208 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 27 Jan 2021 11:28:27 +0100 Subject: [PATCH 2/6] Added a test mail sending function ref #124 --- src/mailer.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/mailer.ts b/src/mailer.ts index bc2281d..13475f0 100644 --- a/src/mailer.ts +++ b/src/mailer.ts @@ -41,6 +41,18 @@ export class Mailer { await this.sendMail(mail); } + public async sendTestMail(to_address: string) { + const body_html = fs.readFileSync(__dirname + '/static/mail_templates/test.html', { encoding: 'utf8' }).replace("{{recipient_mail}}", to_address).replace("{{copyright_owner}}", "LfK!").replace("{{link_imprint}}", `${config.app_url}/imprint`).replace("{{link_privacy}}", `${config.app_url}/privacy`); + const body_txt = fs.readFileSync(__dirname + '/static/mail_templates/test.html', { encoding: 'utf8' }).replace("{{recipient_mail}}", to_address).replace("{{copyright_owner}}", "LfK!").replace("{{link_imprint}}", `${config.app_url}/imprint`).replace("{{link_privacy}}", `${config.app_url}/privacy`); + const mail: MailOptions = { + to: to_address, + subject: "LfK! Test Mail", + text: body_txt, + html: body_html + }; + await this.sendMail(mail); + } + public async sendMail(mail: MailOptions) { mail.from = config.mail_from; await this.transport.sendMail(mail); From 9bd7636a23b5a9662ea2b965d2a2407727a188fb Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 27 Jan 2021 11:37:46 +0100 Subject: [PATCH 3/6] Added comments ref #124 --- src/mailer.ts | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/mailer.ts b/src/mailer.ts index 13475f0..d52e540 100644 --- a/src/mailer.ts +++ b/src/mailer.ts @@ -4,12 +4,18 @@ import { MailOptions } from 'nodemailer/lib/json-transport'; import Mail from 'nodemailer/lib/mailer'; import { config } from './config'; import { MailServerConfigError } from './errors/MailErrors'; + /** * This class is responsible for all things mail sending. + * This uses the mail emplates from src/static/mail_templates */ export class Mailer { private transport: Mail; + /** + * The class's default constructor. + * Creates the transporter and tests the connection. + */ constructor() { this.transport = nodemailer.createTransport({ host: config.mail_server, @@ -27,6 +33,11 @@ export class Mailer { }); } + /** + * Function for sending a test mail from the test 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) { const reset_link = `${config.app_url}/reset/${token}` const body_html = fs.readFileSync(__dirname + '/static/mail_templates/pw-reset.html', { encoding: 'utf8' }).replace("{{reset_link}}", reset_link).replace("{{recipient_mail}}", to_address).replace("{{copyright_owner}}", "LfK!").replace("{{link_imprint}}", `${config.app_url}/imprint`).replace("{{link_privacy}}", `${config.app_url}/privacy`); @@ -41,9 +52,13 @@ export class Mailer { await this.sendMail(mail); } - public async sendTestMail(to_address: string) { + /** + * Function for sending a test mail from the test mail template. + * @param to_address The address the test mail will be sent to - this is the configured from-address by default. + */ + public async sendTestMail(to_address: string = config.mail_from) { const body_html = fs.readFileSync(__dirname + '/static/mail_templates/test.html', { encoding: 'utf8' }).replace("{{recipient_mail}}", to_address).replace("{{copyright_owner}}", "LfK!").replace("{{link_imprint}}", `${config.app_url}/imprint`).replace("{{link_privacy}}", `${config.app_url}/privacy`); - const body_txt = fs.readFileSync(__dirname + '/static/mail_templates/test.html', { encoding: 'utf8' }).replace("{{recipient_mail}}", to_address).replace("{{copyright_owner}}", "LfK!").replace("{{link_imprint}}", `${config.app_url}/imprint`).replace("{{link_privacy}}", `${config.app_url}/privacy`); + const body_txt = fs.readFileSync(__dirname + '/static/mail_templates/test.txt', { encoding: 'utf8' }).replace("{{recipient_mail}}", to_address).replace("{{copyright_owner}}", "LfK!").replace("{{link_imprint}}", `${config.app_url}/imprint`).replace("{{link_privacy}}", `${config.app_url}/privacy`); const mail: MailOptions = { to: to_address, subject: "LfK! Test Mail", @@ -53,6 +68,10 @@ export class Mailer { 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); From ad4b903c258820f14df28d56b12e099075ca7d78 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 27 Jan 2021 11:53:33 +0100 Subject: [PATCH 4/6] Added a Mail permisssion target ref #124 --- src/models/enums/PermissionTargets.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/models/enums/PermissionTargets.ts b/src/models/enums/PermissionTargets.ts index 4bb9c1c..249ec2f 100644 --- a/src/models/enums/PermissionTargets.ts +++ b/src/models/enums/PermissionTargets.ts @@ -15,5 +15,6 @@ export enum PermissionTarget { STATION = 'STATION', CARD = 'CARD', DONATION = 'DONATION', - CONTACT = 'CONTACT' + CONTACT = 'CONTACT', + MAIL = 'MAIL' } \ No newline at end of file From 54ed313342a72b029b9545bc5ea193e3f0c2166d Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 27 Jan 2021 11:59:32 +0100 Subject: [PATCH 5/6] Implemented the test-mail endpoint via a new mailcontroller ref #124 --- src/controllers/MailController.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/controllers/MailController.ts diff --git a/src/controllers/MailController.ts b/src/controllers/MailController.ts new file mode 100644 index 0000000..94ff599 --- /dev/null +++ b/src/controllers/MailController.ts @@ -0,0 +1,26 @@ +import { Authorized, JsonController, Post } from 'routing-controllers'; +import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; +import { config } from '../config'; +import { Mailer } from '../mailer'; +import { ResponseEmpty } from '../models/responses/ResponseEmpty'; + + +@JsonController('/mails') +@OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] }) +export class MailController { + + private mailer: Mailer; + + constructor() { + this.mailer = new Mailer(); + } + + @Post('/test') + @Authorized(["MAIL:CREATE"]) + @ResponseSchema(ResponseEmpty, { statusCode: 200 }) + @OpenAPI({ description: 'Sends a test email to the configured from-address.' }) + async get() { + await this.mailer.sendTestMail(config.mail_from); + return new ResponseEmpty(); + } +} \ No newline at end of file From ae74b3963fddb847aed4a828031b93b26cf551db Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 27 Jan 2021 12:03:02 +0100 Subject: [PATCH 6/6] Added test mail sending test ref #124 --- src/tests/mails/mail_test.spec.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/tests/mails/mail_test.spec.ts diff --git a/src/tests/mails/mail_test.spec.ts b/src/tests/mails/mail_test.spec.ts new file mode 100644 index 0000000..d8a8d71 --- /dev/null +++ b/src/tests/mails/mail_test.spec.ts @@ -0,0 +1,22 @@ +import axios from 'axios'; +import { config } from '../../config'; + +const base = "http://localhost:" + config.internal_port + +let access_token; +let axios_config; + +beforeAll(async () => { + const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); + access_token = res.data["access_token"]; + axios_config = { + headers: { "authorization": "Bearer " + access_token }, + validateStatus: undefined + }; +}); + +describe('POST /mails/test valid', () => { + it('test mail request should return 200', async () => { + const res1 = await axios.post(base + '/api/mails/test', null, axios_config); + }); +}); \ No newline at end of file