Skip to content

Commit

Permalink
Merge pull request #22 from p-society/auth-otp
Browse files Browse the repository at this point in the history
Addition of Otp flow to project.
  • Loading branch information
zakhaev26 authored Dec 23, 2024
2 parents 7a90b3e + 67c9d1b commit ca15013
Show file tree
Hide file tree
Showing 33 changed files with 407 additions and 254 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"@socket.io/redis-adapter": "^8.3.0",
"@types/nodemailer": "^6.4.17",
"bcrypt": "^5.1.1",
"bcryptjs": "^2.4.3",
"bullmq": "^5.34.3",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
Expand All @@ -62,6 +63,7 @@
"@nestjs/schematics": "^10.1.4",
"@nestjs/testing": "^10.4.1",
"@types/bcrypt": "^5.0.2",
"@types/bcryptjs": "^2.4.6",
"@types/express": "^4.17.21",
"@types/jest": "^29.5.12",
"@types/lodash": "^4.17.13",
Expand Down
4 changes: 4 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { ReactionsModule } from './services/apis/reactions/reactions.module';
import { ProfilesModule } from './services/apis/profiles/profiles.module';
import { QueueModule } from './services/bullmq/queue.module';
import { BullModule } from '@nestjs/bullmq';
import { GenerateOtpModule } from './services/apis/otp/generateOtp.module';
import { MailerModule } from './services/apis/mailer/mailer.module';

@Module({
imports: [
Expand Down Expand Up @@ -43,6 +45,8 @@ import { BullModule } from '@nestjs/bullmq';
AdapterModule,
ProfilesModule,
ReactionsModule,
GenerateOtpModule,
MailerModule,
],
controllers: [AppController],
providers: [
Expand Down
15 changes: 15 additions & 0 deletions src/common/hashing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import * as bcrypt from 'bcryptjs';

export async function hashString(input: string): Promise<string> {
const saltRounds = 10;
const hashedString = await bcrypt.hash(input, saltRounds);
return hashedString;
}

export async function compareHashedString(
input: string,
hashedString: string,
): Promise<boolean> {
const isMatch = await bcrypt.compare(input, hashedString);
return isMatch;
}
7 changes: 0 additions & 7 deletions src/constants/kafkajs-consumer-options.ts

This file was deleted.

1 change: 1 addition & 0 deletions src/constants/otp.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const OTP_TTL = 10 * 60; // 10 minutes
10 changes: 10 additions & 0 deletions src/constants/sports-enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,14 @@ enum SportsEnum {
Basketball = 'Basketball',
Volleyball = 'Volleyball',
}

export const SportsEnumList = [
SportsEnum.Football,
SportsEnum.Badminton,
SportsEnum.Basketball,
SportsEnum.Cricket,
SportsEnum.Football,
SportsEnum.Volleyball,
];

export default SportsEnum;
40 changes: 20 additions & 20 deletions src/services/apis/mailer/mailer.controller.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import { Controller, Post } from '@nestjs/common';
import { MailerService } from './mailer.service';
// import { Controller, Post } from '@nestjs/common';
// import { MailerService } from './mailer.service';

@Controller('mail')
export class MailerController {
constructor(private mailerService: MailerService) {}
// @Controller('mail')
// export class MailerController {
// constructor(private mailerService: MailerService) {}

// @Public()
// @Post()
// async sendMail() {
// await this.mailerService.sendMPassGenerationEmail(
// '[email protected]',
// 'Saswat',
// 'XYZ'
// )
// await this.mailerService.sendSupportTicketResolvedEmail(
// '[email protected]',
// 'Saswat',
// '123456'
// )
// }
}
// // @Public()
// // @Post()
// // async sendMail() {
// // await this.mailerService.sendMPassGenerationEmail(
// // '[email protected]',
// // 'Saswat',
// // 'XYZ'
// // )
// // await this.mailerService.sendSupportTicketResolvedEmail(
// // '[email protected]',
// // 'Saswat',
// // '123456'
// // )
// // }
// }
14 changes: 10 additions & 4 deletions src/services/apis/mailer/mailer.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { MailerService } from './mailer.service';
import { MailerModule as NestMailerModule } from '@nestjs-modules/mailer';
import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter';
import * as path from 'path';
import { MailerController } from './mailer.controller';
// import { MailerController } from './mailer.controller';

@Global()
@Module({
Expand All @@ -24,15 +24,21 @@ import { MailerController } from './mailer.controller';
from: configService.get<string>('MAIL_FROM'),
},
template: {
dir: path.join(__dirname, 'templates'),
dir: path.join(
__dirname,
'../../../../src/services/apis/mailer/templates',
),
adapter: new HandlebarsAdapter(),
options: {
strict: true,
},
},
options: {
partials: {
dir: path.join(__dirname, 'templates/partials'),
dir: path.join(
__dirname,
'../../../../src/services/apis/mailer/templates/partials',
),
options: {
strict: true,
},
Expand All @@ -41,7 +47,7 @@ import { MailerController } from './mailer.controller';
}),
}),
],
controllers: [MailerController],
controllers: [],
providers: [MailerService],
exports: [MailerService],
})
Expand Down
15 changes: 9 additions & 6 deletions src/services/apis/mailer/mailer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,20 @@ export class MailerService {

async sendMail(
to: string,
subject: string,
content: string,
// subject: string,
// content: string,
otp: string,
template?: string,
context?: Record<string, any>,
//context?: Record<string, any>,
) {
await this.mailerService.sendMail({
to,
subject,
text: content,
subject: 'OTP for your account',
template: template ? `./${template}` : undefined,
context: context || {},
context: {
otp,
supportEmail: '[email protected]',
},
});
}
}
74 changes: 68 additions & 6 deletions src/services/apis/mailer/templates/partials/otp.hbs
Original file line number Diff line number Diff line change
@@ -1,6 +1,68 @@
<p> Use
the OTP below to complete the process:</p>
<div class='otp'> {{otp}} </div>
<p>This OTP is valid for 15 minutes. If you didn’t request this, please ignore
this email or contact support at
{{supportEmail}}</p>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 0;
}
.container {
max-width: 600px;
margin: 20px 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;
border-bottom: 1px solid #dddddd;
}
.header h1 {
margin: 0;
color: #333333;
}
.content {
padding: 20px 0;
}
.otp {
font-size: 24px;
font-weight: bold;
color: #333333;
text-align: center;
margin: 20px 0;
}
.footer {
text-align: center;
padding-top: 20px;
border-top: 1px solid #dddddd;
color: #777777;
}
.footer a {
color: #007BFF;
text-decoration: none;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Programming Society</h1>
</div>
<div class="content">
<p>Dear Member,</p>
<p>Use the OTP below to complete the process:</p>
<div class="otp">{{otp}}</div>
<p>This OTP is valid for 10 minutes. If you didn’t request this, please ignore this email or contact support at <a href="mailto:{{supportEmail}}">{{supportEmail}}</a>.</p>
</div>
<div class="footer">
<p>Thank you,<br>Programming Society Team</p>
</div>
</div>
</body>
</html>
23 changes: 23 additions & 0 deletions src/services/apis/otp/dto/generateOtp.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { date, z } from 'zod';

// DTO for incoming OTP payload. This ensures the payload structure and validates fields.
/**
* The email of the user to send OTP.
* @example "[email protected]"
* */

export const OtpValidation = z.object({
email: z.string().email(),
});

/**
* The email of the user to send OTP.
* @example "
* */
export const VerifyOtpValidation = z.object({
email: z.string().email(),
otp: z.string().trim(),
});

export type OtpDto = z.infer<typeof OtpValidation>;
export type VerifyOtpDto = z.infer<typeof VerifyOtpValidation>;
53 changes: 0 additions & 53 deletions src/services/apis/otp/dto/otp.dto.ts

This file was deleted.

30 changes: 30 additions & 0 deletions src/services/apis/otp/generateOtp.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Body, Controller, Post } from '@nestjs/common';
import { GenerateOtpService } from './generateOtp.service';
import {
OtpDto,
OtpValidation,
VerifyOtpDto,
VerifyOtpValidation,
} from './dto/generateOtp.dto';
import { Public } from '../auth/decorators/public.decorator';

@Controller('otp')
export class GenerateOtpController {
constructor(private readonly generateOtpService: GenerateOtpService) {}

@Public()
@Post('generate')
async create(@Body() generateOtpDto: OtpDto) {
generateOtpDto = OtpValidation.parse(generateOtpDto);
return await this.generateOtpService.enqueueOtpJob(generateOtpDto);
}

@Public()
@Post('verify')
async verify(@Body() verifyOtpDto: VerifyOtpDto) {
verifyOtpDto = VerifyOtpValidation.parse(verifyOtpDto);
return (await this.generateOtpService.compareOtp(verifyOtpDto))
? 'OTP is correct'
: 'OTP is incorrect';
}
}
11 changes: 11 additions & 0 deletions src/services/apis/otp/generateOtp.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { GenerateOtpController } from './generateOtp.controller';
import { GenerateOtpService } from './generateOtp.service';

@Module({
imports: [],
controllers: [GenerateOtpController],
providers: [GenerateOtpService],
exports: [GenerateOtpService], // not exporting services as no need in testing (??)
})
export class GenerateOtpModule {}
Loading

0 comments on commit ca15013

Please sign in to comment.