diff --git a/src/middleware/validate.middleware.ts b/src/middleware/validate.middleware.ts index 2af161b..a7b5a78 100644 --- a/src/middleware/validate.middleware.ts +++ b/src/middleware/validate.middleware.ts @@ -1,4 +1,4 @@ -import { ZodSchema } from "zod"; +import type { ZodSchema } from "zod"; import type { Request, Response, NextFunction } from "express"; export const validate = diff --git a/src/repositories/order.repo.ts b/src/repositories/order.repo.ts index e69de29..305ba62 100644 --- a/src/repositories/order.repo.ts +++ b/src/repositories/order.repo.ts @@ -0,0 +1,27 @@ +import type { ClientSession } from "mongoose"; +import { OrderModel, type OrderDocument } from "../models/order.model.js"; + +export class OrderRepository { + static create( + data: Omit, + session: ClientSession, + ): Promise { + return OrderModel.create([data], { session }).then((res) => res[0]); + } + + static findById(id: string, session?: ClientSession) { + return OrderModel.findById(id).session(session ?? null); + } + + static findAll() { + return OrderModel.find().lean(); + } + + static findByUser(userId: string) { + return OrderModel.find({ userId }).lean(); + } + + static save(order: OrderDocument, session?: ClientSession) { + return order.save({ session }); + } +} diff --git a/src/repositories/product.repo.ts b/src/repositories/product.repo.ts index e69de29..5a78148 100644 --- a/src/repositories/product.repo.ts +++ b/src/repositories/product.repo.ts @@ -0,0 +1,49 @@ +import type { ClientSession } from "mongoose"; +import { ProductModel } from "../models/product.model.js"; +import type { ProductDocument } from "../models/product.model.js"; + +export class ProductRepository { + static findAll(): Promise { + return ProductModel.find().lean(); + } + + static findByIds( + ids: string[], + session?: ClientSession, + ): Promise { + return ProductModel.find({ _id: { $in: ids } }).session(session ?? null); + } + + static findById(id: string): Promise { + return ProductModel.findById(id); + } + + static create( + data: Pick, + ): Promise { + return ProductModel.create(data); + } + + static updateById( + id: string, + update: Partial>, + ): Promise { + return ProductModel.findByIdAndUpdate(id, update, { new: true }); + } + + static decrementStock(id: string, quantity: number, session: ClientSession) { + return ProductModel.updateOne( + { _id: id, stock: { $gte: quantity } }, + { $inc: { stock: -quantity } }, + { session }, + ); + } + + static incrementStock(id: string, quantity: number, session: ClientSession) { + return ProductModel.updateOne( + { _id: id }, + { $inc: { stock: quantity } }, + { session }, + ); + } +} diff --git a/src/repositories/user.repo.ts b/src/repositories/user.repo.ts index e69de29..c3b9a47 100644 --- a/src/repositories/user.repo.ts +++ b/src/repositories/user.repo.ts @@ -0,0 +1,20 @@ +import type { ClientSession } from "mongoose"; +import { UserModel } from "../models/user.model.js"; +import type { UserDocument } from "../models/user.model.js"; + +export class UserRepository { + static findByEmail(email: string): Promise { + return UserModel.findOne({ email }); + } + + static findById(id: string): Promise { + return UserModel.findById(id); + } + + static create( + data: Pick, + session?: ClientSession, + ): Promise { + return UserModel.create([data], { session }).then((res) => res[0]); + } +} diff --git a/src/routes/auth.routes.ts b/src/routes/auth.routes.ts index c70705a..3ee7f42 100644 --- a/src/routes/auth.routes.ts +++ b/src/routes/auth.routes.ts @@ -1,10 +1,10 @@ import { Router } from "express"; -import { AuthController } from "../controllers/auth.controller"; -import { validate } from "../middleware/validate.middleware"; -import { authRateLimiter } from "../middleware/rateLimit.middleware"; -import { registerSchema, loginSchema } from "../validation/auth.schema"; +import { AuthController } from "../controllers/auth.controller.js"; +import { validate } from "../middleware/validate.middleware.js"; +import { authRateLimiter } from "../middleware/rateLimit.middleware.js"; +import { registerSchema, loginSchema } from "../validation/auth.schema.js"; -const router = Router(); +const router: Router = Router(); router.post( "/register", diff --git a/src/routes/order.routes.ts b/src/routes/order.routes.ts index 8589d90..90abc68 100644 --- a/src/routes/order.routes.ts +++ b/src/routes/order.routes.ts @@ -1,14 +1,14 @@ import { Router } from "express"; -import { OrderController } from "../controllers/order.controller"; -import { authMiddleware } from "../middleware/auth.middleware"; -import { requireRole } from "../middleware/role.middleware"; -import { validate } from "../middleware/validate.middleware"; +import { OrderController } from "../controllers/order.controller.js"; +import { authMiddleware } from "../middleware/auth.middleware.js"; +import { requireRole } from "../middleware/role.middleware.js"; +import { validate } from "../middleware/validate.middleware.js"; import { createOrderSchema, orderIdParamSchema, -} from "../validation/order.schema"; +} from "../validation/order.schema.js"; -const router = Router(); +const router: Router = Router(); router.post( "/", diff --git a/src/routes/product.routes.ts b/src/routes/product.routes.ts index 00b6543..6c60c20 100644 --- a/src/routes/product.routes.ts +++ b/src/routes/product.routes.ts @@ -1,14 +1,14 @@ import { Router } from "express"; -import { ProductController } from "../controllers/product.controller"; -import { authMiddleware } from "../middleware/auth.middleware"; -import { requireRole } from "../middleware/role.middleware"; -import { validate } from "../middleware/validate.middleware"; +import { ProductController } from "../controllers/product.controller.js"; +import { authMiddleware } from "../middleware/auth.middleware.js"; +import { requireRole } from "../middleware/role.middleware.js"; +import { validate } from "../middleware/validate.middleware.js"; import { createProductSchema, updateProductSchema, -} from "../validation/product.schema"; +} from "../validation/product.schema.js"; -const router = Router(); +const router: Router = Router(); router.post( "/", diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts index adb2c2f..cd1fe68 100644 --- a/src/services/auth.service.ts +++ b/src/services/auth.service.ts @@ -1,47 +1,33 @@ -import bcrypt from "bcrypt"; -import { UserModel } from "../models/user.model"; -import { signToken } from "../utils/jwt"; - -const SALT_ROUNDS = 10; +import { UserRepository } from "../repositories/user.repo.js"; +import { hashPassword, verifyPassword } from "../utils/password.js"; +import { signToken } from "../utils/jwt.js"; export class AuthService { static async register(email: string, password: string) { - const existing = await UserModel.findOne({ email }); + const existing = await UserRepository.findByEmail(email); if (existing) { throw { statusCode: 409, message: "Email already registered" }; } - const passwordHash = await bcrypt.hash(password, SALT_ROUNDS); + const passwordHash = await hashPassword(password); - const user = await UserModel.create({ + const user = await UserRepository.create({ email, passwordHash, role: "customer", }); - return { - id: user._id, - email: user.email, - role: user.role, - }; + return { id: user._id, email: user.email, role: user.role }; } static async login(email: string, password: string) { - const user = await UserModel.findOne({ email }); - if (!user) { + const user = await UserRepository.findByEmail(email); + if (!user || !(await verifyPassword(password, user.passwordHash))) { throw { statusCode: 401, message: "Invalid credentials" }; } - const valid = await bcrypt.compare(password, user.passwordHash); - if (!valid) { - throw { statusCode: 401, message: "Invalid credentials" }; - } - - const token = signToken({ - sub: user._id, - role: user.role, - }); - - return { token }; + return { + token: signToken({ sub: user._id, role: user.role }), + }; } } diff --git a/src/services/order.service.ts b/src/services/order.service.ts index 7014460..50992b1 100644 --- a/src/services/order.service.ts +++ b/src/services/order.service.ts @@ -1,6 +1,6 @@ import mongoose from "mongoose"; -import { ProductRepository } from "../repositories/product.repo"; -import { OrderRepository } from "../repositories/order.repo"; +import { ProductRepository } from "../repositories/product.repo.js"; +import { OrderRepository } from "../repositories/order.repo.js"; export class OrderService { static async createOrder( diff --git a/src/services/product.service.ts b/src/services/product.service.ts index feef6ce..4e8d031 100644 --- a/src/services/product.service.ts +++ b/src/services/product.service.ts @@ -1,29 +1,22 @@ -import { ProductModel } from "../models/product.model"; +import { ProductRepository } from "../repositories/product.repo.js"; export class ProductService { - static async create(data: { name: string; price: number; stock: number }) { - const product = await ProductModel.create(data); - return product; + static create(data: { name: string; price: number; stock: number }) { + return ProductRepository.create(data); } - static async list() { - return ProductModel.find().lean(); + static list() { + return ProductRepository.findAll(); } static async update( id: string, data: Partial<{ name: string; price: number; stock: number }>, ) { - const product = await ProductModel.findByIdAndUpdate( - id, - { $set: data }, - { new: true }, - ); - + const product = await ProductRepository.updateById(id, data); if (!product) { throw { statusCode: 404, message: "Product not found" }; } - return product; } }