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
diff --git a/src/mailer.ts b/src/mailer.ts
index bc2281d..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,6 +52,26 @@ export class Mailer {
await this.sendMail(mail);
}
+ /**
+ * 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.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",
+ 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);
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
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
+
+
+
+
+
+
+
+
+
+
+
+ LfK!
+ |
+
+
+
+ Test mail
+ This is a test mail triggered by an admin in the LfK! backend.
+ |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+ Copyright © {{copyright_owner}}. All rights reserved.
+ |
+
+
+
+ Imprint
+ Privacy
+ |
+
+
+
+
+
+
+ |
+
+
+
+ This mail was sent to {{recipient_mail}} because someone request a mail test for this mail address. |
+
+
+
+ |
+
+
+
+
+
+
+
+
\ 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
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