diff --git a/src/lib/server/api/emails/email-change-notice.email.ts b/src/lib/server/api/emails/email-change-notice.email.ts new file mode 100644 index 0000000..ae664ae --- /dev/null +++ b/src/lib/server/api/emails/email-change-notice.email.ts @@ -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*/ ` + + + + + Email Change Request + + +

Email address change notice

+

+ 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.

+ + + + ` + } +} diff --git a/src/lib/server/api/emails/login-verification.email.ts b/src/lib/server/api/emails/login-verification.email.ts new file mode 100644 index 0000000..7dd7819 --- /dev/null +++ b/src/lib/server/api/emails/login-verification.email.ts @@ -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*/ ` + + + + + Message + + +

Verify your email address

+

+ 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.

+
+

Verification Code

+

${this.token}

+

(This code is valid for 15 minutes)

+
+ + + + ` + } +} diff --git a/src/lib/server/api/emails/welcome.email.ts b/src/lib/server/api/emails/welcome.email.ts new file mode 100644 index 0000000..53a394f --- /dev/null +++ b/src/lib/server/api/emails/welcome.email.ts @@ -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*/ ` + + + + + Message + + +

Welcome to Example

+

+ 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.

+ + + + `; + } +} + diff --git a/src/lib/server/api/interfaces/email.interface.ts b/src/lib/server/api/interfaces/email.interface.ts new file mode 100644 index 0000000..876f6e7 --- /dev/null +++ b/src/lib/server/api/interfaces/email.interface.ts @@ -0,0 +1,7 @@ + + + +export interface Email { + subject(): string + html(): string; +} diff --git a/src/lib/server/api/services/mailer.service.ts b/src/lib/server/api/services/mailer.service.ts index 368fdb9..a4e7557 100644 --- a/src/lib/server/api/services/mailer.service.ts +++ b/src/lib/server/api/services/mailer.service.ts @@ -1,104 +1,44 @@ -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 = { - to: string | string[]; - props: T; -}; + email: Email; +} @injectable() export class MailerService { - private nodemailer = nodemailer.createTransport({ - host: 'smtp.ethereal.email', - port: 587, - secure: false, // Use `true` for port 465, `false` for all other ports - auth: { - 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) { - 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) { - 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" ', // sender address + 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 + auth: { + user: 'adella.hoppe@ethereal.email', + pass: 'dshNQZYhATsdJ3ENke' + } + }).sendMail({ + from: '"Example" ', 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 }); + } } }