Compare commits

..

4 Commits

  1. 4
      src/admin/admin.controller.ts
  2. 63
      src/admin/admin.service.ts
  3. 37
      src/cart/cart.service.ts
  4. 23
      src/invoice/invoice.service.ts
  5. 163
      src/products/products.service.ts
  6. 17
      src/users/users.controller.ts
  7. 110
      src/users/users.service.ts
  8. 34
      src/wallet/wallet.service.ts

@ -27,8 +27,8 @@ export class AdminController {
}
//get a new access token
@Post("new-token")
async newAccessToken(@Body("refreshToken") refreshToken: string) {
return this.adminService.newAccessToken(refreshToken);
async newAccessToken(@Body("token") token: string) {
return this.adminService.newAccessToken(token);
}
//edit admin profile
@UseGuards(RoleGuard)

@ -17,15 +17,31 @@ export class AdminService {
//register method
async register(createAdminDto: CreateAdminDto): Promise<{ message }> {
try {
const existingAdmin = await this.adminModel.findOne({
const existingAdminByEmail = await this.adminModel.findOne({
where: { email: createAdminDto.email },
});
if (existingAdmin) {
if (existingAdminByEmail) {
throw new HttpException("The provided email is already registered.", HttpStatus.CONFLICT);
}
const existingAdminByUsername = await this.adminModel.findOne({
where: { username: createAdminDto.username },
});
if (existingAdminByUsername) {
throw new HttpException("The provided username is already taken.", HttpStatus.CONFLICT);
}
const existingAdminByPhoneNumber = await this.adminModel.findOne({
where: { phoneNumber: createAdminDto.phoneNumber },
});
if (existingAdminByPhoneNumber) {
throw new HttpException("The provided phone number is already registered.", HttpStatus.CONFLICT);
}
createAdminDto.password = await bcrypt.hash(createAdminDto.password, 10);
await this.adminModel.create(createAdminDto);
return { message: "admin register is successful" };
return { message: "Admin registration is successful." };
} catch (error) {
if (error instanceof HttpException) {
throw error;
@ -37,11 +53,11 @@ export class AdminService {
async login(loginAdminDto: LoginAdminDto): Promise<{ accessToken: string; refreshToken: string }> {
try {
const admin = await this.adminModel.findOne({
where: { email: loginAdminDto.email },
where: { email: loginAdminDto.email,username:loginAdminDto.username },
});
if (!admin) {
throw new HttpException("Invalid email or password.", HttpStatus.UNAUTHORIZED);
throw new HttpException("Invalid email, username or password.", HttpStatus.UNAUTHORIZED);
}
const isValidPassword = await bcrypt.compare(loginAdminDto.password, admin.password);
@ -78,38 +94,41 @@ export class AdminService {
}
}
//logout (delete refresh token from database)
async logout(userId: number) {
const user = await this.adminModel.findOne({
where: { id: userId },
});
if (!user) {
throw new HttpException("User not found.", HttpStatus.NOT_FOUND);
async logout(userId: number): Promise<{ message: string }> {
try {
if (!userId) {
throw new HttpException("User ID is required.", HttpStatus.BAD_REQUEST);
}
const user = await this.adminModel.findOne({ where: { id: userId } });
if (!user) {
throw new HttpException("User not found.", HttpStatus.NOT_FOUND);
}
await this.adminModel.update({ refreshToken: null }, { where: { id: userId } });
return { message: "Logout is successful" };
} catch (error) {
throw new HttpException("An unexpected error occurred during logout. Please try again later.", HttpStatus.INTERNAL_SERVER_ERROR);
}
await this.adminModel.update({ refreshToken: null }, { where: { id: userId } });
return { message: "logout is successful" };
}
//getting new access token
async newAccessToken(refreshToken: string) {
if (!refreshToken) {
throw new HttpException({ message: "Refresh token is required." }, HttpStatus.BAD_REQUEST);
throw new HttpException("Refresh token is required.", HttpStatus.BAD_REQUEST);
}
let decoded;
try {
decoded = this.jwtService.verify(refreshToken, { secret: process.env.JWT_REFRESH_SECRET });
decoded = this.jwtService.verify(refreshToken, { secret: this.configService.get<string>("JWT_REFRESH_SECRET") });
} catch (error) {
throw new HttpException({ message: "Invalid or expired token." }, HttpStatus.UNAUTHORIZED);
throw new HttpException("Invalid or expired token.", HttpStatus.UNAUTHORIZED);
}
const user = await this.adminModel.findOne({
where: { id: decoded.id },
});
const user = await this.adminModel.findOne({where:{id:decoded.id}});
if (!user) {
throw new HttpException({ message: "User not found." }, HttpStatus.NOT_FOUND);
throw new HttpException("User not found.", HttpStatus.NOT_FOUND);
}
if (user.role !== "admin") {
throw new HttpException({ message: "You are not authorized to get an admin token." }, HttpStatus.FORBIDDEN);
if (user.refreshToken !== refreshToken) {
throw new HttpException("Invalid refresh token.", HttpStatus.FORBIDDEN);
}
const accessToken = this.jwtService.sign(

@ -61,7 +61,7 @@ export class CartService {
cartItem: cart,
};
} catch (error) {
console.error("Error during adding item to cart:", error);
console.log(error)
throw new HttpException("An unexpected error occurred while adding the product to cart. Please try again later.", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@ -77,29 +77,47 @@ export class CartService {
include: [
{
model: Product,
attributes: ["name"],
attributes: ["name", "price"],
},
],
});
if (!cartItems || cartItems.length === 0) {
throw new HttpException("No cart items found for the specified user.", HttpStatus.NOT_FOUND);
return { cartItems: [], totalPrice: 0 };
}
const totalPrice = cartItems.reduce((sum, item) => sum + (Number(item.productPrice) * item.quantity || 0), 0);
const totalPrice = cartItems.reduce((sum, item) => {
return sum + (Number(item.productPrice) * item.quantity || 0);
}, 0);
return { cartItems, totalPrice };
} catch (error) {
throw new HttpException("An unexpected error occurred while fetching the cart. Please try again later.", HttpStatus.INTERNAL_SERVER_ERROR);
console.error("Error fetching cart items:", error);
throw new HttpException(
"An unexpected error occurred while fetching the cart. Please try again later.",
HttpStatus.INTERNAL_SERVER_ERROR
);
}
}
// Update cart item quantity
async updateCart(userId: number, productId: number, quantity: number): Promise<Cart> {
const cartItem = await this.cartModel.findOne({ where: { userId, productId, status: "open" } });
if (!cartItem) {
throw new HttpException("Product not found in the cart.", HttpStatus.NOT_FOUND);
}
const product = await this.productModel.findByPk(productId);
if (!product) {
throw new HttpException("Product not found.", HttpStatus.NOT_FOUND);
}
if (product.quantity < quantity) {
throw new HttpException("Insufficient product quantity.", HttpStatus.CONFLICT);
}
try {
cartItem.quantity = quantity;
await cartItem.save();
@ -127,11 +145,14 @@ export class CartService {
//delete whole cart by user
async clearCart(userId: number) {
await this.cartModel.destroy({ where: { userId } });
return { message: "cart cleared successful" };
await this.cartModel.destroy({
where: { userId, status: 'open' },
});
return { message: "Cart cleared successfully" };
}
//order(clearCart disable)
//order
async processOrder(userId: number, totalAmount: number): Promise<{ message: string; invoice: Invoice }> {
try {
const carts = await this.cartModel.findAll({ where: { userId, status: "open" } });

@ -17,11 +17,22 @@ export class InvoiceService {
if (!user) {
throw new HttpException("User not found", HttpStatus.NOT_FOUND);
}
const invoice = await this.invoiceModel.create({
userId,
totalPaymentAmount: 0,
});
return invoice;
try {
const invoice = await this.invoiceModel.create({
userId,
totalPaymentAmount: 0,
});
if (!invoice) {
throw new HttpException("Failed to create invoice", HttpStatus.INTERNAL_SERVER_ERROR);
}
return invoice;
} catch (error) {
console.error("Error during invoice creation:", error);
throw new HttpException("An error occurred while creating the invoice.", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
async updateTotalPayment(userId: number) {
const user = await User.findByPk(userId);
@ -103,7 +114,7 @@ export class InvoiceService {
throw new HttpException("An error occurred while retrieving the invoice.", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
async getUserInvoices(userId: number){
async getUserInvoices(userId: number) {
try {
if (!userId) {
throw new HttpException("User ID are required.", HttpStatus.BAD_REQUEST);

@ -18,20 +18,15 @@ export class ProductsService {
});
if (existingProduct) {
throw new HttpException(
'Product with this name already exists.',
HttpStatus.BAD_REQUEST,
);
}
existingProduct.quantity += createProductDto.quantity || 0;
await existingProduct.save();
const product = await this.productModel.create(createProductDto);
return product;
return existingProduct;
}
const newProduct = await this.productModel.create(createProductDto);
return newProduct;
} catch (error) {
console.error(error);
throw new HttpException(
'An error occurred while creating the product.',
HttpStatus.INTERNAL_SERVER_ERROR,
);
throw new HttpException("An error occurred while creating or updating the product.", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@ -41,19 +36,16 @@ export class ProductsService {
const product = await this.productModel.findByPk(id);
if (!product) {
throw new HttpException(
'Product not found with the given id.',
HttpStatus.NOT_FOUND,
);
throw new HttpException("Product not found with the given ID.", HttpStatus.NOT_FOUND);
}
return product;
} catch (error) {
console.error(error);
throw new HttpException(
error.response,
HttpStatus.INTERNAL_SERVER_ERROR,
);
if (error instanceof HttpException) {
throw error;
}
throw new HttpException("An unexpected error occurred while fetching the product.", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@ -62,55 +54,105 @@ export class ProductsService {
search?: string,
priceMin?: number,
priceMax?: number,
): Promise<Product[]> {
page: number = 1,
limit: number = 10,
): Promise<{ products: Product[]; total: number; totalPages: number; currentPage: number }> {
try {
// ساخت شرطهای جستجو و فیلتر
const where: any = {};
const where: Record<string, any> = {};
if (search) {
where.name = { [Op.like]: `%${search}%` }; // جستجوی نام محصول به صورت جزئی
}
if (priceMin !== undefined) {
where.price = { ...(where.price || {}), [Op.gte]: priceMin }; // فیلتر حداقل قیمت
where.name = { [Op.iLike]: `%${search}%` };
}
if (priceMax !== undefined) {
where.price = { ...(where.price || {}), [Op.lte]: priceMax }; // فیلتر حداکثر قیمت
if (priceMin !== undefined || priceMax !== undefined) {
where.price = {};
if (priceMin !== undefined) {
where.price[Op.gte] = priceMin;
}
if (priceMax !== undefined) {
where.price[Op.lte] = priceMax;
}
}
const products = await this.productModel.findAll({ where });
const offset = (page - 1) * limit;
if (!products || products.length === 0) {
throw new HttpException('No products found.', HttpStatus.NOT_FOUND);
}
const { rows: products, count: total } = await this.productModel.findAndCountAll({
where,
limit,
offset,
});
const totalPages = Math.ceil(total / limit);
return products;
return {
products,
total,
totalPages,
currentPage: page,
};
} catch (error) {
console.error(error);
throw new HttpException(
'An error occurred while retrieving products.',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}}
console.error("Error retrieving products:", error.message);
if (error instanceof HttpException) {
throw error;
}
throw new HttpException("An unexpected error occurred while retrieving products.", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
// update a product info
async update(id: string, updateProductDto: UpdateProductDto): Promise<Product> {
const product = await this.productModel.findByPk(id);
if (!product) {
throw new HttpException("Product not found.", HttpStatus.NOT_FOUND);
}
try {
const product = await this.productModel.findByPk(id);
const { name, description, price, imageUrl, tags, quantity, brand, color, category } = updateProductDto;
if (!product) {
throw new HttpException("Product not found.", HttpStatus.NOT_FOUND);
let updated = false; // متغیر برای بررسی تغییرات
if (name && name !== product.name) {
product.name = name;
updated = true;
}
if (description && description !== product.description) {
product.description = description;
updated = true;
}
if (price !== undefined && price !== product.price) {
product.price = price;
updated = true;
}
if (imageUrl && imageUrl !== product.imageUrl) {
product.imageUrl = imageUrl;
updated = true;
}
if (tags && tags !== product.tags) {
product.tags = tags;
updated = true;
}
if (quantity !== undefined && quantity !== product.quantity) {
product.quantity = quantity;
updated = true;
}
if (brand && brand !== product.brand) {
product.brand = brand;
updated = true;
}
if (color && color !== product.color) {
product.color = color;
updated = true;
}
if (category && category !== product.category) {
product.category = category;
updated = true;
}
const { name, description, price, imageUrl, tags, quantity, brand, color, category } = updateProductDto;
if (name) product.name = name;
if (description) product.description = description;
if (price) product.price = price;
if (imageUrl) product.imageUrl = imageUrl;
if (tags) product.tags = tags;
if (quantity) product.quantity = quantity;
if (brand) product.brand = brand;
if (color) product.color = color;
if (category) product.category = category;
await product.save();
if (updated) {
await product.save();
}
return product;
} catch (error) {
@ -124,14 +166,19 @@ export class ProductsService {
const product = await this.productModel.findByPk(id);
if (!product) {
throw new HttpException("Product not found with the given id.", HttpStatus.NOT_FOUND);
throw new HttpException(`Product with id ${id} not found.`, HttpStatus.NOT_FOUND);
}
await product.destroy();
return { message: "Product deleted successfully." };
} catch (error) {
throw new HttpException("An error occurred while deleting the product.", HttpStatus.INTERNAL_SERVER_ERROR);
console.error("Error during product deletion:", error.message);
if (error instanceof HttpException) {
throw error;
}
throw new HttpException("An unexpected error occurred while deleting the product.", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}

@ -1,4 +1,4 @@
import { Controller, Post, Body, UseGuards, Get, Request, Put, Param } from "@nestjs/common";
import { Controller, Post, Body, UseGuards, Get, 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";
@ -22,12 +22,12 @@ export class UsersController {
}
//get access token
@Post("new-token")
async newAccessToken(@Body("refreshToken") refreshToken: string) {
return this.usersService.newAccessToken(refreshToken);
async newAccessToken(@Body("token") token: string) {
return this.usersService.newAccessToken(token);
}
//logout user
@UseGuards(JwtAuthGuard)
@Get('logout')
@Get("logout")
async logout(@Request() req) {
const userId = req.user.id;
return this.usersService.logout(userId);
@ -46,7 +46,7 @@ export class UsersController {
const userId = req.user.id;
return this.usersService.editProfile(userId, updateUserDto);
}
//admin endpoints/////////////////////////////////////////////////////////
/////////////////////////////////////admin endpoints/////////////////////////////////////////////////////////
//get users list (admin)
@UseGuards(RoleGuard)
@Get("users")
@ -61,8 +61,11 @@ export class UsersController {
}
//delete a specific user by admin
@UseGuards(RoleGuard)
@Get("users/delete/:id")
async deleteUser(@Param("id") id){
@Delete(":id?")
async deleteUser(@Param("id") id) {
if (!id) {
throw new HttpException("User ID is required.", HttpStatus.BAD_REQUEST);
}
return this.usersService.deleteUser(id);
}
}

@ -17,17 +17,50 @@ export class UsersService {
) {}
// Register method
async register(createUserDto: CreateUserDto): Promise<{ message }> {
async register(createUserDto: CreateUserDto): Promise<{ message: string }> {
try {
createUserDto.password = await bcrypt.hash(createUserDto.password, parseInt(process.env.BCRYPT_SALT_ROUNDS || "10", 10));
const userExists = await this.userModel.findOne({
const emailExists = await this.userModel.findOne({
where: { email: createUserDto.email },
});
if (userExists) {
if (emailExists) {
throw new BadRequestException("Email is already registered.");
}
const user = await this.userModel.create(createUserDto);
return { message: "user registered successful" };
const phoneExists = await this.userModel.findOne({
where: { phoneNumber: createUserDto.phoneNumber },
});
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.");
}
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",
},
);
await this.userModel.update(
{ refreshToken },
{ where: { id: user.id } },
);
return { message: "User registered successfully." };
} catch (error) {
if (error instanceof HttpException) {
throw error;
@ -36,14 +69,14 @@ export class UsersService {
}
}
// Login method
async login(loginUserDto: LoginUserDto): Promise<{ accessToken: string; refreshToken: string }> {
async login(loginUserDto: LoginUserDto): Promise<{ accessToken: string , refreshToken:string}> {
try {
const user = await this.userModel.findOne({
where: { email: loginUserDto.email },
where: { email: loginUserDto.email, username:loginUserDto.username },
});
if (!user) {
throw new UnauthorizedException("Invalid email or password.");
throw new UnauthorizedException("Invalid email , username or password.");
}
const passwordMatch = await bcrypt.compare(loginUserDto.password, user.password);
@ -72,7 +105,7 @@ export class UsersService {
{ where: { id: user.id } },
);
return { accessToken: accessToken, refreshToken: refreshToken };
return { accessToken, refreshToken };
} catch (error) {
if (error instanceof HttpException) {
throw error;
@ -83,21 +116,23 @@ export class UsersService {
// getting access token
async newAccessToken(refreshToken: string) {
if (!refreshToken) {
throw new HttpException({ message: "Refresh token is required." }, HttpStatus.BAD_REQUEST);
throw new HttpException("Refresh token is required.", HttpStatus.BAD_REQUEST);
}
let decoded;
try {
decoded = this.jwtService.verify(refreshToken, { secret: process.env.JWT_REFRESH_SECRET });
decoded = this.jwtService.verify(refreshToken, { secret: this.configService.get<string>("JWT_REFRESH_SECRET") });
} catch (error) {
throw new HttpException({ message: "Invalid or expired token." }, HttpStatus.UNAUTHORIZED);
throw new HttpException("Invalid or expired token.", HttpStatus.UNAUTHORIZED);
}
const user = await this.userModel.findOne({
where: { id: decoded.id },
});
const user = await this.getProfile(decoded.id);
if (!user) {
throw new HttpException({ message: "User not found." }, HttpStatus.NOT_FOUND);
throw new HttpException("User not found.", HttpStatus.NOT_FOUND);
}
if (user.refreshToken !== refreshToken) {
throw new HttpException("Invalid refresh token.", HttpStatus.FORBIDDEN);
}
const accessToken = this.jwtService.sign(
@ -111,15 +146,23 @@ export class UsersService {
return { accessToken };
}
//logout (delete refresh token from database)
async logout(userId: number) {
const user = await this.userModel.findOne({
where: { id: userId },
});
if (!user) {
throw new HttpException("User not found.", HttpStatus.NOT_FOUND);
async logout(userId: number): Promise<{ message: string }> {
if (!userId) {
throw new HttpException("User ID is required.", HttpStatus.BAD_REQUEST);
}
try {
const user = await this.userModel.findOne({ where: { id: userId } });
if (!user) {
throw new HttpException("User not found.", HttpStatus.NOT_FOUND);
}
await this.userModel.update({ refreshToken: null }, { where: { id: userId } });
return { message: "Logout is successful." };
} catch (error) {
throw new HttpException("An error occurred while logging out.", HttpStatus.INTERNAL_SERVER_ERROR);
}
await this.userModel.update({ refreshToken: null }, { where: { id: userId } });
return { message: "logout is successful" };
}
//get information user method
async getProfile(userId: number): Promise<User> {
@ -177,16 +220,23 @@ export class UsersService {
}
}
//delete a specific user by admin
async deleteUser(userId: number) {
async deleteUser(userId: number): Promise<{ message: string }> {
const user = await this.userModel.findOne({
where: { id: userId },
});
if (!user) {
throw new HttpException("User not found.", HttpStatus.NOT_FOUND);
throw new HttpException("User not found!.", HttpStatus.NOT_FOUND);
}
try {
const deletedCount = await this.userModel.destroy({ where: { id: userId } });
if (deletedCount === 0) {
throw new HttpException("User not found.", HttpStatus.NOT_FOUND);
}
return { message: "User deleted successfully." };
} catch (error) {
throw new HttpException("An error occurred while deleting the user.", HttpStatus.INTERNAL_SERVER_ERROR);
}
await this.userModel.destroy({
where: { id: userId },
});
return { message: "user deleted successful" };
}
}

@ -27,8 +27,7 @@ export class WalletService {
const wallet = await this.walletModel.findOne({ where: { userId } });
if (!wallet) {
const newWallet = await this.walletModel.create({ userId, balance: 0 });
return { walletId: newWallet.id, userId: newWallet.userId, balance: newWallet.balance };
throw new HttpException('Wallet not found!', HttpStatus.NOT_FOUND)
}
return { balance: wallet.balance };
@ -54,28 +53,37 @@ export class WalletService {
const wallet = await this.walletModel.findOne({ where: { userId } });
if (!wallet) {
throw new Error("Wallet not found");
throw new HttpException("Wallet not found", HttpStatus.NOT_FOUND);
}
if (wallet.balance < amount) {
throw new Error("Insufficient funds");
throw new HttpException("Insufficient funds", HttpStatus.BAD_REQUEST);
}
try {
wallet.balance -= amount;
wallet.balance -= amount;
await this.transactionModel.create({
walletId: wallet.id,
amount: String(amount).startsWith("-") ? String(amount) : `-${amount}`,
});
await wallet.save();
await this.transactionModel.create({
walletId: wallet.id,
amount: `-${amount}`,
});
await wallet.save();
return "Payment processed successfully";
return "Payment processed successfully";
} catch (error) {
console.error("Error processing payment:", error.message);
throw new HttpException("An error occurred while processing the payment.", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
//getting transaction
async getTransactionById(userId: number) {
const wallet = this.getWalletInfo(userId);
const wallet = await this.getWalletInfo(userId);
if (!wallet) {
throw new HttpException("Wallet not found for the user.", HttpStatus.NOT_FOUND);
}
return await this.transactionModel.findAll({
where: { walletId: (await wallet).walletId },
where: { walletId: wallet.walletId },
});
}
}

Loading…
Cancel
Save