♻️ refactor(account, order, plan, profile, support, email): restructure application modules and enhance error handling

Updated Docker configuration, refactored middleware for improved error handling, and restructured account, order, plan, profile, and support modules, including their routes, services, and validations. Enhanced email processing queues and utilities for token generation, pagination, and response management to streamline the application architecture and enhance maintainability.
This commit is contained in:
abumahid
2026-04-21 03:12:39 +06:00
parent c881efea0f
commit 0f7af70b90
94 changed files with 2593 additions and 127 deletions
+60
View File
@@ -0,0 +1,60 @@
import catchAsync from "../../utils/catch_async.js";
import manageResponse from "../../utils/manage_response.js";
import { order_service } from "./order.service.js";
const get_all_order = catchAsync(async (req, res) => {
const result = await order_service.get_all_order_from_db(req);
manageResponse(res, {
success: true,
statusCode: 200,
message: "All order fetched successfully.",
data: result,
meta: {},
});
});
const get_single_order = catchAsync(async (req, res) => {
const result = await order_service.get_single_order_from_db(req);
manageResponse(res, {
success: true,
statusCode: 200,
message: "Single order fetched successfully.",
data: result,
meta: {},
});
});
const create_order = catchAsync(async (req, res) => {
const result = await order_service.create_order_into_db(req);
manageResponse(res, {
success: true,
statusCode: 200,
message: "order created successfully.",
data: result,
meta: {},
});
});
const update_order = catchAsync(async (req, res) => {
const result = await order_service.update_order_into_db(req);
manageResponse(res, {
success: true,
statusCode: 200,
message: "order updated successfully.",
data: result,
meta: {},
});
});
const delete_order = catchAsync(async (req, res) => {
const result = await order_service.delete_order_from_db(req);
manageResponse(res, {
success: true,
statusCode: 200,
message: "order deleted successfully.",
data: result,
meta: {},
});
});
export const order_controller = {
get_all_order,
get_single_order,
create_order,
update_order,
delete_order,
};
+12
View File
@@ -0,0 +1,12 @@
import { Router } from "express";
import RequestValidator from "../../middlewares/request_validator.js";
import { order_controller } from "./order.controller.js";
import { order_validations } from "./order.validation.js";
import auth from "../../middlewares/auth.js";
const router = Router();
router.get("/", order_controller.get_all_order);
router.post("/", RequestValidator(order_validations.create_order), order_controller.create_order);
router.get("/:id", order_controller.get_single_order);
router.patch("/:id", auth("ADMIN"), RequestValidator(order_validations.update_order), order_controller.update_order);
router.delete("/:id", order_controller.delete_order);
export default router;
+159
View File
@@ -0,0 +1,159 @@
import { configs } from "../../configs/index.js";
import { prisma } from "../../lib/prisma.js";
import { orderEmailQueue } from "../../queues/email/order/order.email.queue.js";
import { AppError } from "../../utils/app_error.js";
import paginationHelper from "../../utils/pagination_helper.js";
const get_all_order_from_db = async (req) => {
// define your own login here
const search = req.query.search;
const customerName = req.query.customerName;
const productName = req.query.productName;
// for date filter
const startDate = req.query.startDate;
const endDate = req.query.endDate;
const status = req.query.status || undefined;
const { page, limit, skip, sortBy, sortOrder } = paginationHelper(req.query);
const andCondition = [];
if (search) {
andCondition.push({
OR: [
{
productName: {
contains: search,
mode: "insensitive",
},
},
],
});
}
if (customerName) {
andCondition.push({
OR: [
{
customerName: {
contains: customerName,
mode: "insensitive",
},
},
],
});
}
if (productName) {
andCondition.push({
OR: [
{
productName: {
contains: productName,
mode: "insensitive",
},
},
],
});
}
if (status) {
andCondition.push({
OR: [
{
status: {
contains: status,
mode: "insensitive",
},
},
],
});
}
// for date filter
const dateFilter = {};
if (startDate) {
const start = new Date(startDate);
start.setHours(0, 0, 0, 0);
dateFilter.gte = start;
}
if (endDate) {
const end = new Date(endDate);
end.setHours(23, 59, 59, 999);
dateFilter.lte = end;
}
if (Object.keys(dateFilter).length > 0) {
andCondition.push({
createdAt: dateFilter,
});
}
const getAllOrders = await prisma.order.findMany({
take: limit,
skip,
where: {
AND: andCondition,
},
orderBy: {
[sortBy]: sortOrder,
},
});
const result = await prisma.order.count({
where: {
AND: andCondition,
},
});
return {
data: getAllOrders,
pagination: {
total: result,
page,
limit,
totalPages: Math.ceil(result / limit),
},
};
};
const get_single_order_from_db = async (req) => {
// define your own login here
const { id } = req.params;
const result = await prisma.order.findUnique({ where: { id } });
return result;
};
const create_order_into_db = async (req) => {
const payload = req?.body;
console.log(payload);
payload.status = "INITIATED";
payload.paymentType = "COD";
// nwo init order
const result = await prisma.order.create({ data: payload });
// if email exist sent tracking link
if (payload.customerEmail) {
const trackingLink = `${configs.jwt.front_end_url}/track-order/${result.id}`;
await orderEmailQueue.add("order-email-queue", {
email: payload.customerEmail,
subject: "Order Tracking",
textBody: `Your order has been created. Track your order here: ${trackingLink}`,
htmlBody: `<p>Your order has been created. Track your order here: <a href="${trackingLink}">Track Order</a></p>`,
});
}
return result;
};
const update_order_into_db = async (req) => {
// define your own login here
const user = req.user;
console.log(user);
if (user?.role !== "ADMIN") {
throw new AppError("You are not authorized to perform this action", 403);
}
const { id } = req.params;
const isProductExist = await prisma.order.findUnique({ where: { id } });
if (!isProductExist) {
throw new AppError("Order is not found", 404);
}
const result = await prisma.order.update({ where: { id }, data: req.body });
return result;
};
const delete_order_from_db = async (req) => {
// define your own login here
const { id } = req.params;
const result = await prisma.order.delete({ where: { id } });
return result;
};
export const order_service = {
get_all_order_from_db,
get_single_order_from_db,
create_order_into_db,
update_order_into_db,
delete_order_from_db,
};
+161
View File
@@ -0,0 +1,161 @@
export const orderSwaggerDocs = {
"/api/order": {
post: {
tags: ["order"],
summary: "Create new order",
description: "",
requestBody: {
required: true,
content: {
"application/json": {
example: JSON.stringify({
shopAccountId: "",
productPrice: 1500,
productQuantity: 2,
productName: "Wireless Mouse",
customerName: "Rahim Uddin",
customerPhone: "+8801712345678",
customerEmail: "softvence.abumahid@gmail.com",
customerAddress: "Rangpur, Bangladesh",
customerNote: "Please deliver between 3-5 PM",
}),
},
},
},
responses: {
201: { description: "order created successfully" },
500: { description: "Validation error or internal server error" },
},
},
get: {
tags: ["order"],
summary: "Get all order",
description: "",
parameters: [
{
name: "page",
in: "query",
required: false,
schema: { type: "number" },
},
{
name: "limit",
in: "query",
required: false,
schema: { type: "number" },
},
{
name: "search",
in: "query",
required: false,
schema: { type: "string" },
},
{
name: "customerName",
in: "query",
required: false,
schema: { type: "string" },
},
{
name: "productName",
in: "query",
required: false,
schema: { type: "string" },
},
{
name: "status",
in: "query",
required: false,
schema: { type: "string" },
},
{
name: "date",
in: "query",
required: false,
schema: { type: "string" },
},
{
name: "startDate",
in: "query",
required: false,
schema: { type: "string", format: "date" },
example: "2026-04-01",
},
{
name: "endDate",
in: "query",
required: false,
schema: { type: "string", format: "date" },
example: "2026-04-31",
},
],
responses: {
200: { description: "order fetched successfully" },
401: { description: "unauthorized" },
},
},
},
"/api/order/{id}": {
get: {
tags: ["order"],
summary: "Get single order",
description: "",
parameters: [
{
name: "id",
in: "path",
required: true,
schema: { type: "string" },
},
],
responses: {
200: { description: "order fetched successfully" },
401: { description: "unauthorized" },
},
},
patch: {
tags: ["order"],
summary: "Update order",
description: "",
parameters: [
{
name: "id",
in: "path",
required: true,
schema: { type: "string" },
},
],
requestBody: {
required: true,
content: {
"application/json": {
example: JSON.stringify({
status: "INITIATED",
}), // put your request body
},
},
},
responses: {
200: { description: "order updated successfully" },
500: { description: "Validation error or internal server error" },
},
},
delete: {
tags: ["order"],
summary: "Delete order",
description: "",
parameters: [
{
name: "id",
in: "path",
required: true,
schema: { type: "string" },
},
],
responses: {
200: { description: "order delete successfully" },
401: { description: "unauthorized" },
},
},
},
};
+19
View File
@@ -0,0 +1,19 @@
import { z } from "zod";
const create_order = z.object({
shopAccountId: z.string(),
productPrice: z.number(),
productQuantity: z.number(),
productName: z.string(),
customerName: z.string(),
customerPhone: z.string(),
customerEmail: z.string().optional(),
customerAddress: z.string(),
customerNote: z.string().optional()
});
const update_order = z.object({
status: z.string().optional()
});
export const order_validations = {
create_order,
update_order,
};