Compare commits

...

11 Commits

  1. 3
      .vscode/settings.json
  2. 23
      config/config.json
  3. 62
      migrations/20250104074851-create-user.js
  4. 63
      migrations/20250104094702-create-admin.js
  5. 43
      models/index.js
  6. 30
      models/user.js
  7. 821
      package-lock.json
  8. 7
      package.json
  9. 22
      src/admin/admin.controller.ts
  10. 53
      src/admin/admin.service.ts
  11. 29
      src/admin/dto/create-Admin.dto.ts
  12. 15
      src/admin/dto/login-Admin.dto.ts
  13. 33
      src/admin/dto/update-user.dto.ts
  14. 31
      src/admin/entities/admin.entity.ts
  15. 4
      src/app.module.ts
  16. 4
      src/cart/entities/cart.entity.ts
  17. 17
      src/config/database.config.ts
  18. 17
      src/config/migration-config.ts
  19. 9
      src/guard/role.guard.ts
  20. 6
      src/invoice/entities/invoice.entity.ts
  21. 2
      src/main.ts
  22. 29
      src/users/dto/create-user.dto.ts
  23. 15
      src/users/dto/login-user.dto.ts
  24. 33
      src/users/dto/update-user.dto.ts
  25. 26
      src/users/entities/user.entity.ts
  26. 42
      src/users/users.controller.ts
  27. 80
      src/users/users.service.ts
  28. 10
      src/wallet/entities/wallet.entity.ts

@ -0,0 +1,3 @@
{
"cSpell.words": ["zarinpal"]
}

@ -0,0 +1,23 @@
{
"development": {
"username": "postgres",
"password": "1234",
"database": "ecommerce",
"host": "127.0.0.1",
"dialect": "postgres"
},
"test": {
"username": "root",
"password": null,
"database": "database_test",
"host": "127.0.0.1",
"dialect": "mysql"
},
"production": {
"username": "root",
"password": null,
"database": "database_production",
"host": "127.0.0.1",
"dialect": "mysql"
}
}

@ -0,0 +1,62 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
email: {
type: Sequelize.STRING,
unique: true,
allowNull: false
},
password: {
type: Sequelize.STRING,
allowNull: false
},
role: {
type: Sequelize.STRING,
defaultValue: 'user'
},
firstName: {
type: Sequelize.STRING,
allowNull: false
},
lastName: {
type: Sequelize.STRING,
allowNull: false
},
username: {
type: Sequelize.STRING,
unique: true,
allowNull: false
},
phoneNumber: {
type: Sequelize.STRING,
unique: true,
allowNull: false
},
gender: {
type: Sequelize.ENUM("male", "female"),
allowNull: false
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('Users');
}
};

@ -0,0 +1,63 @@
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('Admins', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
email: {
type: Sequelize.STRING,
allowNull: false,
unique: true,
},
password: {
type: Sequelize.STRING,
allowNull: false,
},
role: {
type: Sequelize.STRING,
defaultValue: 'admin',
},
firstName: {
type: Sequelize.STRING,
allowNull: false,
},
lastName: {
type: Sequelize.STRING,
allowNull: false,
},
username: {
type: Sequelize.STRING,
allowNull: false,
unique: true,
},
phoneNumber: {
type: Sequelize.STRING,
allowNull: false,
unique: true,
},
gender: {
type: Sequelize.ENUM('male', 'female'),
allowNull: false,
},
createdAt: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW,
},
updatedAt: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW,
},
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Admins');
},
};

@ -0,0 +1,43 @@
'use strict';
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const process = require('process');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {};
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}
fs
.readdirSync(__dirname)
.filter(file => {
return (
file.indexOf('.') !== 0 &&
file !== basename &&
file.slice(-3) === '.js' &&
file.indexOf('.test.js') === -1
);
})
.forEach(file => {
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;

@ -0,0 +1,30 @@
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class User extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
}
User.init({
email: DataTypes.STRING,
password: DataTypes.STRING,
role: DataTypes.STRING,
firstName: DataTypes.STRING,
lastName: DataTypes.STRING,
username: DataTypes.STRING,
phoneNumber: DataTypes.STRING,
gender: DataTypes.ENUM
}, {
sequelize,
modelName: 'User',
});
return User;
};

821
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -30,6 +30,8 @@
"@nestjs/sequelize": "^10.0.1",
"bcrypt": "^5.1.1",
"bcryptjs": "^2.4.3",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
"pg": "^8.13.1",
@ -37,7 +39,9 @@
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1",
"sequelize": "^6.37.5",
"sequelize-typescript": "^2.1.6"
"sequelize-typescript": "^2.1.6",
"umzug": "^3.8.2",
"zarinpal-checkout": "^0.3.0"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",
@ -55,6 +59,7 @@
"eslint-plugin-prettier": "^5.0.0",
"jest": "^29.5.0",
"prettier": "^3.0.0",
"sequelize-cli": "^6.6.2",
"source-map-support": "^0.5.21",
"supertest": "^7.0.0",
"ts-jest": "^29.1.0",

@ -1,18 +1,26 @@
import { Controller, Get, Post, Body, Patch, Param, Delete } from "@nestjs/common";
import { Controller, Get, Post, Body, Request, Put, UseGuards } from "@nestjs/common";
import { AdminService } from "./admin.service";
import { Admin } from "./entities/admin.entity";
import { CreateAdminDto } from "./dto/create-Admin.dto";
import { LoginAdminDto } from "./dto/login-Admin.dto";
import { JwtAuthGuard } from "src/guard/auth.guard";
import { UpdateUserDto } from "./dto/update-user.dto";
@Controller("admin")
export class AdminController {
constructor(private readonly adminService: AdminService) {}
@Post("register")
async register(@Body() body: { email: string; password: string }): Promise<Admin> {
const { email, password } = body;
return this.adminService.register(email, password);
async register(@Body() createAdminDto: CreateAdminDto): Promise<Admin> {
return this.adminService.register(createAdminDto);
}
@Post("login")
async login(@Body() body: { email: string; password: string }): Promise<{ token: string }> {
const { email, password } = body;
return this.adminService.login(email, password);
async login(@Body() loginAdminDto: LoginAdminDto): Promise<{ token: string }> {
return this.adminService.login(loginAdminDto);
}
@UseGuards(JwtAuthGuard)
@Put()
async editAdminProfile(@Request() req, @Body() updateAdminDto: UpdateUserDto): Promise<Admin> {
const userId = req.user.id;
return this.adminService.editAdminProfile(userId, updateAdminDto);
}
}

@ -4,6 +4,9 @@ import { Admin } from "./entities/admin.entity";
import * as bcrypt from "bcrypt";
import { JwtService } from "@nestjs/jwt";
import { ConfigService } from "@nestjs/config";
import { CreateAdminDto } from "./dto/create-Admin.dto";
import { LoginAdminDto } from "./dto/login-Admin.dto";
import { UpdateUserDto } from "./dto/update-user.dto";
@Injectable()
export class AdminService {
constructor(
@ -11,48 +14,41 @@ export class AdminService {
private readonly jwtService: JwtService,
private readonly configService: ConfigService,
) {}
async register(email: string, password: string): Promise<Admin> {
//register method
async register(createAdminDto: CreateAdminDto): Promise<Admin> {
try {
if (!email) {
throw new HttpException("Email should be entered.", HttpStatus.BAD_REQUEST);
}
if (!password) {
throw new HttpException("Password should be entered.", HttpStatus.BAD_REQUEST);
}
const existingAdmin = await this.adminModel.findOne({ where: { email } });
const existingAdmin = await this.adminModel.findOne({ where: { email: createAdminDto.email } });
if (existingAdmin) {
throw new HttpException("Email is already registered.", HttpStatus.CONFLICT);
}
const hashedPassword = await bcrypt.hash(password, 10);
createAdminDto.password = await bcrypt.hash(createAdminDto.password, 10);
const admin = await this.adminModel.create({
email,
password: hashedPassword,
role: "admin",
});
const admin = await this.adminModel.create(createAdminDto);
return admin;
} catch (error) {
console.log(error);
if (error instanceof HttpException) {
throw error;
}
throw new HttpException("An error occurred during registration.", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
async login(email: string, password: string): Promise<{ token: string }> {
//login method
async login(loginAdminDto: LoginAdminDto): Promise<{ token: string }> {
try {
const admin = await this.adminModel.findOne({ where: { email } });
const admin = await this.adminModel.findOne({ where: { email: loginAdminDto.email } });
if (!admin) {
throw new HttpException("Invalid email or password", HttpStatus.UNAUTHORIZED);
}
const isValidPassword = await bcrypt.compare(password, admin.password);
const isValidPassword = await bcrypt.compare(loginAdminDto.password, admin.password);
if (!isValidPassword) {
throw new HttpException("Invalid email or password", HttpStatus.UNAUTHORIZED);
}
const token = this.jwtService.sign(
{ id: admin.id, role: admin.role },
{
@ -60,10 +56,27 @@ export class AdminService {
expiresIn: this.configService.get<string | number>("JWT_EXPIRES") || "1h",
},
);
return { token };
} catch (error) {
console.log(error);
throw new HttpException("An error occurred during login.", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
//edit admin profile method
async editAdminProfile(userId: number, updateAdminDto: UpdateUserDto): Promise<Admin> {
try {
const user = await this.adminModel.findOne({ where: { id: userId } });
if (!user) {
throw new Error("Admin not found");
}
await user.update(updateAdminDto);
return user;
} catch (error) {
throw new Error(`An error occurred while updating admin: ${error.message}`);
}
}
}

@ -0,0 +1,29 @@
import { IsString, IsEmail, IsEnum, IsNotEmpty, IsOptional, Matches } from "class-validator";
export class CreateAdminDto {
@IsEmail({}, { message: "Invalid email format" })
email: string;
@IsString()
@IsNotEmpty({ message: "Password is required" })
password: string;
@IsString()
@IsNotEmpty({ message: "First name is required" })
firstName: string;
@IsString()
@IsNotEmpty({ message: "Last name is required" })
lastName: string;
@IsString()
@IsNotEmpty({ message: "Username is required" })
username: string;
@IsString()
@Matches(/^[0-9]{11}$/, { message: "Phone number must be 10 digits" })
phoneNumber: string;
@IsEnum(["male", "female"], { message: "Gender must be 'male' or 'female'" })
gender: string;
}

@ -0,0 +1,15 @@
import { IsString, IsEmail,IsNotEmpty} from "class-validator";
export class LoginAdminDto {
@IsEmail({}, { message: "Invalid email format" })
email: string;
@IsString()
@IsNotEmpty({ message: "Password is required" })
password: string;
@IsString()
@IsNotEmpty({ message: "Username is required" })
username: string;
}

@ -0,0 +1,33 @@
import { IsOptional, IsString, IsEmail, IsEnum } from 'class-validator';
import {Gender } from '../entities/admin.entity';
export class UpdateUserDto {
@IsOptional()
@IsString()
username?: string;
@IsOptional()
@IsEmail()
email?: string;
@IsOptional()
@IsString()
password?: string;
@IsOptional()
@IsString()
firstName?: string;
@IsOptional()
@IsString()
lastName?: string;
@IsOptional()
@IsString()
phoneNumber?: string;
@IsOptional()
@IsEnum(Gender)
gender?: Gender;
}

@ -1,12 +1,35 @@
import { Model, Table, Column } from "sequelize-typescript";
import { Column, Table, Model, DataType } from "sequelize-typescript";
@Table
export class Admin extends Model<Admin> {
@Column
@Column({ unique: true })
email: string;
@Column
password: string;
@Column
@Column({ defaultValue: "admin" })
role: string;
}
@Column
firstName: string;
@Column
lastName: string;
@Column({ unique: true })
username: string;
@Column({ unique: true })
phoneNumber: string;
@Column({
type: DataType.ENUM("male", "female"),
allowNull: false,
})
gender: string;
}
export enum Gender {
Male = "male",
Female = "female",
}

@ -24,7 +24,9 @@ import { PaymentModule } from "./payment/payment.module";
WalletModule,
InvoiceModule,
AdminModule,
PaymentModule
PaymentModule,
],
controllers: [AppController],
providers: [AppService],

@ -8,14 +8,14 @@ export class Cart extends Model<Cart> {
@Column
userId: number;
@BelongsTo(() => User)
@BelongsTo(() => User, { onDelete: 'CASCADE' })
user: User;
@ForeignKey(() => Product)
@Column
productId: number;
@BelongsTo(() => Product)
@BelongsTo(() => Product, { onDelete: 'CASCADE' })
product: Product;
@Column({

@ -1,14 +1,17 @@
import { SequelizeModuleOptions } from '@nestjs/sequelize';
import { SequelizeModuleOptions } from "@nestjs/sequelize";
import * as dotenv from "dotenv";
import * as path from "path";
dotenv.config();
export const databaseConfig: SequelizeModuleOptions = {
dialect: 'postgres',
host: process.env.DATABASE_HOST || 'localhost',
dialect: "postgres",
host: process.env.DATABASE_HOST || "localhost",
port: +process.env.DATABASE_PORT || 5432,
username: process.env.DATABASE_USER || 'postgres',
password: process.env.DATABASE_PASSWORD || 'password',
database: process.env.DATABASE_NAME || 'ecommerce',
username: process.env.DATABASE_USER || "postgres",
password: process.env.DATABASE_PASSWORD || "password",
database: process.env.DATABASE_NAME || "ecommerce",
models: [path.join(__dirname, "../**/entities/*.entity.ts")],
autoLoadModels: true,
synchronize: true,
synchronize: true,
};

@ -0,0 +1,17 @@
import { SequelizeModuleOptions } from "@nestjs/sequelize";
import * as dotenv from "dotenv";
import * as path from "path";
dotenv.config();
export const databaseConfig: SequelizeModuleOptions = {
dialect: "postgres",
host: process.env.DATABASE_HOST || "localhost",
port: +process.env.DATABASE_PORT || 5432,
username: process.env.DATABASE_USER || "postgres",
password: process.env.DATABASE_PASSWORD || "password",
database: process.env.DATABASE_NAME || "ecommerce",
models: [path.join(__dirname, "../**/entities/*.entity.ts")],
autoLoadModels: true,
synchronize: true,
};

@ -6,20 +6,25 @@ export class RoleGuard implements CanActivate {
constructor(
private jwtService: JwtService,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const token = request.headers["authorization"]?.split(" ")[1];
if (!token) throw new UnauthorizedException("Authorization token is missing");
try {
const decoded = this.jwtService.verify(token, { secret: process.env.JWT_SECRET });
const userRole = decoded.role;
if (userRole !== "admin") {
throw new UnauthorizedException("You do not have the required role");
throw new UnauthorizedException(`You do not have the required role. Current role: ${userRole}`);
}
request.user = decoded;
return true;
} catch (error) {
throw new UnauthorizedException("Invalid or expired token");
console.log(error);
throw new UnauthorizedException(error.response);
}
}
}

@ -1,14 +1,16 @@
import { Table, Model, Column, BelongsTo, ForeignKey } from "sequelize-typescript";
import { User } from "../../users/entities/user.entity";
import { Product } from "../../products/entities/product.entity";
import { Product } from "../../products/entities/product.entity";
@Table
export class Invoice extends Model<Invoice> {
@ForeignKey(() => User)
@Column
userId: number;
@BelongsTo(() => User)
@BelongsTo(() => User, { onDelete: 'CASCADE' })
user: User;
@Column
totalAmount: number;
}

@ -1,8 +1,10 @@
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(process.env.PORT ?? 3000);
}
bootstrap();

@ -0,0 +1,29 @@
import { IsString, IsEmail, IsEnum, IsNotEmpty, IsOptional, Matches } from "class-validator";
export class CreateUserDto {
@IsEmail({}, { message: "Invalid email format" })
email: string;
@IsString()
@IsNotEmpty({ message: "Password is required" })
password: string;
@IsString()
@IsNotEmpty({ message: "First name is required" })
firstName: string;
@IsString()
@IsNotEmpty({ message: "Last name is required" })
lastName: string;
@IsString()
@IsNotEmpty({ message: "Username is required" })
username: string;
@IsString()
@Matches(/^[0-9]{11}$/, { message: "Phone number must be 10 digits" })
phoneNumber: string;
@IsEnum(["male", "female"], { message: "Gender must be 'male' or 'female'" })
gender: string;
}

@ -0,0 +1,15 @@
import { IsString, IsEmail, IsEnum, IsNotEmpty, IsOptional, Matches } from "class-validator";
export class LoginUserDto {
@IsEmail({}, { message: "Invalid email format" })
email: string;
@IsString()
@IsNotEmpty({ message: "Password is required" })
password: string;
@IsString()
@IsNotEmpty({ message: "Username is required" })
username: string;
}

@ -0,0 +1,33 @@
import { IsOptional, IsString, IsEmail, IsEnum } from 'class-validator';
import {Gender } from '../entities/user.entity';
export class UpdateUserDto {
@IsOptional()
@IsString()
username?: string;
@IsOptional()
@IsEmail()
email?: string;
@IsOptional()
@IsString()
password?: string;
@IsOptional()
@IsString()
firstName?: string;
@IsOptional()
@IsString()
lastName?: string;
@IsOptional()
@IsString()
phoneNumber?: string;
@IsOptional()
@IsEnum(Gender)
gender?: Gender;
}

@ -1,11 +1,35 @@
import { Column, Table, Model } from "sequelize-typescript";
import { Column, Table, Model, DataType } from "sequelize-typescript";
@Table
export class User extends Model<User> {
@Column({ unique: true })
email: string;
@Column
password: string;
@Column({ defaultValue: "user" })
role: string;
@Column
firstName: string;
@Column
lastName: string;
@Column({ unique: true })
username: string;
@Column({ unique: true })
phoneNumber: string;
@Column({
type: DataType.ENUM("male", "female"),
allowNull: false,
})
gender: string;
}
export enum Gender {
Male = 'male',
Female = 'female',
}

@ -1,20 +1,44 @@
import { Controller, Post, Body, Res, UseGuards, Get } from "@nestjs/common";
import { Controller, Post, Body, UseGuards, Get, Request, Put } 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/guard/auth.guard";
import { UpdateUserDto } from "./dto/update-user.dto";
import { RoleGuard } from "src/guard/role.guard";
@Controller("user")
export class UsersController {
constructor(private readonly usersService: UsersService) {}
//register as user
@Post("register")
async register(@Body() body: { email: string; password: string }):Promise<User> {
const { email, password } = body;
return this.usersService.register(email, password);
async register(@Body() createUserDto: CreateUserDto): Promise<User> {
return this.usersService.register(createUserDto);
}
//login as user
@Post("login")
async login(@Body() body: { email: string; password: string }):Promise<{token}> {
const { email, password } = body;
return this.usersService.login(email, password);
async login(@Body() loginUserDto: LoginUserDto): Promise<{ token }> {
return this.usersService.login(loginUserDto);
}
//retrieve a user information
@UseGuards(JwtAuthGuard)
@Get()
async getProfile(@Request() req): Promise<User> {
const userId = req.user.id;
return this.usersService.getProfile(userId);
}
//edit user profile
@UseGuards(JwtAuthGuard)
@Put()
async editProfile(@Request() req, @Body() updateUserDto: UpdateUserDto): Promise<User> {
const userId = req.user.id;
return this.usersService.editProfile(userId, updateUserDto);
}
//get users list (admin)
@UseGuards(RoleGuard)
@Get("users")
async findAll(): Promise<User[]> {
return this.usersService.findAll();
}
}

@ -1,36 +1,34 @@
import { HttpException, HttpStatus, Injectable, UnauthorizedException, BadRequestException } 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";
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { JwtService } from "@nestjs/jwt";
import { ConfigService } from "@nestjs/config";
import { CreateUserDto } from "./dto/create-user.dto";
import { LoginUserDto } from "./dto/login-user.dto";
import { UpdateUserDto } from "./dto/update-user.dto";
@Injectable()
export class UsersService {
constructor(
@InjectModel(User) private readonly userModel: typeof User,
private readonly jwtService: JwtService,
private readonly configService: ConfigService
private readonly jwtService: JwtService,
private readonly configService: ConfigService,
) {}
// Register method
async register(email: string, password: string): Promise<User> {
if (!email) throw new BadRequestException("Email should be entered");
if (!password) throw new BadRequestException("Password should be entered");
async register(createUserDto: CreateUserDto): Promise<User> {
try {
const hashedPassword = await bcrypt.hash(password, 10);
createUserDto.password = await bcrypt.hash(createUserDto.password, parseInt(process.env.BCRYPT_SALT_ROUNDS || "10", 10));
const userExists = await this.userModel.findOne({ where: { email } });
const userExists = await this.userModel.findOne({
where: { email: createUserDto.email },
});
if (userExists) {
throw new BadRequestException('Email already exists');
throw new BadRequestException("Email already exists");
}
const user = await this.userModel.create({
email,
password: hashedPassword,
});
const user = await this.userModel.create(createUserDto);
return user;
} catch (error) {
throw new HttpException(`An error occurred: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR);
@ -38,24 +36,21 @@ export class UsersService {
}
// Login method
async login(email: string, password: string): Promise<{ token: string }> {
if (!email) throw new BadRequestException("Email should be entered");
if (!password) throw new BadRequestException("Password should be entered");
async login(loginUserDto: LoginUserDto): Promise<{ token: string }> {
const user = await this.userModel.findOne({
where: { email },
where: { email: loginUserDto.email },
});
if (!user) {
throw new UnauthorizedException("Invalid email or password");
}
const passwordMatch = await bcrypt.compare(password, user.password);
const passwordMatch = await bcrypt.compare(loginUserDto.password, user.password);
if (!passwordMatch) {
throw new UnauthorizedException("Invalid email or password");
}
const token = this.jwtService.sign(
{ id: user.id },
{ id: user.id,role:user.role },
{
secret: this.configService.get<string>("JWT_SECRET"),
expiresIn: this.configService.get<string | number>("JWT_EXPIRES") || "1h",
@ -64,4 +59,41 @@ export class UsersService {
return { token };
}
//get information user method
async getProfile(userId: number): Promise<User> {
const user = await this.userModel.findOne({
where: { id: userId },
attributes: { exclude: ["password"] },
});
if (!user) {
throw new Error("User not found");
}
return user;
}
//edit profile user method
async editProfile(userId: number, updateUserDto: UpdateUserDto) {
const user = await this.userModel.findOne({ where: { id: userId } });
if (!user) {
throw new NotFoundException("User not found");
}
if (updateUserDto.password) {
updateUserDto.password = await bcrypt.hash(updateUserDto.password, 10);
}
await user.update(updateUserDto);
return user;
}
//get users list
async findAll(): Promise<User[]> {
try {
return await this.userModel.findAll();
} catch (error) {
throw new Error("An error occurred while fetching users");
}
}
}

@ -1,4 +1,4 @@
import { Model, Table, Column, ForeignKey, BelongsTo } from 'sequelize-typescript';
import { Model, Table, Column, ForeignKey, BelongsTo, DataType } from 'sequelize-typescript';
import { User } from '../../users/entities/user.entity';
@Table
@ -7,9 +7,13 @@ export class Wallet extends Model<Wallet> {
@Column
userId: number;
@BelongsTo(() => User)
@BelongsTo(() => User, { onDelete: 'CASCADE' })
user: User;
@Column
@Column({
type: DataType.DECIMAL(10, 2),
allowNull: false,
defaultValue: 0,
})
balance: number;
}

Loading…
Cancel
Save