Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/email-verification/dto/verify-email.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {IsNotEmpty, IsString } from 'class-validator';

export class VerifyEmailDto {
@IsString()
@IsNotEmpty()
token: string;

}
8 changes: 8 additions & 0 deletions src/email-verification/email-verification.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { RequestEmailDto } from './dto/request-email.dto';
import { VerifyEmailDto } from './dto/verify-email.dto';
import { EmailVerificationService } from './email-verification.service';
import { Body, Controller, Post } from '@nestjs/common';

Expand All @@ -11,4 +12,11 @@ export class EmailVerificationController {
await this.emailVerificationService.sendEmail(dto.email);
return { message: 'Verification link sent to your email' };
}

@Post('verify-email')
async verifyEmail(@Body() dto: VerifyEmailDto) {
const email = await this.emailVerificationService.decodeVerificationToken(dto.token);
await this.emailVerificationService.verifyEmail(email);
return { message: 'The email is verified' };
}
}
3 changes: 2 additions & 1 deletion src/email-verification/email-verification.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import { EmailVerificationController } from './email-verification.controller';
import { EmailVerificationService } from './email-verification.service';
import { User } from '../user/entities/user.entity';
import { JwtService } from '@nestjs/jwt';
import { UserService } from 'user/user.service';

@Module({
imports: [TypeOrmModule.forFeature([EmailVerification, User])],
controllers: [EmailVerificationController],
providers: [EmailVerificationService, JwtService],
providers: [EmailVerificationService, JwtService, UserService],
})
export class EmailVerificationModule {}
26 changes: 25 additions & 1 deletion src/email-verification/email-verification.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { UserService } from './../user/user.service';
import { ConfigService } from '@nestjs/config';
import { JwtService } from '@nestjs/jwt';
import { Injectable } from '@nestjs/common';
import { BadRequestException, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { EmailVerification } from './entities/email-verification.entity';
Expand All @@ -17,6 +18,7 @@ export class EmailVerificationService {
private readonly userRepo: Repository<User>,
private readonly jwtService: JwtService,
private readonly configService: ConfigService,
private readonly userService: UserService,
private readonly mailService: MailService,
) {}

Expand Down Expand Up @@ -47,4 +49,26 @@ export class EmailVerificationService {

await this.emailVerificationRepo.save({ user, token, expires_at });
}

public async verifyEmail(email: string) {
await this.userService.confirmEmail(email);
}

public async decodeVerificationToken(token: string) {
try {
const payload = await this.jwtService.verify(token, {
secret: this.configService.get('JWT_SECRET'),
});

if (typeof payload === 'object' && 'email' in payload) {
return payload.email;
}
throw new BadRequestException('Invalid token payload structure');
} catch (error) {
if (error?.name === 'TokenExpiredError') {
throw new BadRequestException('Your email confirmation token has expired');
}
}
throw new BadRequestException('Failed to verify email token');
}
}
24 changes: 22 additions & 2 deletions src/user/user.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,24 @@
import { Injectable } from '@nestjs/common';
import { BadRequestException, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { Repository } from 'typeorm';

@Injectable()
export class UserService {}
export class UserService {
constructor(
@InjectRepository(User)
private readonly userRepo: Repository<User>,
) {}

async confirmEmail(email: string) {
const user = await this.userRepo.findOne({ where: { email } });
if (!user) throw new BadRequestException('User not found');

if (user.verified_at) {
throw new BadRequestException('Email already verified');
}

user.verified_at = new Date();
await this.userRepo.save(user);
}
}