mailer configured
This commit is contained in:
parent
831ea0eeef
commit
67a302ec50
14
package-lock.json
generated
14
package-lock.json
generated
@ -21,7 +21,7 @@
|
|||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"handlebars": "^4.7.8",
|
"handlebars": "^4.7.8",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"nodemailer": "^6.9.9",
|
"nodemailer": "^6.10.0",
|
||||||
"otp-generator": "^4.0.1",
|
"otp-generator": "^4.0.1",
|
||||||
"passport-jwt": "^4.0.1",
|
"passport-jwt": "^4.0.1",
|
||||||
"pg": "^8.14.1",
|
"pg": "^8.14.1",
|
||||||
@ -7304,9 +7304,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/nodemailer": {
|
"node_modules/nodemailer": {
|
||||||
"version": "6.9.9",
|
"version": "6.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.9.tgz",
|
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.0.tgz",
|
||||||
"integrity": "sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA==",
|
"integrity": "sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
@ -15717,9 +15717,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"nodemailer": {
|
"nodemailer": {
|
||||||
"version": "6.9.9",
|
"version": "6.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.9.tgz",
|
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.0.tgz",
|
||||||
"integrity": "sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA=="
|
"integrity": "sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA=="
|
||||||
},
|
},
|
||||||
"nodemon": {
|
"nodemon": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
|
|||||||
@ -35,7 +35,7 @@
|
|||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"handlebars": "^4.7.8",
|
"handlebars": "^4.7.8",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"nodemailer": "^6.9.9",
|
"nodemailer": "^6.10.0",
|
||||||
"otp-generator": "^4.0.1",
|
"otp-generator": "^4.0.1",
|
||||||
"passport-jwt": "^4.0.1",
|
"passport-jwt": "^4.0.1",
|
||||||
"pg": "^8.14.1",
|
"pg": "^8.14.1",
|
||||||
|
|||||||
@ -21,6 +21,8 @@ import { TicketModule } from './ticket/ticket.module';
|
|||||||
import { TicketPricingModule } from './ticketPricing/ticketPricing.module';
|
import { TicketPricingModule } from './ticketPricing/ticketPricing.module';
|
||||||
import { TimeSlotModule } from './timeSlot/timeSlot.module';
|
import { TimeSlotModule } from './timeSlot/timeSlot.module';
|
||||||
import { PushSubscriptionModule } from './push-subscription/push-subscription.module';
|
import { PushSubscriptionModule } from './push-subscription/push-subscription.module';
|
||||||
|
import { MailerModule } from './node-mailer/mailer.module';
|
||||||
|
import { ConfigModule } from '@nestjs/config';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@ -40,7 +42,9 @@ import { PushSubscriptionModule } from './push-subscription/push-subscription.mo
|
|||||||
TicketPricingModule,
|
TicketPricingModule,
|
||||||
TimeSlotModule,
|
TimeSlotModule,
|
||||||
UserModule,
|
UserModule,
|
||||||
PushSubscriptionModule
|
PushSubscriptionModule,
|
||||||
|
MailerModule,
|
||||||
|
ConfigModule.forRoot()
|
||||||
|
|
||||||
],
|
],
|
||||||
controllers: [AppController, AppConfigController],
|
controllers: [AppController, AppConfigController],
|
||||||
|
|||||||
20
src/node-mailer/mailer.controller.spec.ts
Normal file
20
src/node-mailer/mailer.controller.spec.ts
Normal file
@ -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>(MailerController);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(controller).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
63
src/node-mailer/mailer.controller.ts
Normal file
63
src/node-mailer/mailer.controller.ts
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/node-mailer/mailer.interface.ts
Normal file
10
src/node-mailer/mailer.interface.ts
Normal file
@ -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<string, string>;
|
||||||
|
};
|
||||||
12
src/node-mailer/mailer.module.ts
Normal file
12
src/node-mailer/mailer.module.ts
Normal file
@ -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 {}
|
||||||
18
src/node-mailer/mailer.service.spec.ts
Normal file
18
src/node-mailer/mailer.service.spec.ts
Normal file
@ -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>(MailerService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(service).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
57
src/node-mailer/mailer.service.ts
Normal file
57
src/node-mailer/mailer.service.ts
Normal file
@ -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<string>('MAIL_HOST'),
|
||||||
|
auth: {
|
||||||
|
user: this.configService.get<string>('MAIL_USER'),
|
||||||
|
pass: this.configService.get<string>('MAIL_PASS'),
|
||||||
|
},
|
||||||
|
secure: true,
|
||||||
|
port: this.configService.get<number>('MAIL_PORT'),
|
||||||
|
});
|
||||||
|
return transporter;
|
||||||
|
}
|
||||||
|
|
||||||
|
template(html: string, replacements: Record<string, string>) {
|
||||||
|
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<string>('APP_NAME');
|
||||||
|
const fromAddress =
|
||||||
|
from?.address ?? this.configService.get<string>('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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
62
src/node-mailer/templates/emailTemplate.html
Normal file
62
src/node-mailer/templates/emailTemplate.html
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Arogya Hrudaya Registration</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-color: #ffffff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
padding: 10px 0;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 8px 8px 0 0;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
padding: 20px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
text-align: center;
|
||||||
|
padding: 10px 0;
|
||||||
|
color: #777;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: #4CAF50;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<h1>Codevice Solutions Private Limited</h1>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<h2>Hello, {{name}}!</h2>
|
||||||
|
<p>Use the following OTP to login. OTP is valid for 5 mins. Do not share this OTP to anyone.</p>
|
||||||
|
<h2>{{otp}}</h2>
|
||||||
|
<p>Best regards,<br>CSPL</p>
|
||||||
|
</div>
|
||||||
|
<div class="footer">
|
||||||
|
<p>© 2025 Codevice Solutions Pvt. Ltd. All rights reserved.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
Reference in New Issue
Block a user