updated mailer configs

This commit is contained in:
rykuno 2024-08-05 07:22:46 -05:00
parent 6390f8e19a
commit fca1a2444a
5 changed files with 142 additions and 89 deletions

View file

@ -0,0 +1,32 @@
import type { Email } from "../interfaces/email.interface";
export class EmailChangeNoticeEmail implements Email {
constructor() { }
subject(): string {
return 'Email Change Notice'
}
html() {
return /*html*/ `
<html lang='en'>
<head>
<meta http-equiv='X-UA-Compatible' content='IE=edge' />
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
<title>Email Change Request</title>
</head>
<body>
<p class='title'>Email address change notice </p>
<p>
An update to your email address has been requested. If this is unexpected or you did not perform this action, please login and secure your account.</p>
</body>
<style>
.title { font-size: 24px; font-weight: 700; } .token-text { font-size: 24px; font-weight: 700; margin-top: 8px; }
.token-title { font-size: 18px; font-weight: 700; margin-bottom: 0px; }
.center { display: flex; justify-content: center; align-items: center; flex-direction: column;}
.token-subtext { font-size: 12px; margin-top: 0px; }
</style>
</html>
`
}
}

View file

@ -0,0 +1,39 @@
import type { Email } from "../interfaces/email.interface";
export class LoginVerificationEmail implements Email {
constructor(private readonly token: string) { }
subject(): string {
return 'Email Verification'
}
html() {
return /*html*/ `
<html lang='en'>
<head>
<meta http-equiv='X-UA-Compatible' content='IE=edge' />
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
<title>Message</title>
</head>
<body>
<p class='title'>Verify your email address</p>
<p>
Thanks for using example.com. We want to make sure it's really you. Please enter the following
verification code when prompted. If you don't have an exmaple.com an account, you can ignore
this message.</p>
<div class='center'>
<p class='token-title'>Verification Code</p>
<p class='token-text'>${this.token}</p>
<p class='token-subtext'>(This code is valid for 15 minutes)</p>
</div>
</body>
<style>
.title { font-size: 24px; font-weight: 700; } .token-text { font-size: 24px; font-weight: 700;
margin-top: 8px; } .token-title { font-size: 18px; font-weight: 700; margin-bottom: 0px; }
.center { display: flex; justify-content: center; align-items: center; flex-direction: column;}
.token-subtext { font-size: 12px; margin-top: 0px; }
</style>
</html>
`
}
}

View file

@ -0,0 +1,35 @@
import type { Email } from '../interfaces/email.interface';
export class WelcomeEmail implements Email {
constructor() { }
subject(): string {
return 'Welcome!'
}
html(): string {
return /*html*/ `
<html lang='en'>
<head>
<meta http-equiv='X-UA-Compatible' content='IE=edge' />
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
<title>Message</title>
</head>
<body>
<p class='title'>Welcome to Example</p>
<p>
Thanks for using example.com. We want to make sure it's really you. Please enter the following
verification code when prompted. If you don't have an exmaple.com an account, you can ignore
this message.</p>
</body>
<style>
.title { font-size: 24px; font-weight: 700; } .token-text { font-size: 24px; font-weight: 700;
margin-top: 8px; } .token-title { font-size: 18px; font-weight: 700; margin-bottom: 0px; }
.center { display: flex; justify-content: center; align-items: center; flex-direction: column;}
.token-subtext { font-size: 12px; margin-top: 0px; }
</style>
</html>
`;
}
}

View file

@ -0,0 +1,7 @@
export interface Email {
subject(): string
html(): string;
}

View file

@ -1,42 +1,18 @@
import fs from 'fs';
import path from 'path';
import nodemailer from 'nodemailer';
import handlebars from 'handlebars';
import { fileURLToPath } from 'url';
import { injectable } from 'tsyringe';
import type { Email } from '../interfaces/email.interface';
import { config } from '../common/config';
/* -------------------------------------------------------------------------- */
/* Service */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* ---------------------------------- About --------------------------------- */
/*
Services are responsible for handling business logic and data manipulation.
They genreally call on repositories or other services to complete a use-case.
*/
/* ---------------------------------- Notes --------------------------------- */
/*
Services should be kept as clean and simple as possible.
Create private functions to handle complex logic and keep the public methods as
simple as possible. This makes the service easier to read, test and understand.
*/
/* -------------------------------------------------------------------------- */
type SendMail = {
type SendProps = {
to: string | string[];
subject: string;
html: string;
};
type SendTemplate<T> = {
to: string | string[];
props: T;
};
email: Email;
}
@injectable()
export class MailerService {
private nodemailer = nodemailer.createTransport({
private async sendDev({ to, email }: SendProps) {
const message = await nodemailer.createTransport({
host: 'smtp.ethereal.email',
port: 587,
secure: false, // Use `true` for port 465, `false` for all other ports
@ -44,61 +20,25 @@ export class MailerService {
user: 'adella.hoppe@ethereal.email',
pass: 'dshNQZYhATsdJ3ENke'
}
});
sendEmailVerificationToken(data: SendTemplate<{ token: string }>) {
const template = handlebars.compile(this.getTemplate('email-verification-token'));
return this.send({
to: data.to,
subject: 'Email Verification',
html: template({ token: data.props.token })
});
}
sendEmailChangeNotification(data: SendTemplate<null>) {
const template = handlebars.compile(this.getTemplate('email-change-notice'));
return this.send({
to: data.to,
subject: 'Email Change Notice',
html: template(null)
});
}
sendLoginRequest(data: SendTemplate<{ token: string }>) {
const template = handlebars.compile(this.getTemplate('email-verification-token'));
return this.send({
to: data.to,
subject: 'Login Request',
html: template({ token: data.props.token })
});
}
sendWelcome(data: SendTemplate<null>) {
const template = handlebars.compile(this.getTemplate('welcome'));
return this.send({
to: data.to,
subject: 'Welcome!',
html: template(null)
});
}
private async send({ to, subject, html }: SendMail) {
const message = await this.nodemailer.sendMail({
from: '"Example" <example@ethereal.email>', // sender address
}).sendMail({
from: '"Example" <example@ethereal.email>',
bcc: to,
subject, // Subject line
text: html,
html
subject: email.subject(),
text: email.html(),
html: email.html()
});
console.log(nodemailer.getTestMessageUrl(message));
}
private getTemplate(template: string) {
const __filename = fileURLToPath(import.meta.url); // get the resolved path to the file
const __dirname = path.dirname(__filename); // get the name of the directory
return fs.readFileSync(
path.join(__dirname, `../infrastructure/email-templates/${template}.hbs`),
'utf-8'
);
private async sendProd({ to, email }: SendProps) {
// CONFIGURE MAILER
}
async send({ to, email }: SendProps) {
if (config.isProduction) {
await this.sendProd({ to, email });
} else {
await this.sendDev({ to, email });
}
}
}