feat: add user createOne and refactor code

This commit is contained in:
2026-02-23 09:19:34 +03:30
parent 72a001a150
commit 63b4567166
13 changed files with 192 additions and 110 deletions
+3 -1
View File
@@ -6,6 +6,8 @@ import { OtpModule } from '../otp/otp.module';
import { HashingModule } from '@/common/modules/hashing/hashing.module';
import { JwtModule } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { LoginProvider } from './providers/login.provider';
import { GenerateTokenProvider } from './providers/generate-token.provider';
@Module({
imports: [
@@ -25,6 +27,6 @@ import { ConfigService } from '@nestjs/config';
}),
],
controllers: [AuthController],
providers: [AuthService],
providers: [AuthService, GenerateTokenProvider, LoginProvider],
})
export class AuthModule {}
+3 -1
View File
@@ -4,7 +4,7 @@ import { Transform } from 'class-transformer';
import { IsNotEmpty, IsOptional, IsString, Matches } from 'class-validator';
import { i18nValidationMessage as t } from 'nestjs-i18n';
export class LoginDTO {
export class PhoneDTO {
@ApiProperty({
type: 'string',
description: `User's Phone Number`,
@@ -24,7 +24,9 @@ export class LoginDTO {
})
@Transform(({ value }) => Utils.StringUtils.normalizePhone(value as string))
phone: string;
}
export class LoginDTO extends PhoneDTO {
@ApiProperty({
type: 'string',
description: 'Password for User Account',
+13
View File
@@ -0,0 +1,13 @@
import { IsNotEmpty, IsString } from 'class-validator';
import { PhoneDTO } from './login.dto';
import { i18nValidationMessage as t } from 'nestjs-i18n';
export class VerifyOtpDTO extends PhoneDTO {
@IsString()
@IsNotEmpty({
message: t('validation.requiredField', {
field: '$t(auth.fields.otp)',
}),
})
otp: string;
}
@@ -2,6 +2,6 @@ POST http://localhost:3000/users/login
Content-Type: application/json
{
"phone": "09333026363",
"phone": "09126093033",
"password": "rad1.3.5"
}
@@ -0,0 +1,9 @@
export interface AccessTokenPayload {
sub: number;
phone: string;
}
export interface RefreshTokenPayload {
sub: number;
jti: string;
}
+5 -76
View File
@@ -1,88 +1,17 @@
import { UsersService } from '@/modules/users/providers/users.service';
import { Inject, Injectable, UnauthorizedException } from '@nestjs/common';
import { Injectable } from '@nestjs/common';
import { LoginDTO } from '../dtos/login.dto';
import { OtpService } from '@/modules/otp/providers/otp.service';
import { HashingProvider } from '@/common/modules/hashing/providers/hashing.provider';
import { JwtService } from '@nestjs/jwt';
import type { ConfigType } from '@nestjs/config';
import jwtConfig from '@/config/jwt.config';
import { LoginProvider } from './login.provider';
@Injectable()
export class AuthService {
constructor(
/**
* Inject Users Service
* Inject Login Provider
*/
private readonly usersService: UsersService,
/**
* Inject OTP Service
*/
private readonly otpService: OtpService,
/**
* Inject Hashing Provider
*/
private readonly hashingProvider: HashingProvider,
/**
* Inject Jwt Service
*/
private readonly jwtService: JwtService,
/**
* Inject Jwt Config
*/
@Inject(jwtConfig.KEY)
private readonly jwtConfiguration: ConfigType<typeof jwtConfig>,
private readonly loginProvider: LoginProvider,
) {}
public async logIn(loginDto: LoginDTO) {
const { phone, password } = loginDto;
const user = await this.usersService.findOneByPhone(phone);
if (!user) {
// TODO: Send OTP
const otpCode = await this.otpService.createOTP(phone);
return {
newUser: true,
message: `Otp sent to ${phone}`,
// Just for development
code: otpCode,
};
}
if (!password) {
return {
newUser: false,
message: 'Please send password',
};
}
const isPasswordCorrect = await this.hashingProvider.compare(
password,
user.password || '',
);
if (isPasswordCorrect) {
const tokenPayload = {
sub: user.id,
phone: user.phone,
};
const accessToken = await this.jwtService.signAsync(tokenPayload);
const refreshToken = await this.jwtService.signAsync(tokenPayload, {
secret: this.jwtConfiguration.refresh.secret,
expiresIn: this.jwtConfiguration.refresh.expiresIn,
});
return { access: accessToken, refresh: refreshToken };
}
throw new UnauthorizedException(
'Entered phone number or password is wrong.',
);
return await this.loginProvider.logIn(loginDto);
}
}
@@ -0,0 +1,67 @@
import jwtConfig from '@/config/jwt.config';
import { User } from '@/modules/users/user.entity';
import { Inject, Injectable } from '@nestjs/common';
import type { ConfigType } from '@nestjs/config';
import { JwtService } from '@nestjs/jwt';
import {
AccessTokenPayload,
RefreshTokenPayload,
} from '../interfaces/jwt.interface';
@Injectable()
export class GenerateTokenProvider {
constructor(
/**
* Inject Jwt Service
*/
private readonly jwtService: JwtService,
/**
* Inject Jwt Config
*/
@Inject(jwtConfig.KEY)
private readonly jwtConfiguration: ConfigType<typeof jwtConfig>,
) {}
public async signToken<T>(
userId: number,
expiresIn: number,
secret?: string,
payload?: T,
) {
return await this.jwtService.signAsync(
{
sub: userId,
...payload,
},
{
audience: this.jwtConfiguration.audience,
issuer: this.jwtConfiguration.issuer,
secret,
expiresIn,
},
);
}
public async generateTokens(user: User, jti: string) {
const [access, refresh] = await Promise.all([
// Sign Access Token
this.signToken<Partial<AccessTokenPayload>>(
user.id,
this.jwtConfiguration.access.expiresIn,
this.jwtConfiguration.access.secret,
{ phone: user.phone },
),
// Sign Refresh Token
this.signToken<Partial<RefreshTokenPayload>>(
user.id,
this.jwtConfiguration.refresh.expiresIn,
this.jwtConfiguration.refresh.secret,
{ jti },
),
]);
return { access, refresh };
}
}
@@ -0,0 +1,70 @@
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { LoginDTO } from '../dtos/login.dto';
import { UsersService } from '@/modules/users/providers/users.service';
import { OtpService } from '@/modules/otp/providers/otp.service';
import { HashingProvider } from '@/common/modules/hashing/providers/hashing.provider';
import { GenerateTokenProvider } from './generate-token.provider';
import { randomUUID } from 'crypto';
@Injectable()
export class LoginProvider {
constructor(
/**
* Inject Users Service
*/
private readonly usersService: UsersService,
/**
* Inject OTP Service
*/
private readonly otpService: OtpService,
/**
* Inject Hashing Provider
*/
private readonly hashingProvider: HashingProvider,
/**
* Inject GenerateToken Provider
*/
private readonly generateTokenProvider: GenerateTokenProvider,
) {}
public async logIn(loginDto: LoginDTO) {
const { phone, password } = loginDto;
const user = await this.usersService.findOneByPhone(phone);
if (!user) {
// TODO: Send OTP
const otpCode = await this.otpService.createOTP(phone);
return {
newUser: true,
message: `Otp sent to ${phone}`,
// Just for development
code: otpCode,
};
}
if (!password) {
return {
newUser: false,
message: 'Please send password',
};
}
const isPasswordCorrect = await this.hashingProvider.compare(
password,
user.password || '',
);
if (isPasswordCorrect) {
const jti = randomUUID();
return await this.generateTokenProvider.generateTokens(user, jti);
}
throw new UnauthorizedException(
'Entered phone number or password is wrong.',
);
}
}
+2 -3
View File
@@ -2,7 +2,6 @@ import { Injectable, RequestTimeoutException } from '@nestjs/common';
import { Repository } from 'typeorm';
import { User } from '../user.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { CreateUserDTO } from '../dto/create-user.dto';
@Injectable()
export class UsersService {
@@ -25,8 +24,8 @@ export class UsersService {
}
}
public async createOne(createUserDto: CreateUserDTO) {
const newUser = this.userRepository.create(createUserDto);
public async createOne(phone: string) {
const newUser = this.userRepository.create({ phone });
try {
await this.userRepository.save(newUser);
+13 -21
View File
@@ -1,12 +1,4 @@
import {
Body,
Controller,
Get,
Param,
ParseIntPipe,
Post,
} from '@nestjs/common';
import { CreateUserDTO } from './dto/create-user.dto';
import { Controller } from '@nestjs/common';
import { UsersService } from './providers/users.service';
@Controller('users')
@@ -18,18 +10,18 @@ export class UsersController {
private readonly usersService: UsersService,
) {}
@Get()
public getAllUsers() {
return ['user1', 'user2'];
}
// @Get()
// public getAllUsers() {
// return ['user1', 'user2'];
// }
@Get(':id')
public getUserById(@Param('id', ParseIntPipe) id: number) {
return `user ${id}`;
}
// @Get(':id')
// public getUserById(@Param('id', ParseIntPipe) id: number) {
// return `user ${id}`;
// }
@Post()
public async createUser(@Body() createUserDto: CreateUserDTO) {
return await this.usersService.createOne(createUserDto);
}
// @Post()
// public async createUser(@Body() createUserDto: CreateUserDTO) {
// return await this.usersService.createOne(createUserDto);
// }
}