Merge branch 'dev' into create-vistor-password

This commit is contained in:
faris Aljohari
2024-08-12 12:57:29 +03:00
108 changed files with 2031 additions and 681 deletions

View File

@ -2,14 +2,16 @@ import { Module } from '@nestjs/common';
import { AuthenticationController } from './controllers/authentication.controller';
import { AuthenticationService } from './services/authentication.service';
import { ConfigModule } from '@nestjs/config';
import { UserRepositoryModule } from '../../libs/common/src/modules/user/user.repository.module';
import { UserRepositoryModule } from '@app/common/modules/user/user.repository.module';
import { CommonModule } from '../../libs/common/src';
import { UserAuthController } from './controllers';
import { UserAuthService } from './services';
import { UserRepository } from '../../libs/common/src/modules/user/repositories';
import { UserSessionRepository } from '../../libs/common/src/modules/session/repositories/session.repository';
import { UserOtpRepository } from '../../libs/common/src/modules/user-otp/repositories/user-otp.repository';
import { UserRoleRepository } from '@app/common/modules/user-role/repositories';
import { UserRepository } from '@app/common/modules/user/repositories';
import { UserSessionRepository } from '@app/common/modules/session/repositories/session.repository';
import {
UserRoleRepository,
UserOtpRepository,
} from '@app/common/modules/user/repositories';
import { RoleTypeRepository } from '@app/common/modules/role-type/repositories';
@Module({

View File

@ -1,5 +1,5 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
import { IsEmail, IsNotEmpty, IsOptional, IsString } from 'class-validator';
import { IsPasswordStrong } from 'src/validators/password.validator';
export class UserSignUpDto {
@ -38,4 +38,8 @@ export class UserSignUpDto {
@IsString()
@IsNotEmpty()
public lastName: string;
@IsString()
@IsOptional()
public regionUuid: string;
}

View File

@ -11,4 +11,9 @@ export class UserLoginDto {
@IsString()
@IsOptional()
password: string;
@ApiProperty()
@IsString()
@IsOptional()
regionUuid?: string;
}

View File

@ -1,6 +1,12 @@
import { OtpType } from '../../../libs/common/src/constants/otp-type.enum';
import { ApiProperty } from '@nestjs/swagger';
import { IsEmail, IsEnum, IsNotEmpty, IsString } from 'class-validator';
import {
IsEmail,
IsEnum,
IsNotEmpty,
IsOptional,
IsString,
} from 'class-validator';
export class UserOtpDto {
@ApiProperty()
@ -12,6 +18,11 @@ export class UserOtpDto {
@IsEnum(OtpType)
@IsNotEmpty()
type: OtpType;
@ApiProperty()
@IsOptional()
@IsString()
regionUuid?: string;
}
export class VerifyOtpDto extends UserOtpDto {

View File

@ -1,5 +1,5 @@
import { RoleTypeRepository } from './../../../libs/common/src/modules/role-type/repositories/role.type.repository';
import { UserRoleRepository } from './../../../libs/common/src/modules/user-role/repositories/user.role.repository';
import { UserRoleRepository } from './../../../libs/common/src/modules/user/repositories/user.repository';
import { UserRepository } from '../../../libs/common/src/modules/user/repositories';
import {
BadRequestException,
@ -12,12 +12,14 @@ import { HelperHashService } from '../../../libs/common/src/helper/services';
import { UserLoginDto } from '../dtos/user-login.dto';
import { AuthService } from '../../../libs/common/src/auth/services/auth.service';
import { UserSessionRepository } from '../../../libs/common/src/modules/session/repositories/session.repository';
import { UserOtpRepository } from '../../../libs/common/src/modules/user-otp/repositories/user-otp.repository';
import { UserOtpRepository } from '../../../libs/common/src/modules/user/repositories/user.repository';
import { ForgetPasswordDto, UserOtpDto, VerifyOtpDto } from '../dtos';
import { EmailService } from '../../../libs/common/src/util/email.service';
import { OtpType } from '../../../libs/common/src/constants/otp-type.enum';
import { UserEntity } from '../../../libs/common/src/modules/user/entities/user.entity';
import * as argon2 from 'argon2';
import { differenceInSeconds } from '@app/common/helper/differenceInSeconds';
import { LessThan, MoreThan } from 'typeorm';
@Injectable()
export class UserAuthService {
@ -44,9 +46,17 @@ export class UserAuthService {
);
try {
const { regionUuid, ...rest } = userSignUpDto;
const user = await this.userRepository.save({
...userSignUpDto,
...rest,
password: hashedPassword,
region: regionUuid
? {
uuid: regionUuid,
}
: {
regionName: 'United Arab Emirates',
},
});
return user;
@ -80,41 +90,49 @@ export class UserAuthService {
}
async userLogin(data: UserLoginDto) {
const user = await this.authService.validateUser(data.email, data.password);
try {
const user = await this.authService.validateUser(
data.email,
data.password,
data.regionUuid,
);
if (!user) {
throw new UnauthorizedException('Invalid login credentials.');
}
if (!user) {
throw new UnauthorizedException('Invalid login credentials.');
}
const session = await Promise.all([
await this.sessionRepository.update(
{ userId: user.id },
{
isLoggedOut: true,
},
),
await this.authService.createSession({
const session = await Promise.all([
await this.sessionRepository.update(
{ userId: user.id },
{
isLoggedOut: true,
},
),
await this.authService.createSession({
userId: user.uuid,
loginTime: new Date(),
isLoggedOut: false,
}),
]);
return await this.authService.login({
email: user.email,
userId: user.uuid,
loginTime: new Date(),
isLoggedOut: false,
}),
]);
return await this.authService.login({
email: user.email,
userId: user.uuid,
uuid: user.uuid,
roles: user?.roles?.map((role) => {
return { uuid: role.uuid, type: role.roleType.type };
}),
sessionId: session[1].uuid,
});
uuid: user.uuid,
roles: user?.roles?.map((role) => {
return { uuid: role.uuid, type: role.roleType.type };
}),
sessionId: session[1].uuid,
});
} catch (error) {
throw new BadRequestException('Invalid credentials');
}
}
async deleteUser(uuid: string) {
const user = await this.findOneById(uuid);
if (!user) {
throw new BadRequestException('User does not found');
throw new BadRequestException('User not found');
}
return await this.userRepository.update({ uuid }, { isActive: false });
}
@ -124,7 +142,55 @@ export class UserAuthService {
}
async generateOTP(data: UserOtpDto): Promise<string> {
await this.otpRepository.delete({ email: data.email, type: data.type });
const threeDaysAgo = new Date();
threeDaysAgo.setDate(threeDaysAgo.getDate() - 3);
const userExists = await this.userRepository.exists({
where: {
region: data.regionUuid
? {
uuid: data.regionUuid,
}
: undefined,
email: data.email,
isUserVerified: data.type === OtpType.PASSWORD ? true : undefined,
},
});
if (!userExists) {
throw new BadRequestException('User not found');
}
await this.otpRepository.softDelete({ email: data.email, type: data.type });
await this.otpRepository.delete({
email: data.email,
type: data.type,
createdAt: LessThan(threeDaysAgo),
});
const countOfOtp = await this.otpRepository.count({
withDeleted: true,
where: {
email: data.email,
type: data.type,
createdAt: MoreThan(threeDaysAgo),
},
});
const lastOtp = await this.otpRepository.findOne({
where: { email: data.email, type: data.type },
order: { createdAt: 'DESC' },
withDeleted: true,
});
const cooldown = 30 * Math.pow(2, countOfOtp - 1);
if (lastOtp) {
const now = new Date();
const timeSinceLastOtp = differenceInSeconds(now, lastOtp.createdAt);
if (timeSinceLastOtp < cooldown) {
throw new BadRequestException({
message: `Please wait ${cooldown - timeSinceLastOtp} more seconds before requesting a new OTP.`,
data: {
cooldown: cooldown - timeSinceLastOtp,
},
});
}
}
const otpCode = Math.floor(100000 + Math.random() * 900000).toString();
const expiryTime = new Date();
expiryTime.setMinutes(expiryTime.getMinutes() + 1);