diff --git a/src/Mailer.ts b/src/Mailer.ts
index d2752f8..bf844f1 100644
--- a/src/Mailer.ts
+++ b/src/Mailer.ts
@@ -121,8 +121,8 @@ export class Mailer {
/**
* 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.
+ * @param to_address The address the mail will be sent to. Should always get pulled from a runner object.
+ * @param token The runner's selfservice token - will be combined with the app_url to generate a selfservice profile link.
*/
public async sendWelcomeMail(to_address: string, token: string, locale: string = "en") {
await i18next.changeLanguage(locale);
@@ -152,6 +152,39 @@ export class Mailer {
await this.sendMail(mail);
}
+ /**
+ * Function for sending a selfservice link forgotten mail from the runner_forgot template.
+ * @param to_address The address the mail will be sent to. Should always get pulled from a runner object.
+ * @param token The runner's selfservice token - will be combined with the app_url to generate a selfservice profile link.
+ */
+ public async sendSelfserviceForgottenMail(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/runner_forgot.html', { encoding: 'utf8' }));
+ const template_txt = Handlebars.compile(fs.readFileSync(__dirname + '/templates/runner_forgot.txt', { encoding: 'utf8' }));
+ const body_html = template_html(replacements);
+ const body_txt = template_txt(replacements);
+
+ const mail: MailOptions = {
+ to: to_address,
+ subject: i18next.t("your-event_name-profile", 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
diff --git a/src/controllers/MailController.ts b/src/controllers/MailController.ts
index 99be6c2..69e8d6c 100644
--- a/src/controllers/MailController.ts
+++ b/src/controllers/MailController.ts
@@ -2,6 +2,7 @@ import { Authorized, Body, JsonController, Post, QueryParam } from 'routing-cont
import { OpenAPI } from 'routing-controllers-openapi';
import { Mailer } from '../Mailer';
import { locales } from '../models/LocaleEnum';
+import { MailTypes } from '../models/MailTypeEnum';
import { ResetMail } from '../models/ResetMail';
import { SuccessResponse } from '../models/SuccessResponse';
import { WelcomeMail } from '../models/WelcomeMail';
@@ -28,7 +29,7 @@ export class MailController {
} catch (error) {
throw error;
}
- return new SuccessResponse(locale);
+ return new SuccessResponse(MailTypes.PASSWORD_RESET, locale);
}
@Post('/test')
@@ -44,7 +45,7 @@ export class MailController {
console.log(error)
throw error;
}
- return new SuccessResponse(locale);
+ return new SuccessResponse(MailTypes.TEST, locale);
}
@Post('/registration')
@@ -60,6 +61,22 @@ export class MailController {
console.log(error)
throw error;
}
- return new SuccessResponse(locale);
+ return new SuccessResponse(MailTypes.RUNNER_WELCOME, locale);
+ }
+
+ @Post('/registration_forgot')
+ @OpenAPI({ description: "Sends selfservice link forgotten mails", parameters: [{ in: "query", name: "locale", schema: { type: "string", enum: ["de", "en"] } }] })
+ async sendSelfserviceForgotten(@Body({ validate: true }) mailOptions: WelcomeMail, @QueryParam("locale") locale: locales) {
+ if (!this.initialized) {
+ await this.mailer.init();
+ this.initialized = true;
+ }
+ try {
+ this.mailer.sendSelfserviceForgottenMail(mailOptions.address, mailOptions.selfserviceToken, locale?.toString())
+ } catch (error) {
+ console.log(error)
+ throw error;
+ }
+ return new SuccessResponse(MailTypes.RUNNER_FORGOT, locale);
}
}
diff --git a/src/locales/en.json b/src/locales/en.json
index 627914c..c612a2b 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -12,15 +12,20 @@
"privacy": "Privacy",
"reset-password": "Reset password",
"test-mail": "Test mail",
+ "thank-you-for-requesting-a-new-link-to-your-event_name-runner-profile": "Thank you for requesting a new link to your {{event_name}} runner profile.",
"thanks-for-registering-and-welcome-to-the-event_name": "Thanks for registering and welcome to the {{event_name}}!",
"the-only-thing-you-have-to-do-now-is-to-bring-your-registration-code-with-you": "The only thing you have to do now is to bring your registration code with you.",
"this-is-a-test-mail-triggered-by-an-admin-in-the-lfk-backend": "This is a test mail triggered by an admin in the LfK! backend.",
"this-mail-was-sent-to-recipient_mail-because-someone-request-a-mail-test-for-this-mail-address": "This mail was sent to you because someone request a mail test for this mail address.",
"this-mail-was-sent-to-you-because-someone-request-a-password-reset-for-a-account-linked-to-the-mail-address": "This mail was sent to you because someone request a password reset for a account linked to the mail address.",
- "this-mail-was-sent-to-you-because-someone-used-your-mail-address-to-register-themselfes-for-the-event_name": "This mail was sent to you, because someone used your mail address to register themselfes for the {{event_name}}",
+ "this-mail-was-sent-to-you-because-someone-requested-a-new-link-to-access-your-profile-for-the-event_name": "This mail was sent to you, because someone requested a new link to access your profile for the {{event_name}}.",
+ "this-mail-was-sent-to-you-because-someone-used-your-mail-address-to-register-themselfes-for-the-event_name": "This mail was sent to you, because someone used your mail address to register themselves for the {{event_name}}",
+ "to-prevent-spam-you-can-only-request-a-new-link-every-24hrs": "To prevent spam you can only request a new link every 24hrs.",
"view-my-data": "View my data",
"we-successfully-processed-your-registration": "We successfully processed your registration.",
"welcome": "Welcome",
"you-can-view-your-registration-code-lap-times-and-much-more-by-visiting-our-selfservice": "You can view your registration code, lap times and much more by visiting our selfservice:",
- "your-password-wont-be-changed-until-you-click-the-reset-link-below-and-set-a-new-one": "Your password won't be changed until you click the reset link below and set a new one."
+ "your-event_name-profile": "Your {{event_name}} profile",
+ "your-password-wont-be-changed-until-you-click-the-reset-link-below-and-set-a-new-one": "Your password won't be changed until you click the reset link below and set a new one.",
+ "your-profile": "Your Profile"
}
\ No newline at end of file
diff --git a/src/models/MailTypeEnum.ts b/src/models/MailTypeEnum.ts
new file mode 100644
index 0000000..338f52c
--- /dev/null
+++ b/src/models/MailTypeEnum.ts
@@ -0,0 +1,6 @@
+export enum MailTypes {
+ PASSWORD_RESET = "PASSWORD_RESET",
+ RUNNER_FORGOT = "RUNNER_FORGOT",
+ RUNNER_WELCOME = "RUNNER_WELCOME",
+ TEST = "TEST"
+}
\ No newline at end of file
diff --git a/src/models/SuccessResponse.ts b/src/models/SuccessResponse.ts
index d3104a8..48cc43e 100644
--- a/src/models/SuccessResponse.ts
+++ b/src/models/SuccessResponse.ts
@@ -1,5 +1,6 @@
import { IsBoolean, IsString } from 'class-validator';
import { locales } from './LocaleEnum';
+import { MailTypes } from './MailTypeEnum';
/**
* Simple success response class to make everyone happy :)
@@ -15,7 +16,11 @@ export class SuccessResponse {
@IsString()
locale: locales;
- constructor(locale?: locales) {
+ @IsString()
+ type: MailTypes;
+
+ constructor(type: MailTypes, locale?: locales) {
+ this.type = type;
this.locale = locale || locales.en;
}
}
\ No newline at end of file
diff --git a/src/templates/runner_forgot.html b/src/templates/runner_forgot.html
new file mode 100644
index 0000000..056f5b3
--- /dev/null
+++ b/src/templates/runner_forgot.html
@@ -0,0 +1,396 @@
+
+
+
+
+ {{__ "your-event_name-profile"}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{__ "your-event_name-profile"}}
+
+
+
+
+
+
+
+
+
+
+
+ {{event_name}}
+ |
+
+
+
+ {{__ "your-profile"}}
+
+ {{__ "thank-you-for-requesting-a-new-link-to-your-event_name-runner-profile"}}
+ {{__ "you-can-view-your-registration-code-lap-times-and-much-more-by-visiting-our-selfservice"}}
+
+ |
+
+
+
+
+
+
+
+ |
+
+
+
+ Link: {{selfservice_link}}
+ {{__ "if-you-ever-loose-the-link-you-can-request-a-new-one-by-visiting-our-website"}} {{forgot_link}} |
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+ Copyright © {{copyright_owner}}. {{__ "all-rights-reserved"}}
+ |
+
+
+
+ {{__ "imprint"}}
+ {{__ "privacy"}}
+ |
+
+
+
+
+
+
+ |
+
+
+
+
+ {{__ "this-mail-was-sent-to-you-because-someone-requested-a-new-link-to-access-your-profile-for-the-event_name"}}
+ {{__ "to-prevent-spam-you-can-only-request-a-new-link-every-24hrs"}}
+ {{__ "if-you-didnt-register-yourself-you-should-contact-us-to-get-your-data-removed-from-our-systems"}} {{contact_mail}}
+ |
+
+
+
+ |
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/templates/runner_forgot.txt b/src/templates/runner_forgot.txt
new file mode 100644
index 0000000..1597847
--- /dev/null
+++ b/src/templates/runner_forgot.txt
@@ -0,0 +1,15 @@
+{{__ "your-event_name-profile"}}
+
+{{__ "thank-you-for-requesting-a-new-link-to-your-event_name-runner-profile"}}
+{{__ "you-can-view-your-registration-code-lap-times-and-much-more-by-visiting-our-selfservice"}}
+
+{{selfservice_link}}
+
+{{__ "if-you-ever-loose-the-link-you-can-request-a-new-one-by-visiting-our-website"}} {{forgot_link}}
+
+
+Copyright © {{copyright_owner}}. {{__ "all-rights-reserved"}}.
+{{__ "imprint"}}: {{link_imprint}} | {{__ "privacy"}}: {{link_privacy}}
+{{__ "this-mail-was-sent-to-you-because-someone-requested-a-new-link-to-access-your-profile-for-the-event_name"}}
+{{__ "to-prevent-spam-you-can-only-request-a-new-link-every-24hrs"}}
+{{__ "if-you-didnt-register-yourself-you-should-contact-us-to-get-your-data-removed-from-our-systems"}} {{contact_mail}}
\ No newline at end of file
diff --git a/src/tests/pw_reset_mail.spec.ts b/src/tests/pw_reset_mail.spec.ts
index d43f3a3..aeb4692 100644
--- a/src/tests/pw_reset_mail.spec.ts
+++ b/src/tests/pw_reset_mail.spec.ts
@@ -42,7 +42,8 @@ describe('POST /reset with auth and vaild body', () => {
expect(res.data).toEqual({
success: true,
message: "Sent!",
- locale: "en"
+ locale: "en",
+ type: "PASSWORD_RESET"
})
});
it('Post with auth, body and locale=en should return 200', async () => {
@@ -54,7 +55,8 @@ describe('POST /reset with auth and vaild body', () => {
expect(res.data).toEqual({
success: true,
message: "Sent!",
- locale: "en"
+ locale: "en",
+ type: "PASSWORD_RESET"
})
});
it('Post with auth, body and locale=de should return 200', async () => {
@@ -66,7 +68,8 @@ describe('POST /reset with auth and vaild body', () => {
expect(res.data).toEqual({
success: true,
message: "Sent!",
- locale: "de"
+ locale: "de",
+ type: "PASSWORD_RESET"
})
});
});
\ No newline at end of file
diff --git a/src/tests/selfservice_forgot_mail.spec.ts b/src/tests/selfservice_forgot_mail.spec.ts
new file mode 100644
index 0000000..fcf89a3
--- /dev/null
+++ b/src/tests/selfservice_forgot_mail.spec.ts
@@ -0,0 +1,75 @@
+import axios from 'axios';
+import { config } from '../config';
+const base = "http://localhost:" + config.internal_port
+
+const axios_config = {
+ validateStatus: undefined
+};
+
+describe('POST /registration_forgot without auth', () => {
+ it('Post without auth should return 401', async () => {
+ const res = await axios.post(base + '/registration_forgot', null, axios_config);
+ expect(res.status).toEqual(401);
+ });
+});
+
+describe('POST /registration_forgot with auth but wrong body', () => {
+ it('Post with auth but no body should return 400', async () => {
+ const res = await axios.post(base + '/registration_forgot?key=' + config.api_key, null, axios_config);
+ expect(res.status).toEqual(400);
+ });
+ it('Post with auth but no mail should return 400', async () => {
+ const res = await axios.post(base + '/registration_forgot?key=' + config.api_key, { selfserviceToken: "test" }, axios_config);
+ expect(res.status).toEqual(400);
+ });
+ it('Post with auth but no reset key should return 400', async () => {
+ const res = await axios.post(base + '/registration_forgot?key=' + config.api_key, { address: "test@dev.lauf-fuer-kaya.de" }, axios_config);
+ expect(res.status).toEqual(400);
+ });
+ it('Post with auth but invalid mail should return 400', async () => {
+ const res = await axios.post(base + '/registration_forgot?key=' + config.api_key, { selfserviceToken: "test", address: "testdev.l.de" }, axios_config);
+ expect(res.status).toEqual(400);
+ });
+});
+
+describe('POST /reset with auth and vaild body', () => {
+ it('Post with auth, body and no locale should return 200', async () => {
+ const res = await axios.post(base + '/registration_forgot?key=' + config.api_key, {
+ selfserviceToken: "test",
+ address: "test@dev.lauf-fuer-kaya.de"
+ }, axios_config);
+ expect(res.status).toEqual(200);
+ expect(res.data).toEqual({
+ success: true,
+ message: "Sent!",
+ locale: "en",
+ type: "RUNNER_FORGOT"
+ })
+ });
+ it('Post with auth, body and locale=en should return 200', async () => {
+ const res = await axios.post(base + '/registration_forgot?locale=en&key=' + config.api_key, {
+ selfserviceToken: "test",
+ address: "test@dev.lauf-fuer-kaya.de"
+ }, axios_config);
+ expect(res.status).toEqual(200);
+ expect(res.data).toEqual({
+ success: true,
+ message: "Sent!",
+ locale: "en",
+ type: "RUNNER_FORGOT"
+ })
+ });
+ it('Post with auth, body and locale=de should return 200', async () => {
+ const res = await axios.post(base + '/registration_forgot?locale=de&key=' + config.api_key, {
+ selfserviceToken: "test",
+ address: "test@dev.lauf-fuer-kaya.de"
+ }, axios_config);
+ expect(res.status).toEqual(200);
+ expect(res.data).toEqual({
+ success: true,
+ message: "Sent!",
+ locale: "de",
+ type: "RUNNER_FORGOT"
+ })
+ });
+});
\ No newline at end of file
diff --git a/src/tests/selfservice_welcome_mail.spec.ts b/src/tests/selfservice_welcome_mail.spec.ts
index 709eec0..f9bb7ee 100644
--- a/src/tests/selfservice_welcome_mail.spec.ts
+++ b/src/tests/selfservice_welcome_mail.spec.ts
@@ -42,7 +42,8 @@ describe('POST /reset with auth and vaild body', () => {
expect(res.data).toEqual({
success: true,
message: "Sent!",
- locale: "en"
+ locale: "en",
+ type: "RUNNER_WELCOME"
})
});
it('Post with auth, body and locale=en should return 200', async () => {
@@ -54,7 +55,8 @@ describe('POST /reset with auth and vaild body', () => {
expect(res.data).toEqual({
success: true,
message: "Sent!",
- locale: "en"
+ locale: "en",
+ type: "RUNNER_WELCOME"
})
});
it('Post with auth, body and locale=de should return 200', async () => {
@@ -66,7 +68,8 @@ describe('POST /reset with auth and vaild body', () => {
expect(res.data).toEqual({
success: true,
message: "Sent!",
- locale: "de"
+ locale: "de",
+ type: "RUNNER_WELCOME"
})
});
});
\ No newline at end of file
diff --git a/src/tests/test_mail.spec.ts b/src/tests/test_mail.spec.ts
index ff3dac6..9d88dc6 100644
--- a/src/tests/test_mail.spec.ts
+++ b/src/tests/test_mail.spec.ts
@@ -20,7 +20,8 @@ describe('POST /test with auth', () => {
expect(res.data).toEqual({
success: true,
message: "Sent!",
- locale: "en"
+ locale: "en",
+ type: "TEST"
})
});
it('Post with auth and locale=en should return 200', async () => {
@@ -29,7 +30,8 @@ describe('POST /test with auth', () => {
expect(res.data).toEqual({
success: true,
message: "Sent!",
- locale: "en"
+ locale: "en",
+ type: "TEST"
})
});
it('Post with auth and locale=de should return 200', async () => {
@@ -38,7 +40,8 @@ describe('POST /test with auth', () => {
expect(res.data).toEqual({
success: true,
message: "Sent!",
- locale: "de"
+ locale: "de",
+ type: "TEST"
})
});
});
\ No newline at end of file