From 67a302ec507d417119f49f16780997fe7398f508 Mon Sep 17 00:00:00 2001 From: vv-01 Date: Thu, 20 Mar 2025 17:52:53 +0530 Subject: [PATCH] mailer configured --- package-lock.json | 14 ++--- package.json | 2 +- src/app.module.ts | 6 +- src/node-mailer/mailer.controller.spec.ts | 20 +++++++ src/node-mailer/mailer.controller.ts | 63 ++++++++++++++++++++ src/node-mailer/mailer.interface.ts | 10 ++++ src/node-mailer/mailer.module.ts | 12 ++++ src/node-mailer/mailer.service.spec.ts | 18 ++++++ src/node-mailer/mailer.service.ts | 57 ++++++++++++++++++ src/node-mailer/templates/emailTemplate.html | 62 +++++++++++++++++++ 10 files changed, 255 insertions(+), 9 deletions(-) create mode 100644 src/node-mailer/mailer.controller.spec.ts create mode 100644 src/node-mailer/mailer.controller.ts create mode 100644 src/node-mailer/mailer.interface.ts create mode 100644 src/node-mailer/mailer.module.ts create mode 100644 src/node-mailer/mailer.service.spec.ts create mode 100644 src/node-mailer/mailer.service.ts create mode 100644 src/node-mailer/templates/emailTemplate.html diff --git a/package-lock.json b/package-lock.json index 5b8cc98..ea547a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "dotenv": "^16.3.1", "handlebars": "^4.7.8", "moment": "^2.30.1", - "nodemailer": "^6.9.9", + "nodemailer": "^6.10.0", "otp-generator": "^4.0.1", "passport-jwt": "^4.0.1", "pg": "^8.14.1", @@ -7304,9 +7304,9 @@ "license": "MIT" }, "node_modules/nodemailer": { - "version": "6.9.9", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.9.tgz", - "integrity": "sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.0.tgz", + "integrity": "sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA==", "engines": { "node": ">=6.0.0" } @@ -15717,9 +15717,9 @@ "dev": true }, "nodemailer": { - "version": "6.9.9", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.9.tgz", - "integrity": "sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA==" + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.0.tgz", + "integrity": "sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA==" }, "nodemon": { "version": "3.0.2", diff --git a/package.json b/package.json index a967785..b66105e 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "dotenv": "^16.3.1", "handlebars": "^4.7.8", "moment": "^2.30.1", - "nodemailer": "^6.9.9", + "nodemailer": "^6.10.0", "otp-generator": "^4.0.1", "passport-jwt": "^4.0.1", "pg": "^8.14.1", diff --git a/src/app.module.ts b/src/app.module.ts index d3abbef..41aa883 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -21,6 +21,8 @@ import { TicketModule } from './ticket/ticket.module'; import { TicketPricingModule } from './ticketPricing/ticketPricing.module'; import { TimeSlotModule } from './timeSlot/timeSlot.module'; import { PushSubscriptionModule } from './push-subscription/push-subscription.module'; +import { MailerModule } from './node-mailer/mailer.module'; +import { ConfigModule } from '@nestjs/config'; @Module({ imports: [ @@ -40,7 +42,9 @@ import { PushSubscriptionModule } from './push-subscription/push-subscription.mo TicketPricingModule, TimeSlotModule, UserModule, - PushSubscriptionModule + PushSubscriptionModule, + MailerModule, + ConfigModule.forRoot() ], controllers: [AppController, AppConfigController], diff --git a/src/node-mailer/mailer.controller.spec.ts b/src/node-mailer/mailer.controller.spec.ts new file mode 100644 index 0000000..d948a9b --- /dev/null +++ b/src/node-mailer/mailer.controller.spec.ts @@ -0,0 +1,20 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { MailerController } from './mailer.controller'; +import { MailerService } from './mailer.service'; + +describe('MailerController', () => { + let controller: MailerController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [MailerController], + providers: [MailerService], + }).compile(); + + controller = module.get(MailerController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/node-mailer/mailer.controller.ts b/src/node-mailer/mailer.controller.ts new file mode 100644 index 0000000..c74d963 --- /dev/null +++ b/src/node-mailer/mailer.controller.ts @@ -0,0 +1,63 @@ +import { + Body, + Controller, + // Post, + // UsePipes, + // ValidationPipe, +} from '@nestjs/common'; +import { MailerService } from './mailer.service'; +// import { ApiResponse } from '@nestjs/swagger'; +// import { SuccessResponse, ErrorResponse } from '../utils/response.util'; +import { SendMailerDto } from './mailer.interface'; +import { Address } from 'nodemailer/lib/mailer'; +import * as path from 'path'; +import * as fs from 'fs'; + +@Controller('mailer') +export class MailerController { + constructor(private readonly mailerService: MailerService) {} + + // @Post('send-email') + // @ApiResponse({ + // status: 201, + // description: 'Email sent successfully', + // type: SuccessResponse, + // }) + // @ApiResponse({ + // status: 400, + // description: 'Bad Request', + // type: ErrorResponse, + // }) + // @UsePipes(new ValidationPipe({ whitelist: true })) + async sendEmail( + @Body() + body: { + from?: Address; + name: string; + email: string; + subject?: string; + otp: string; + }, + ) { + // const htmlTemplate = fs.readFileSync('emailTemplate.html', 'utf8'); + const templatePath = path.join( + process.cwd(), + 'src\\node-mailer\\templates\\emailTemplate.html', + ); + + if (!fs.existsSync(templatePath)) { + throw new Error(`Template file not found at: ${templatePath}`); + } + + const htmlTemplate = fs.readFileSync(templatePath, 'utf8'); + const sendDto: SendMailerDto = { + from: body.from, + reciepients: [{ name: body.name, address: body.email }], + subject: 'Registration OTP', + html: htmlTemplate + .replace('{{name}}', body.name) + .replace('{{otp}}', body.otp), + }; + return this.mailerService.sendMail(sendDto); + } +} diff --git a/src/node-mailer/mailer.interface.ts b/src/node-mailer/mailer.interface.ts new file mode 100644 index 0000000..de0d2a3 --- /dev/null +++ b/src/node-mailer/mailer.interface.ts @@ -0,0 +1,10 @@ +import { Address } from 'nodemailer/lib/mailer'; + +export type SendMailerDto = { + from?: Address; + reciepients: Address[]; + subject: string; + text?: string; + html: string; + placeholderReplacements?: Record; +}; diff --git a/src/node-mailer/mailer.module.ts b/src/node-mailer/mailer.module.ts new file mode 100644 index 0000000..9a42c34 --- /dev/null +++ b/src/node-mailer/mailer.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { MailerService } from './mailer.service'; +import { MailerController } from './mailer.controller'; + +@Module({ + imports: [ConfigModule], + controllers: [MailerController], + providers: [MailerService, MailerController], + exports: [MailerService, MailerController], +}) +export class MailerModule {} diff --git a/src/node-mailer/mailer.service.spec.ts b/src/node-mailer/mailer.service.spec.ts new file mode 100644 index 0000000..1ab5e82 --- /dev/null +++ b/src/node-mailer/mailer.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { MailerService } from './mailer.service'; + +describe('MailerService', () => { + let service: MailerService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [MailerService], + }).compile(); + + service = module.get(MailerService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/node-mailer/mailer.service.ts b/src/node-mailer/mailer.service.ts new file mode 100644 index 0000000..31ee1f9 --- /dev/null +++ b/src/node-mailer/mailer.service.ts @@ -0,0 +1,57 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import * as nodemailer from 'nodemailer'; +import { SendMailerDto } from './mailer.interface'; +import Mail from 'nodemailer/lib/mailer'; + +@Injectable() +export class MailerService { + constructor( + private readonly configService: ConfigService + ) {} + mailTransport() { + const transporter = nodemailer.createTransport({ + service: 'gmail', + host: this.configService.get('MAIL_HOST'), + auth: { + user: this.configService.get('MAIL_USER'), + pass: this.configService.get('MAIL_PASS'), + }, + secure: true, + port: this.configService.get('MAIL_PORT'), + }); + return transporter; + } + + template(html: string, replacements: Record) { + return html.replace(/%(\w*)%/g, function (m, key) { + return replacements.hasOwnProperty(key) ? replacements[key] : ''; + }); + } + + async sendMail(dto: SendMailerDto) { + const { from, reciepients, subject } = dto; + const html = dto.placeholderReplacements + ? this.template(dto.html, dto.placeholderReplacements) + : dto.html; + const transporter = this.mailTransport(); + const fromName = from?.name ?? this.configService.get('APP_NAME'); + const fromAddress = + from?.address ?? this.configService.get('MAIL_USER'); + const options: Mail.Options = { + from: { + name: fromName, + address: fromAddress, + }, + to: reciepients, + subject, + html: this.template(html, dto.placeholderReplacements || {}), + }; + try { + const result = await transporter.sendMail(options); + return result; + } catch (er) { + console.log(er); + } + } +} diff --git a/src/node-mailer/templates/emailTemplate.html b/src/node-mailer/templates/emailTemplate.html new file mode 100644 index 0000000..134ef1e --- /dev/null +++ b/src/node-mailer/templates/emailTemplate.html @@ -0,0 +1,62 @@ + + + + + + Arogya Hrudaya Registration + + + +
+
+

Codevice Solutions Private Limited

+
+
+

Hello, {{name}}!

+

Use the following OTP to login. OTP is valid for 5 mins. Do not share this OTP to anyone.

+

{{otp}}

+

Best regards,
CSPL

+
+ +
+ +