-
Notifications
You must be signed in to change notification settings - Fork 192
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #354 from King-Mikaelson/feat/email
feat: Implement email sending functionalities
- Loading branch information
Showing
11 changed files
with
2,835 additions
and
41 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export interface ArticleInterface { | ||
title: string; | ||
description: string; | ||
link: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { Module } from '@nestjs/common'; | ||
import { EmailService } from './email.service'; | ||
|
||
@Module({ | ||
providers: [EmailService], | ||
exports: [EmailService], | ||
}) | ||
export class EmailModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { EmailService } from './email.service'; | ||
import { MailerService } from '@nestjs-modules/mailer'; | ||
|
||
describe('EmailService', () => { | ||
let service: EmailService; | ||
let mailerService: MailerService; | ||
|
||
const mockMailerService = { | ||
sendMail: jest.fn().mockResolvedValue({}), | ||
}; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [ | ||
EmailService, | ||
{ | ||
provide: MailerService, | ||
useValue: mockMailerService, | ||
}, | ||
], | ||
}).compile(); | ||
|
||
service = module.get<EmailService>(EmailService); | ||
mailerService = module.get<MailerService>(MailerService); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(service).toBeDefined(); | ||
}); | ||
|
||
it('should send user confirmation email', async () => { | ||
const email = '[email protected]'; | ||
const url = 'http://example.com/confirm'; | ||
const token = 'mike'; | ||
const link = `${url}?token=${token}`; | ||
|
||
await service.sendUserConfirmationMail(email, url, token); | ||
|
||
expect(mailerService.sendMail).toHaveBeenCalledWith({ | ||
to: email, | ||
subject: 'Welcome to My App! Confirm your Email', | ||
template: 'confirmation', | ||
context: { | ||
link, | ||
email, | ||
}, | ||
}); | ||
}); | ||
|
||
it('should send forgot password email', async () => { | ||
const email = '[email protected]'; | ||
const url = 'http://example.com/reset'; | ||
const token = 'mike'; | ||
const link = `${url}?token=${token}`; | ||
|
||
await service.sendForgotPasswordMail(email, url, token); | ||
|
||
expect(mailerService.sendMail).toHaveBeenCalledWith({ | ||
to: email, | ||
subject: 'Reset Password', | ||
template: 'reset-password', | ||
context: { | ||
link, | ||
email, | ||
}, | ||
}); | ||
}); | ||
|
||
it('should send waitlist confirmation email', async () => { | ||
const email = '[email protected]'; | ||
const url = 'http://example.com/waitlist'; | ||
|
||
await service.sendWaitListMail(email, url); | ||
|
||
expect(mailerService.sendMail).toHaveBeenCalledWith({ | ||
to: email, | ||
subject: 'Waitlist Confirmation', | ||
template: 'waitlist', | ||
context: { | ||
url, | ||
email, | ||
}, | ||
}); | ||
}); | ||
|
||
it('should send newsletter email', async () => { | ||
const email = '[email protected]'; | ||
const articles = [ | ||
{ | ||
title: 'Article Title 1', | ||
description: 'Short description of the article.', | ||
link: 'https://example.com/article1', | ||
}, | ||
{ | ||
title: 'Article Title 2', | ||
description: 'Short description of the article.', | ||
link: 'https://example.com/article2', | ||
}, | ||
]; | ||
await service.sendNewsLetterMail(email, articles); | ||
|
||
expect(mailerService.sendMail).toHaveBeenCalledWith({ | ||
to: email, | ||
subject: 'Monthly Newsletter', | ||
template: 'newsletter', | ||
context: { | ||
email, | ||
articles, | ||
}, | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { MailerService } from '@nestjs-modules/mailer'; | ||
import { ArticleInterface } from './article.interface'; | ||
|
||
@Injectable() | ||
export class EmailService { | ||
constructor(private readonly mailerService: MailerService) {} | ||
|
||
async sendUserConfirmationMail(email: string, url: string, token: string) { | ||
const link = `${url}?token=${token}`; | ||
await this.mailerService.sendMail({ | ||
to: email, | ||
subject: 'Welcome to My App! Confirm your Email', | ||
template: 'confirmation', | ||
context: { | ||
link, | ||
email, | ||
}, | ||
}); | ||
} | ||
|
||
async sendForgotPasswordMail(email: string, url: string, token: string) { | ||
const link = `${url}?token=${token}`; | ||
await this.mailerService.sendMail({ | ||
to: email, | ||
subject: 'Reset Password', | ||
template: 'reset-password', | ||
context: { | ||
link, | ||
email, | ||
}, | ||
}); | ||
} | ||
|
||
async sendWaitListMail(email: string, url: string) { | ||
await this.mailerService.sendMail({ | ||
to: email, | ||
subject: 'Waitlist Confirmation', | ||
template: 'waitlist', | ||
context: { | ||
url, | ||
email, | ||
}, | ||
}); | ||
} | ||
|
||
async sendNewsLetterMail(email: string, articles: ArticleInterface[]) { | ||
await this.mailerService.sendMail({ | ||
to: email, | ||
subject: 'Monthly Newsletter', | ||
template: 'newsletter', | ||
context: { | ||
email, | ||
articles, | ||
}, | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
<html lang='en'> | ||
<head> | ||
<meta charset='UTF-8' /> | ||
<meta name='viewport' content='width=device-width, initial-scale=1.0' /> | ||
<title>Email Confirmation</title> | ||
<style> | ||
body { font-family: Arial, sans-serif; background-color: #f4f4f4; margin: 0; padding: 0; } .container { width: | ||
100%; max-width: 600px; margin: 0 auto; background-color: #ffffff; padding: 20px; border-radius: 8px; box-shadow: | ||
0 0 10px rgba(0, 0, 0, 0.1); } .header { text-align: center; padding-bottom: 20px; } .header img { width: 120px; | ||
/* Adjust based on your logo */ } .content { text-align: center; padding: 20px; } .content h1 { color: #333; /* | ||
Dark gray for readability */ font-size: 24px; margin-bottom: 15px; } .content p { color: #666; /* Light gray for | ||
softer text */ font-size: 16px; line-height: 1.5; margin-bottom: 20px; } .btn { display: inline-block; padding: | ||
12px 25px; color: #ffffff; background-color: #28a745; /* Secondary brand color */ text-decoration: none; | ||
border-radius: 5px; font-weight: bold; font-size: 16px; margin-top: 20px; } .footer { text-align: center; | ||
padding-top: 20px; font-size: 12px; color: #888888; } .footer a { color: #28a745; /* Match button color for | ||
consistency */ text-decoration: none; } | ||
</style> | ||
</head> | ||
<body> | ||
<div class='container'> | ||
<div class='header'> | ||
<img | ||
src='https://www.shutterstock.com/image-vector/circle-line-simple-design-logo-600nw-2174926871.jpg' | ||
alt='Company Logo' | ||
/> | ||
</div> | ||
<div class='content'> | ||
<h1>Confirm Your Email Address</h1> | ||
<p>Hello {{email}},</p> | ||
<p>Thank you for registering. Please confirm your email address by clicking the button below:</p> | ||
<a href='{{link}}' class='btn'>Confirm Email</a> | ||
<p>If you didn't create an account with us, you can safely ignore this email.</p> | ||
</div> | ||
<div class='footer'> | ||
<p>© 2024 Company Name. All rights reserved.</p> | ||
<p><a href='https://example.com/unsubscribe'>Unsubscribe</a></p> | ||
</div> | ||
</div> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
<html lang='en'> | ||
<head> | ||
<meta charset='UTF-8' /> | ||
<meta name='viewport' content='width=device-width, initial-scale=1.0' /> | ||
<title>Newsletter</title> | ||
<style> | ||
body { font-family: Arial, sans-serif; background-color: #f4f4f4; margin: 0; padding: 0; } .container { width: | ||
100%; max-width: 600px; margin: 0 auto; background-color: #ffffff; padding: 20px; border-radius: 8px; box-shadow: | ||
0 0 10px rgba(0, 0, 0, 0.1); } .header { text-align: center; padding-bottom: 20px; } .header img { width: 120px; | ||
/* Adjust based on your logo */ } .content { padding: 20px; } .content h1 { color: #333; /* Dark gray for | ||
readability */ font-size: 24px; margin-bottom: 15px; } .content p { color: #666; /* Light gray for softer text */ | ||
font-size: 16px; line-height: 1.5; margin-bottom: 20px; } .content .article { margin-bottom: 20px; } .content | ||
.article h2 { font-size: 20px; color: #007bff; /* Primary brand color for headers */ margin-bottom: 10px; } | ||
.content .article p { font-size: 14px; color: #666; } .content .article a { color: #007bff; /* Match header color | ||
*/ text-decoration: none; } .footer { text-align: center; padding-top: 20px; font-size: 12px; color: #888888; } | ||
.footer a { color: #007bff; /* Match brand color */ text-decoration: none; } | ||
</style> | ||
</head> | ||
<body> | ||
<div class='container'> | ||
<div class='header'> | ||
<img | ||
src='https://www.shutterstock.com/image-vector/circle-line-simple-design-logo-600nw-2174926871.jpg' | ||
alt='Company Logo' | ||
/> | ||
</div> | ||
<div class='content'> | ||
<h1>Monthly Newsletter</h1> | ||
<p>Hello {{email}},</p> | ||
{{#each articles}} | ||
<div class='article'> | ||
<h2>{{this.title}}</h2> | ||
<p>{{this.description}} <a href='{{this.link}}'>Read more</a></p> | ||
</div> | ||
{{/each}} | ||
</div> | ||
<div class='footer'> | ||
<p>© 2024 Company Name. All rights reserved.</p> | ||
<p><a href='https://example.com/unsubscribe'>Unsubscribe</a></p> | ||
</div> | ||
</div> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
<html lang='en'> | ||
<head> | ||
<meta charset='UTF-8' /> | ||
<meta name='viewport' content='width=device-width, initial-scale=1.0' /> | ||
<title>Forgot Password</title> | ||
<style> | ||
body { font-family: Arial, sans-serif; background-color: #f4f4f4; margin: 0; padding: 0; } .container { width: | ||
100%; max-width: 600px; margin: 0 auto; background-color: #ffffff; padding: 20px; border-radius: 8px; box-shadow: | ||
0 0 10px rgba(0, 0, 0, 0.1); } .header { text-align: center; padding-bottom: 20px; } .header img { width: 120px; | ||
/* Adjust based on your logo */ } .content { text-align: center; padding: 20px; } .content h1 { color: #333; /* | ||
Dark gray for readability */ font-size: 24px; margin-bottom: 15px; } .content p { color: #666; /* Light gray for | ||
softer text */ font-size: 16px; line-height: 1.5; margin-bottom: 20px; } .btn { display: inline-block; padding: | ||
12px 25px; color: #ffffff; background-color: #28a745; /* Secondary brand color */ text-decoration: none; | ||
border-radius: 5px; font-weight: bold; font-size: 16px; margin-top: 20px; } .footer { text-align: center; | ||
padding-top: 20px; font-size: 12px; color: #888888; } .footer a { color: #28a745; /* Match button color for | ||
consistency */ text-decoration: none; } | ||
</style> | ||
</head> | ||
<body> | ||
<div class='container'> | ||
<div class='header'> | ||
<img | ||
src='https://www.shutterstock.com/image-vector/circle-line-simple-design-logo-600nw-2174926871.jpg' | ||
alt='Company Logo' | ||
/> | ||
</div> | ||
<div class='content'> | ||
<h1>Forgot Password</h1> | ||
<p>Hello {{email}},</p> | ||
<p>It seems like you forgot your password. Click the button below to reset it:</p> | ||
<a href='{{link}}' class='btn'>Reset Password</a> | ||
<p>If you didn't request a password reset, please ignore this email.</p> | ||
</div> | ||
<div class='footer'> | ||
<p>© 2024 Company Name. All rights reserved.</p> | ||
<p><a href='https://example.com/unsubscribe'>Unsubscribe</a></p> | ||
</div> | ||
</div> | ||
</body> | ||
</html> |
Oops, something went wrong.