enhance register

master
aliMohtarami 4 weeks ago
parent 65e3afd64f
commit d9be3397f7
  1. 6
      .vscode/settings.json
  2. 163
      package-lock.json
  3. 1
      package.json
  4. 4
      src/shop/shop.service.ts
  5. 1
      src/users/guard/auth.guard.ts
  6. 1
      src/users/guard/jwt.strategy.ts
  7. 39
      src/users/users.controller.ts
  8. 47
      src/users/users.service.ts

@ -1,3 +1,7 @@
{
"cSpell.words": ["zarinpal"]
"cSpell.words": [
"uuidv",
"zarinpal"
],
"CodeGPT.apiKey": "CodeGPT Plus Beta"
}

163
package-lock.json generated

@ -21,6 +21,7 @@
"bcryptjs": "^2.4.3",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"google-auth-library": "^9.15.1",
"ioredis": "^5.4.2",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
@ -3425,7 +3426,6 @@
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"dev": true,
"funding": [
{
"type": "github",
@ -3462,6 +3462,15 @@
"integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==",
"license": "MIT"
},
"node_modules/bignumber.js": {
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz",
"integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==",
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
@ -5242,6 +5251,12 @@
"type": "^2.7.2"
}
},
"node_modules/extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
"license": "MIT"
},
"node_modules/external-editor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
@ -5717,6 +5732,70 @@
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
"license": "ISC"
},
"node_modules/gaxios": {
"version": "6.7.1",
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz",
"integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==",
"license": "Apache-2.0",
"dependencies": {
"extend": "^3.0.2",
"https-proxy-agent": "^7.0.1",
"is-stream": "^2.0.0",
"node-fetch": "^2.6.9",
"uuid": "^9.0.1"
},
"engines": {
"node": ">=14"
}
},
"node_modules/gaxios/node_modules/agent-base": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
"node_modules/gaxios/node_modules/https-proxy-agent": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/gaxios/node_modules/uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/gcp-metadata": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz",
"integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==",
"license": "Apache-2.0",
"dependencies": {
"gaxios": "^6.0.0",
"json-bigint": "^1.0.0"
},
"engines": {
"node": ">=14"
}
},
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@ -5840,6 +5919,44 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/google-auth-library": {
"version": "9.15.1",
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz",
"integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==",
"license": "Apache-2.0",
"dependencies": {
"base64-js": "^1.3.0",
"ecdsa-sig-formatter": "^1.0.11",
"gaxios": "^6.1.1",
"gcp-metadata": "^6.1.0",
"gtoken": "^7.0.0",
"jws": "^4.0.0"
},
"engines": {
"node": ">=14"
}
},
"node_modules/google-auth-library/node_modules/jwa": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
"license": "MIT",
"dependencies": {
"buffer-equal-constant-time": "1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"node_modules/google-auth-library/node_modules/jws": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
"license": "MIT",
"dependencies": {
"jwa": "^2.0.0",
"safe-buffer": "^5.0.1"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@ -5865,6 +5982,40 @@
"dev": true,
"license": "MIT"
},
"node_modules/gtoken": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz",
"integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==",
"license": "MIT",
"dependencies": {
"gaxios": "^6.0.0",
"jws": "^4.0.0"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/gtoken/node_modules/jwa": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
"license": "MIT",
"dependencies": {
"buffer-equal-constant-time": "1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"node_modules/gtoken/node_modules/jws": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
"license": "MIT",
"dependencies": {
"jwa": "^2.0.0",
"safe-buffer": "^5.0.1"
}
},
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@ -6290,7 +6441,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@ -7280,6 +7430,15 @@
"node": ">=6"
}
},
"node_modules/json-bigint": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
"license": "MIT",
"dependencies": {
"bignumber.js": "^9.0.0"
}
},
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",

@ -32,6 +32,7 @@
"bcryptjs": "^2.4.3",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"google-auth-library": "^9.15.1",
"ioredis": "^5.4.2",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",

@ -29,7 +29,7 @@ export class ShopService {
const sandboxMode = true;
return ZarinpalCheckout.create(merchantId, sandboxMode);
}
///////////////////////////////////////////products//////////////////////////////////////////////
/////////////////////////////////////////products//////////////////////////////////////////////
// create a new product
async create(createProductDto: CreateProductDto): Promise<Product> {
try {
@ -203,7 +203,7 @@ export class ShopService {
throw new HttpException("An unexpected error occurred while deleting the product.", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
////////////////////////////////////////////cart/////////////////////////////////////////////////
//////////////////////////////////////////cart/////////////////////////////////////////////////
//create and add item to a cart
async createAndAddItemToCart(addToCartDto: { userId: number; productId: number; quantity: number }): Promise<{ message: string; cartItem: Cart }> {
const { userId, productId, quantity } = addToCartDto;

@ -27,7 +27,6 @@ export class JwtAuthGuard implements CanActivate {
request.user = decoded;
return true;
} catch (error) {
console.log(error)
throw new UnauthorizedException("Invalid or expired token");
}
}

@ -16,7 +16,6 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
const { jti, id } = payload;
const response = await this.redisService.isWhitelisted(id, jti)
console.log(response)
if (!response) {
throw new UnauthorizedException('Token is not whitelisted');
}

@ -1,45 +1,50 @@
import { Controller, Post, Body, UseGuards, Get, Request, Put, Param, HttpException, HttpStatus, Delete, Query } from "@nestjs/common";
import { Controller, Get, Query, Post, Body, UseGuards, Request, Put, Param, HttpException, HttpStatus, Delete } from "@nestjs/common";
import { UsersService } from "./users.service";
import { User } from "./entities/user.entity";
import { CreateUserDto } from "./dto/create-user.dto";
import { LoginUserDto } from "./dto/login-user.dto";
import { JwtAuthGuard } from "src/users/guard/auth.guard";
import { UpdateUserDto } from "./dto/update-user.dto";
import { RoleGuard } from "src/users/guard/role.guard";
import { JwtAuthGuard } from "./guard/auth.guard";
import { RoleGuard } from "./guard/role.guard";
import { User } from "./entities/user.entity";
@Controller("user")
export class UsersController {
constructor(private readonly usersService: UsersService) {}
//register
// Register
@Post("register")
async register(@Body() createUserDto: CreateUserDto) {
return this.usersService.register(createUserDto);
}
//login
// Login
@Post("login")
async login(@Body() loginUserDto: LoginUserDto) {
return this.usersService.login(loginUserDto);
}
//get access token
// Get new access token
@Post("new-token")
async newAccessToken(@Body("token") token: string) {
return this.usersService.newAccessToken(token);
}
//logout user
// Logout user
@UseGuards(JwtAuthGuard)
@Get("logout")
async logout(@Request() req) {
const user = req.user;
return this.usersService.logout(user);
}
//retrieve a user information
// Retrieve user information
@UseGuards(JwtAuthGuard)
@Get()
async getProfile(@Request() req): Promise<User> {
const userId = req.user.id;
return this.usersService.getProfile(userId);
}
//edit user profile
// Edit user profile
@UseGuards(JwtAuthGuard)
@Put()
async editProfile(@Request() req, @Body() updateUserDto: UpdateUserDto) {
@ -47,8 +52,10 @@ export class UsersController {
const { refreshToken, ...sanitizedDto } = updateUserDto;
return this.usersService.editProfile(userId, sanitizedDto);
}
/////////////////////////////////////admin access endpoints/////////////////////////////////////////////////////////
//get users list (admin)
///////////////////////////////////// Admin access endpoints /////////////////////////////////////////
// Get users list (admin)
@UseGuards(RoleGuard)
@Get('users-list')
async findAll(
@ -57,13 +64,15 @@ export class UsersController {
){
return this.usersService.findAll(page, limit);
}
// get a specific user info (admin)
// Get specific user info (admin)
@UseGuards(RoleGuard)
@Get("userinfo/:id")
async findSpecificUserInfoByUser(@Param("id") id) {
return this.usersService.findSpecificUserInfoByUser(id);
}
// delete a specific user (admin)
// Delete specific user (admin)
@UseGuards(RoleGuard)
@Delete(":id?")
async deleteUser(@Param("id") id) {
@ -72,4 +81,4 @@ export class UsersController {
}
return this.usersService.deleteUser(id);
}
}
}

@ -1,4 +1,4 @@
import { HttpException, HttpStatus, Injectable, UnauthorizedException, BadRequestException, NotFoundException, UseGuards } from "@nestjs/common";
import { HttpException, HttpStatus, Injectable, UnauthorizedException, BadRequestException, NotFoundException } from "@nestjs/common";
import { InjectModel } from "@nestjs/sequelize";
import { User } from "./entities/user.entity";
import * as bcrypt from "bcrypt";
@ -9,6 +9,7 @@ import { LoginUserDto } from "./dto/login-user.dto";
import { UpdateUserDto } from "./dto/update-user.dto";
import { v4 as uuidv4 } from "uuid";
import { RedisService } from "src/redis/redis.service";
import { Op } from "sequelize";
@Injectable()
export class UsersService {
@ -17,45 +18,35 @@ export class UsersService {
private readonly jwtService: JwtService,
private readonly configService: ConfigService,
private readonly redisService: RedisService,
) {}
) {
}
// Register method
async register(createUserDto: CreateUserDto) {
try {
createUserDto.password = await bcrypt.hash(createUserDto.password, parseInt(process.env.BCRYPT_SALT_ROUNDS || "10", 10));
const emailExists = await this.userModel.findOne({
where: { email: createUserDto.email },
});
if (emailExists) {
throw new BadRequestException("Email is already registered.");
}
const phoneExists = await this.userModel.findOne({
where: { phoneNumber: createUserDto.phoneNumber },
const existingUser = await this.userModel.findOne({
where: {
[Op.or]: [{ email: createUserDto.email }, { phoneNumber: createUserDto.phoneNumber }, { username: createUserDto.username }],
},
});
if (phoneExists) {
throw new BadRequestException("Phone number is already registered.");
}
const usernameExists = await this.userModel.findOne({
where: { username: createUserDto.username },
});
if (usernameExists) {
throw new BadRequestException("Username is already registered.");
if (existingUser) {
if (existingUser.email === createUserDto.email) {
throw new BadRequestException("Email is already registered.");
}
if (existingUser.phoneNumber === createUserDto.phoneNumber) {
throw new BadRequestException("Phone number is already registered.");
}
if (existingUser.username === createUserDto.username) {
throw new BadRequestException("Username is already registered.");
}
}
await this.userModel.create(createUserDto);
const user = await this.userModel.findOne({
where: { email: createUserDto.email },
});
const refreshToken = this.jwtService.sign(
{ id: user.id },
{
secret: this.configService.get<string>("JWT_REFRESH_SECRET"),
expiresIn: this.configService.get<string | number>("JWT_REFRESH_EXPIRES") || "7d",
},
);
return { message: "User registered successfully." };
} catch (error) {
if (error instanceof HttpException) {
@ -123,7 +114,7 @@ export class UsersService {
if (!user) {
throw new HttpException("User not found.", HttpStatus.NOT_FOUND);
}
const jti = uuidv4();
const accessToken = this.jwtService.sign(
{ id: user.id, role: user.role, jti },

Loading…
Cancel
Save