From 0f7af70b90d2a34d996e72bcb68d9387651ddfda Mon Sep 17 00:00:00 2001 From: abumahid Date: Tue, 21 Apr 2026 03:12:39 +0600 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(account,=20order,?= =?UTF-8?q?=20plan,=20profile,=20support,=20email):=20restructure=20applic?= =?UTF-8?q?ation=20modules=20and=20enhance=20error=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .dockerignore | 9 +- Dockerfile | 27 +- dist/app.js | 37 ++ dist/app/configs/index.js | 25 ++ dist/app/errors/zodError.js | 15 + dist/app/lib/prisma.js | 8 + dist/app/middlewares/auth.js | 23 ++ dist/app/middlewares/global_error_handler.js | 47 +++ dist/app/middlewares/not_found_api.js | 8 + dist/app/middlewares/request_validator.js | 12 + dist/app/middlewares/uploader.js | 12 + .../app/modules/account/account.controller.js | 103 ++++++ dist/app/modules/account/account.route.js | 16 + dist/app/modules/account/account.service.js | 348 ++++++++++++++++++ dist/app/modules/account/account.swagger.js | 187 ++++++++++ .../app/modules/account/account.validation.js | 37 ++ dist/app/modules/order/order.controller.js | 60 +++ dist/app/modules/order/order.route.js | 12 + dist/app/modules/order/order.service.js | 159 ++++++++ dist/app/modules/order/order.swagger.js | 161 ++++++++ dist/app/modules/order/order.validation.js | 19 + dist/app/modules/plan/plan.controller.js | 60 +++ dist/app/modules/plan/plan.route.js | 11 + dist/app/modules/plan/plan.service.js | 68 ++++ dist/app/modules/plan/plan.swagger.js | 125 +++++++ dist/app/modules/plan/plan.validation.js | 33 ++ .../app/modules/profile/profile.controller.js | 15 + dist/app/modules/profile/profile.route.js | 12 + dist/app/modules/profile/profile.service.js | 22 ++ dist/app/modules/profile/profile.swagger.js | 34 ++ .../app/modules/profile/profile.validation.js | 7 + .../app/modules/support/support.controller.js | 80 ++++ dist/app/modules/support/support.route.js | 12 + dist/app/modules/support/support.service.js | 100 +++++ dist/app/modules/support/support.swagger.js | 109 ++++++ .../app/modules/support/support.validation.js | 28 ++ dist/app/queues/connection.js | 4 + dist/app/queues/email/email.processor.js | 14 + dist/app/queues/email/email.queue.js | 6 + dist/app/queues/email/email.worker.js | 7 + .../email/order/order.email.processor.js | 12 + .../queues/email/order/order.email.queue.js | 5 + .../queues/email/order/order.email.worker.js | 7 + dist/app/queues/worker.js | 3 + dist/app/templates/otpTemplate.js | 80 ++++ dist/app/utils/JWT.js | 15 + dist/app/utils/app_error.js | 13 + dist/app/utils/catch_async.js | 11 + dist/app/utils/cloudinary.js | 23 ++ dist/app/utils/mail_sender.js | 113 ++++++ dist/app/utils/manage_response.js | 9 + dist/app/utils/otpGenerator.js | 23 ++ dist/app/utils/pagination_helper.js | 15 + dist/routes.js | 16 + dist/server.js | 19 + dist/swaggerOptions.js | 48 +++ package.json | 1 + prisma/schema/schema.prisma | 3 +- src/app.ts | 8 +- src/app/configs/index.ts | 1 + src/app/errors/zodError.ts | 2 +- src/app/lib/prisma.ts | 3 +- src/app/middlewares/auth.ts | 6 +- src/app/middlewares/global_error_handler.ts | 8 +- src/app/modules/account/account.controller.ts | 8 +- src/app/modules/account/account.route.ts | 8 +- src/app/modules/account/account.service.ts | 16 +- src/app/modules/order/order.controller.ts | 6 +- src/app/modules/order/order.route.ts | 8 +- src/app/modules/order/order.service.ts | 10 +- src/app/modules/plan/plan.controller.ts | 6 +- src/app/modules/plan/plan.route.ts | 6 +- src/app/modules/plan/plan.service.ts | 4 +- src/app/modules/profile/profile.controller.ts | 6 +- src/app/modules/profile/profile.route.ts | 10 +- src/app/modules/profile/profile.service.ts | 6 +- src/app/modules/support/support.controller.ts | 6 +- src/app/modules/support/support.route.ts | 8 +- src/app/modules/support/support.service.ts | 6 +- src/app/queues/connection.ts | 11 +- src/app/queues/email/email.processor.ts | 6 +- src/app/queues/email/email.queue.ts | 2 +- src/app/queues/email/email.worker.ts | 6 +- .../email/order/order.email.processor.ts | 4 +- .../queues/email/order/order.email.queue.ts | 2 +- .../queues/email/order/order.email.worker.ts | 6 +- src/app/queues/worker.ts | 4 +- src/app/templates/otpTemplate.ts | 2 +- src/app/utils/cloudinary.ts | 2 +- src/app/utils/mail_sender.ts | 2 +- src/routes.ts | 10 +- src/server.ts | 8 +- src/swaggerOptions.ts | 16 +- tsconfig.json | 9 +- 94 files changed, 2593 insertions(+), 127 deletions(-) create mode 100644 dist/app.js create mode 100644 dist/app/configs/index.js create mode 100644 dist/app/errors/zodError.js create mode 100644 dist/app/lib/prisma.js create mode 100644 dist/app/middlewares/auth.js create mode 100644 dist/app/middlewares/global_error_handler.js create mode 100644 dist/app/middlewares/not_found_api.js create mode 100644 dist/app/middlewares/request_validator.js create mode 100644 dist/app/middlewares/uploader.js create mode 100644 dist/app/modules/account/account.controller.js create mode 100644 dist/app/modules/account/account.route.js create mode 100644 dist/app/modules/account/account.service.js create mode 100644 dist/app/modules/account/account.swagger.js create mode 100644 dist/app/modules/account/account.validation.js create mode 100644 dist/app/modules/order/order.controller.js create mode 100644 dist/app/modules/order/order.route.js create mode 100644 dist/app/modules/order/order.service.js create mode 100644 dist/app/modules/order/order.swagger.js create mode 100644 dist/app/modules/order/order.validation.js create mode 100644 dist/app/modules/plan/plan.controller.js create mode 100644 dist/app/modules/plan/plan.route.js create mode 100644 dist/app/modules/plan/plan.service.js create mode 100644 dist/app/modules/plan/plan.swagger.js create mode 100644 dist/app/modules/plan/plan.validation.js create mode 100644 dist/app/modules/profile/profile.controller.js create mode 100644 dist/app/modules/profile/profile.route.js create mode 100644 dist/app/modules/profile/profile.service.js create mode 100644 dist/app/modules/profile/profile.swagger.js create mode 100644 dist/app/modules/profile/profile.validation.js create mode 100644 dist/app/modules/support/support.controller.js create mode 100644 dist/app/modules/support/support.route.js create mode 100644 dist/app/modules/support/support.service.js create mode 100644 dist/app/modules/support/support.swagger.js create mode 100644 dist/app/modules/support/support.validation.js create mode 100644 dist/app/queues/connection.js create mode 100644 dist/app/queues/email/email.processor.js create mode 100644 dist/app/queues/email/email.queue.js create mode 100644 dist/app/queues/email/email.worker.js create mode 100644 dist/app/queues/email/order/order.email.processor.js create mode 100644 dist/app/queues/email/order/order.email.queue.js create mode 100644 dist/app/queues/email/order/order.email.worker.js create mode 100644 dist/app/queues/worker.js create mode 100644 dist/app/templates/otpTemplate.js create mode 100644 dist/app/utils/JWT.js create mode 100644 dist/app/utils/app_error.js create mode 100644 dist/app/utils/catch_async.js create mode 100644 dist/app/utils/cloudinary.js create mode 100644 dist/app/utils/mail_sender.js create mode 100644 dist/app/utils/manage_response.js create mode 100644 dist/app/utils/otpGenerator.js create mode 100644 dist/app/utils/pagination_helper.js create mode 100644 dist/routes.js create mode 100644 dist/server.js create mode 100644 dist/swaggerOptions.js diff --git a/.dockerignore b/.dockerignore index bdb87c7..53b26c4 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,8 +1,9 @@ node_modules -npm-debug.log -Dockerfile +dist .git .gitignore -README.md +Dockerfile +docker-compose.yml +*.log .env -dist +uploads \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 254f135..d439d1b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,25 +1,40 @@ # ---------- BUILD STAGE ---------- -FROM node:18-alpine AS builder +FROM node:20-alpine AS builder WORKDIR /app +# Only install deps first (cache friendly) COPY package*.json ./ -RUN npm install +RUN npm ci +# Copy source COPY . . + +# Generate prisma + build +RUN npx prisma generate RUN npm run build # ---------- PRODUCTION STAGE ---------- -FROM node:18-alpine +FROM node:20-alpine WORKDIR /app -COPY package*.json ./ -RUN npm install && npm cache clean --force +ENV NODE_ENV=production +# Only install production deps +COPY package*.json ./ +RUN npm ci --omit=dev && npm cache clean --force + +# Copy Prisma generated client + schema +COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma +COPY --from=builder /app/node_modules/@prisma ./node_modules/@prisma +COPY --from=builder /app/prisma ./prisma + +# Copy built app COPY --from=builder /app/dist ./dist - + +# Uploads folder RUN mkdir -p /app/uploads EXPOSE 5000 diff --git a/dist/app.js b/dist/app.js new file mode 100644 index 0000000..73f1553 --- /dev/null +++ b/dist/app.js @@ -0,0 +1,37 @@ +import cookieParser from 'cookie-parser'; +import cors from 'cors'; +import express from 'express'; +import swaggerJSDoc from 'swagger-jsdoc'; +import swaggerUi from "swagger-ui-express"; +import globalErrorHandler from './app/middlewares/global_error_handler.js'; +import notFound from './app/middlewares/not_found_api.js'; +import appRouter from './routes.js'; +import { swaggerOptions } from './swaggerOptions.js'; +// define app +const app = express(); +const swaggerSpec = swaggerJSDoc(swaggerOptions); +app.use("/docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec)); +// middleware +app.use(cors({ + origin: ["http://localhost:3000"], + methods: ["GET", "POST", "PATCH", "DELETE", "PUT"], + credentials: true +})); +app.use(express.json({ limit: "100mb" })); +app.use(express.raw()); +app.use(cookieParser()); +app.use(express.urlencoded({ extended: true })); +app.use("/api", appRouter); +// stating point +app.get('/', (req, res) => { + res.status(200).json({ + status: 'success', + message: 'Server is running successful !!', + data: null, + }); +}); +// global error handler +app.use(globalErrorHandler); +app.use(notFound); +// export app +export default app; diff --git a/dist/app/configs/index.js b/dist/app/configs/index.js new file mode 100644 index 0000000..96427ee --- /dev/null +++ b/dist/app/configs/index.js @@ -0,0 +1,25 @@ +import "dotenv/config"; +export const configs = { + port: process.env.PORT, + env: process.env.NODE_ENV, + db_url: process.env.DATABASE_URL, + jwt: { + access_token: process.env.ACCESS_TOKEN, + refresh_token: process.env.REFRESH_TOKEN, + access_expires: process.env.ACCESS_EXPIRES, + refresh_expires: process.env.REFRESH_EXPIRES, + reset_secret: process.env.RESET_SECRET, + reset_expires: process.env.RESET_EXPIRES, + front_end_url: process.env.FRONT_END_URL, + verified_token: process.env.VERIFIED_TOKEN, + }, + email: { + app_email: process.env.APP_USER_EMAIL, + app_password: process.env.APP_PASSWORD, + }, + cloudinary: { + cloud_name: process.env.CLOUD_NAME, + cloud_api_key: process.env.CLOUD_API_KEY, + cloud_api_secret: process.env.CLOUD_API_SECRET, + }, +}; diff --git a/dist/app/errors/zodError.js b/dist/app/errors/zodError.js new file mode 100644 index 0000000..77da904 --- /dev/null +++ b/dist/app/errors/zodError.js @@ -0,0 +1,15 @@ +const handleZodError = (err) => { + const errorSources = err.issues.map((issue) => { + return { + path: issue?.path[issue.path.length - 1], + message: issue.message + }; + }); + const statusCode = 400; + return { + statusCode, + message: 'Validation Error', + errorSources + }; +}; +export default handleZodError; diff --git a/dist/app/lib/prisma.js b/dist/app/lib/prisma.js new file mode 100644 index 0000000..e9b8c5b --- /dev/null +++ b/dist/app/lib/prisma.js @@ -0,0 +1,8 @@ +import { PrismaPg } from "@prisma/adapter-pg"; +import pkg from "@prisma/client"; +import "dotenv/config"; +const { PrismaClient } = pkg; +const connectionString = `${process.env.DATABASE_URL}`; +const adapter = new PrismaPg({ connectionString }); +const prisma = new PrismaClient({ adapter }); +export { prisma }; diff --git a/dist/app/middlewares/auth.js b/dist/app/middlewares/auth.js new file mode 100644 index 0000000..20cd2c0 --- /dev/null +++ b/dist/app/middlewares/auth.js @@ -0,0 +1,23 @@ +import { configs } from "../configs/index.js"; +import { AppError } from "../utils/app_error.js"; +import { jwtHelpers } from "../utils/JWT.js"; +const auth = (...roles) => { + return async (req, res, next) => { + try { + const token = req.headers.authorization || req.cookies.access_token; + if (!token) { + throw new AppError("You are not authorize!!", 401); + } + const verifiedUser = jwtHelpers.verifyToken(token, configs.jwt.access_token); + if (!roles.length || !roles.includes(verifiedUser.role)) { + throw new AppError("You are not authorize!!", 401); + } + req.user = verifiedUser; + next(); + } + catch (err) { + next(err); + } + }; +}; +export default auth; diff --git a/dist/app/middlewares/global_error_handler.js b/dist/app/middlewares/global_error_handler.js new file mode 100644 index 0000000..c56e5eb --- /dev/null +++ b/dist/app/middlewares/global_error_handler.js @@ -0,0 +1,47 @@ +import { ZodError } from "zod"; +import { configs } from "../configs/index.js"; +import handleZodError from "../errors/zodError.js"; +import { AppError } from "../utils/app_error.js"; +const globalErrorHandler = (err, req, res, next) => { + let statusCode = 500; + let message = "Something went wrong!"; + let errorSources = [ + { + path: "", + message: "Something went wrong", + }, + ]; + if (err instanceof ZodError) { + const simplifiedError = handleZodError(err); + statusCode = simplifiedError?.statusCode; + message = simplifiedError?.message; + errorSources = simplifiedError?.errorSources; + } + else if (err instanceof AppError) { + statusCode = err?.statusCode; + message = err.message; + errorSources = [ + { + path: "", + message: err?.message, + }, + ]; + } + else if (err instanceof Error) { + message = err.message; + errorSources = [ + { + path: "", + message: err?.message, + }, + ]; + } + res.status(statusCode).json({ + success: false, + message, + errorSources, + err, + stack: configs.env === "development" ? err?.stack : null, + }); +}; +export default globalErrorHandler; diff --git a/dist/app/middlewares/not_found_api.js b/dist/app/middlewares/not_found_api.js new file mode 100644 index 0000000..ab074e0 --- /dev/null +++ b/dist/app/middlewares/not_found_api.js @@ -0,0 +1,8 @@ +const notFound = (req, res, next) => { + res.status(404).json({ + message: 'Sorry Route is not found!! 😴😴😴', + success: false, + error: '', + }); +}; +export default notFound; diff --git a/dist/app/middlewares/request_validator.js b/dist/app/middlewares/request_validator.js new file mode 100644 index 0000000..73024a8 --- /dev/null +++ b/dist/app/middlewares/request_validator.js @@ -0,0 +1,12 @@ +const RequestValidator = (schema) => { + return async (req, res, next) => { + try { + req.body = await schema.parseAsync(req.body); + next(); + } + catch (err) { + next(err); + } + }; +}; +export default RequestValidator; diff --git a/dist/app/middlewares/uploader.js b/dist/app/middlewares/uploader.js new file mode 100644 index 0000000..da05753 --- /dev/null +++ b/dist/app/middlewares/uploader.js @@ -0,0 +1,12 @@ +import multer from "multer"; +import path from "path"; +const storage = multer.diskStorage({ + destination: function (req, file, cb) { + cb(null, path.join(process.cwd(), "uploads")); + }, + filename: function (req, file, cb) { + cb(null, file.originalname); + } +}); +const uploader = multer({ storage: storage }); +export default uploader; diff --git a/dist/app/modules/account/account.controller.js b/dist/app/modules/account/account.controller.js new file mode 100644 index 0000000..30f7321 --- /dev/null +++ b/dist/app/modules/account/account.controller.js @@ -0,0 +1,103 @@ +import { configs } from "../../configs/index.js"; +import catchAsync from "../../utils/catch_async.js"; +import manageResponse from "../../utils/manage_response.js"; +import { account_services } from "./account.service.js"; +const create_account = catchAsync(async (req, res) => { + const result = await account_services.create_account_into_db(req); + manageResponse(res, { + statusCode: 200, + success: true, + message: "Account created successfully", + data: result, + }); +}); +const verify_account_using_otp = catchAsync(async (req, res) => { + const result = await account_services.verify_account_using_otp_into_db(req); + manageResponse(res, { + statusCode: 200, + success: true, + message: "Otp verification successful", + data: result, + }); +}); +const verify_account_using_link = catchAsync(async (req, res) => { + const result = await account_services.verify_account_using_link_into_db(req); + manageResponse(res, { + statusCode: 200, + success: true, + message: "Account verification successful", + data: result, + }); +}); +const login_user = catchAsync(async (req, res) => { + const result = await account_services.login_user_into_db(req); + // set access token into cookie + res.cookie("access_token", result, { + secure: configs.env === "production", + httpOnly: true, + }); + manageResponse(res, { + statusCode: 200, + success: true, + message: "User logged in successfully", + data: { + accessToken: result, + }, + }); +}); +const get_user_account = catchAsync(async (req, res) => { + const result = await account_services.get_user_account_from_db(req); + manageResponse(res, { + statusCode: 200, + success: true, + message: "Account fetched successfully", + data: result, + }); +}); +const change_password = catchAsync(async (req, res) => { + const result = await account_services.change_password_into_db(req); + manageResponse(res, { + statusCode: 200, + success: true, + message: "Password Change successfully", + data: result, + }); +}); +const resend_otp_and_verification_link = catchAsync(async (req, res) => { + const result = await account_services.resend_otp_and_verification_link_from_db(req); + manageResponse(res, { + statusCode: 200, + success: true, + message: "OTP reset successfully", + data: result, + }); +}); +const forget_password_generate_reset_token = catchAsync(async (req, res) => { + const result = await account_services.forget_password_generate_reset_token_from_db(req); + manageResponse(res, { + statusCode: 200, + success: true, + message: "Password reset successfully", + data: result, + }); +}); +const reset_password_using_token = catchAsync(async (req, res) => { + const result = await account_services.reset_password_using_token_into_db(req); + manageResponse(res, { + statusCode: 200, + success: true, + message: "Password reset successfully", + data: result, + }); +}); +export const account_controller = { + create_account, + login_user, + get_user_account, + change_password, + verify_account_using_otp, + resend_otp_and_verification_link, + verify_account_using_link, + forget_password_generate_reset_token, + reset_password_using_token +}; diff --git a/dist/app/modules/account/account.route.js b/dist/app/modules/account/account.route.js new file mode 100644 index 0000000..7ac80a0 --- /dev/null +++ b/dist/app/modules/account/account.route.js @@ -0,0 +1,16 @@ +import { Router } from "express"; +import auth from "../../middlewares/auth.js"; +import RequestValidator from "../../middlewares/request_validator.js"; +import { account_controller } from "./account.controller.js"; +import { account_validation } from "./account.validation.js"; +const accountRouter = Router(); +accountRouter.post("/sign-up", RequestValidator(account_validation.sign_up), account_controller.create_account); +accountRouter.post("/sign-in", RequestValidator(account_validation.sing_in), account_controller.login_user); +accountRouter.put("/verify-otp", RequestValidator(account_validation.verify_otp), account_controller.verify_account_using_otp); +accountRouter.put("/verify-link", RequestValidator(account_validation.verify_link), account_controller.verify_account_using_link); +accountRouter.get("/me", auth("USER", "ADMIN"), account_controller.get_user_account); +accountRouter.put("/change-password", auth("USER", "ADMIN"), RequestValidator(account_validation.change_password), account_controller.change_password); +accountRouter.put("/resend-otp", RequestValidator(account_validation.resend_otp), account_controller.resend_otp_and_verification_link); +accountRouter.put("/forget-password", RequestValidator(account_validation.resend_otp), account_controller.forget_password_generate_reset_token); +accountRouter.put("/reset-password", RequestValidator(account_validation.reset_pass), account_controller.reset_password_using_token); +export default accountRouter; diff --git a/dist/app/modules/account/account.service.js b/dist/app/modules/account/account.service.js new file mode 100644 index 0000000..8e55b46 --- /dev/null +++ b/dist/app/modules/account/account.service.js @@ -0,0 +1,348 @@ +import bcrypt from "bcrypt"; +import { configs } from "../../configs/index.js"; +import { prisma } from "../../lib/prisma.js"; +import { emailQueue } from "../../queues/email/email.queue.js"; +import { AppError } from "../../utils/app_error.js"; +import { jwtHelpers } from "../../utils/JWT.js"; +import sendMail from "../../utils/mail_sender.js"; +import { otpGenerator } from "../../utils/otpGenerator.js"; +const create_account_into_db = async (req) => { + const payload = req?.body; + // check account exist or not + const existingAccount = await prisma.account.findUnique({ + where: { email: payload.email }, + }); + if (existingAccount) { + throw new AppError("Email already exists", 403); + } + // hash password + const hashPassword = bcrypt.hashSync(payload.password, 10); + // create account and profile + const result = await prisma.$transaction(async (tx) => { + const account = await tx.account.create({ + data: { + email: payload.email, + password: hashPassword, + }, + }); + const profile = await tx.profile.create({ + data: { + shopName: payload.shopName, + accountId: account.id, + }, + }); + return { + account, + profile, + }; + }); + // sending otp and verification link + const newOtp = otpGenerator(); + const verificationToken = jwtHelpers.generateToken({ + email: payload.email, + accountId: result.account.id, + }, configs.jwt.verified_token, "5m"); + const verificationLink = `${configs.jwt.front_end_url}/verify/token?=${verificationToken}`; + // save otp into db + await prisma.account.update({ + where: { + email: payload.email, + }, + data: { + lastOtp: newOtp, + lastOtpSendingTime: new Date(), + }, + }); + await emailQueue.add("email-queue", { + name: payload.shopName, + otp: newOtp, + verificationLink: verificationLink, + subject: "Welcome to Quick Launch - Verification OTP", + email: payload.email, + textBody: "You can use otp or verification link for verifying your account" + }); + return null; +}; +const verify_account_using_otp_into_db = async (req) => { + const payload = req?.body; + // check account + const account = await prisma.account.findUnique({ + where: { + email: payload.email, + }, + }); + // check if account exists + if (!account) { + throw new AppError("Account not found", 404); + } + // match with last otp + const isOtpMatch = payload.otp === account.lastOtp; + if (!isOtpMatch) { + throw new AppError("Invalid OTP, Please try again!!", 401); + } + // check otp timing + const OTP_EXPIRY_TIME = 5 * 60 * 1000; // 5 minutes in ms + const isOtpExpired = account.lastOtpSendingTime + ? new Date().getTime() - new Date(account.lastOtpSendingTime).getTime() > + OTP_EXPIRY_TIME + : true; + if (isOtpExpired) { + throw new AppError("OTP Expired, Please try again!!", 401); + } + // change account status + await prisma.account.update({ + where: { + id: account.id, + }, + data: { + isAccountVerified: true, + }, + }); + // infuter user welcome email + return ""; +}; +const verify_account_using_link_into_db = async (req) => { + const token = req?.body?.token; + let decoadeToken; + try { + decoadeToken = jwtHelpers.verifyToken(token, configs.jwt.verified_token); + } + catch (error) { + if (error?.message == "invalid signature") { + throw new AppError("Invalid Token", 403); + } + else if (error?.message == "jwt expired") { + throw new AppError("Token expired, please reset again", 403); + } + } + // check account + const account = await prisma.account.findUnique({ + where: { + email: decoadeToken.email, + }, + }); + // check if account exists + if (!account) { + throw new AppError("Account not found", 404); + } + // change account status + await prisma.account.update({ + where: { + id: account.id, + }, + data: { + isAccountVerified: true, + }, + }); + // infuter user welcome email + return ""; +}; +const login_user_into_db = async (req) => { + const payload = req?.body; + const account = await prisma.account.findUnique({ + where: { + email: payload.email, + }, + }); + // check if account exists + if (!account) { + throw new AppError("Account not found", 404); + } + // checking password + const isPasswordMatch = bcrypt.compareSync(payload.password, account.password); + if (!isPasswordMatch) { + throw new AppError("Invalid password", 401); + } + // check if account is deleted + if (account.isDeleted) { + throw new AppError("Account is deleted", 401); + } + // check if account is verified + if (!account.isAccountVerified) { + throw new AppError("Account is not verified", 401); + } + // generate access + const accessToken = jwtHelpers.generateToken({ + email: account.email, + role: account.role, + accountId: account.id, + }, configs.jwt.access_token, configs.jwt.access_expires); + return accessToken; +}; +const get_user_account_from_db = async (req) => { + const user = req?.user; + const result = await prisma.account.findUnique({ + where: { + id: user?.accountId, + }, + select: { + id: true, + email: true, + role: true, + isAccountVerified: true, + isDeleted: true, + profile: true, + }, + }); + return result; +}; +const change_password_into_db = async (req) => { + const user = req?.user; + // payload + const payload = req?.body; + // check old and new password is not same + const isSamePassword = payload.oldPassword === payload.newPassword; + if (isSamePassword) { + throw new AppError("Old and new password are same, Please provide deffirent password", 404); + } + // check user validity + const isUserExist = await prisma.account.findFirst({ + where: { + email: user?.email, + }, + }); + // if account not exists + if (!isUserExist) { + throw new AppError("Account not found!!", 404); + } + // check old password + const isPasswordMatch = bcrypt.compareSync(payload.oldPassword, isUserExist.password); + if (!isPasswordMatch) { + throw new AppError("Incorrect password", 401); + } + // change password logic + const newHashPassword = bcrypt.hashSync(payload.newPassword, 10); + await prisma.account.update({ + where: { + id: isUserExist.id, + }, + data: { + password: newHashPassword, + }, + }); + // in future email notification for more sucurity + return ""; +}; +const resend_otp_and_verification_link_from_db = async (req) => { + const email = req?.body?.email; + const account = await prisma.account.findUnique({ + where: { + email, + }, + }); + // check if account exists + if (!account) { + throw new AppError("Account not found", 404); + } + // if already verified + if (account.isAccountVerified) { + throw new AppError("Account already verified", 403); + } + // make new otp and verification link + const newOtp = otpGenerator(); + const verificationToken = jwtHelpers.generateToken({ + email, + accountId: account.id, + }, configs.jwt.verified_token, "5m"); + const verificationLink = `${configs.jwt.front_end_url}/verify/token?=${verificationToken}`; + // save otp into db + await prisma.account.update({ + where: { + email, + }, + data: { + lastOtp: newOtp, + lastOtpSendingTime: new Date(), + }, + }); + await sendMail({ + to: email, + subject: "New Verification otp and link", + htmlBody: ` +

OTP ${newOtp}

+ Otp will be expire in 5 minutes + +

+ +

Or you can use Verification link

+

${verificationLink}

+ `, + textBody: "You can use otp or direct link", + }); +}; +const forget_password_generate_reset_token_from_db = async (req) => { + const email = req?.body?.email; + const account = await prisma.account.findUnique({ + where: { + email, + }, + }); + // check if account exists + if (!account) { + throw new AppError("Account not found", 404); + } + // generate forget token + const verificationToken = jwtHelpers.generateToken({ + email: email, + accountId: account.id, + }, configs.jwt.verified_token, "5m"); + const verificationLink = `${configs.jwt.front_end_url}/verify/token?=${verificationToken}`; + await sendMail({ + to: email, + subject: "Forget Password- Use this link for new password ", + htmlBody: ` +

Your Reset Link:

+

${verificationLink}

+ `, + textBody: "", + }); +}; +const reset_password_using_token_into_db = async (req) => { + const token = req?.body?.token; + const newPass = req?.body?.newPass; + let decoadeToken; + try { + decoadeToken = jwtHelpers.verifyToken(token, configs.jwt.verified_token); + } + catch (error) { + if (error?.message == "invalid signature") { + throw new AppError("Invalid Token", 403); + } + else if (error?.message == "jwt expired") { + throw new AppError("Link expired, please reset again", 403); + } + } + // check account + const account = await prisma.account.findUnique({ + where: { + email: decoadeToken.email, + }, + }); + // check if account exists + if (!account) { + throw new AppError("Account not found", 404); + } + // change account password + const newHash = bcrypt.hashSync(newPass, 10); + await prisma.account.update({ + where: { + id: account.id, + }, + data: { + password: newHash, + }, + }); + // infuter user alart for changing password + return ""; +}; +export const account_services = { + create_account_into_db, + login_user_into_db, + get_user_account_from_db, + change_password_into_db, + verify_account_using_otp_into_db, + resend_otp_and_verification_link_from_db, + verify_account_using_link_into_db, + forget_password_generate_reset_token_from_db, + reset_password_using_token_into_db +}; diff --git a/dist/app/modules/account/account.swagger.js b/dist/app/modules/account/account.swagger.js new file mode 100644 index 0000000..c756f62 --- /dev/null +++ b/dist/app/modules/account/account.swagger.js @@ -0,0 +1,187 @@ +export const accountSwaggerDocs = { + "/api/auth/sign-up": { + post: { + tags: ["account"], + summary: "Create new account", + description: "", + requestBody: { + required: true, + content: { + "application/json": { + example: JSON.stringify({ + email: "user@gmail.com", + password: "password", + shopName: "User", + }), + }, + }, + }, + responses: { + 201: { description: "account created successfully" }, + 500: { description: "Validation error or internal server error" }, + }, + }, + }, + "/api/auth/sign-in": { + post: { + tags: ["account"], + summary: "Sign In your account", + description: "", + requestBody: { + required: true, + content: { + "application/json": { + example: JSON.stringify({ + email: "user@gmail.com", + password: "password", + }), + }, + }, + }, + responses: { + 201: { description: "User signed in successfully" }, + 500: { description: "Validation error or internal server error" }, + }, + }, + }, + "/api/auth/verify-otp": { + put: { + tags: ["account"], + summary: "Verify OTP", + description: "", + requestBody: { + required: true, + content: { + "application/json": { + example: JSON.stringify({ + email: "user@gmail.com", + otp: "654321", + }), + }, + }, + }, + responses: { + 201: { description: "OTP verification successfully" }, + 500: { description: "Validation error or internal server error" }, + }, + }, + }, + "/api/auth/verify-link": { + put: { + tags: ["account"], + summary: "Verify Link", + description: "", + requestBody: { + required: true, + content: { + "application/json": { + example: JSON.stringify({ + token: "dsakfjasdkj", + }), + }, + }, + }, + responses: { + 201: { description: "Token verification successfully" }, + 500: { description: "Validation error or internal server error" }, + }, + }, + }, + "/api/auth/me": { + get: { + tags: ["account"], + summary: "Get me account", + description: "", + responses: { + 200: { description: "account fetched successfully" }, + 401: { description: "unauthorized" }, + }, + }, + }, + "/api/auth/change-password": { + put: { + tags: ["account"], + summary: "Change Password", + description: "", + requestBody: { + required: true, + content: { + "application/json": { + example: JSON.stringify({ + oldPassword: "123456", + newPassword: "654321", + }), + }, + }, + }, + responses: { + 201: { description: "Passwrod change successfully" }, + 500: { description: "Validation error or internal server error" }, + }, + }, + }, + "/api/auth/resend-otp": { + put: { + tags: ["account"], + summary: "Resend OTP", + description: "", + requestBody: { + required: true, + content: { + "application/json": { + example: JSON.stringify({ + email: "user@gmail.com", + }), + }, + }, + }, + responses: { + 201: { description: "OTP resend successfully" }, + 500: { description: "Validation error or internal server error" }, + }, + }, + }, + "/api/auth/forget-password": { + put: { + tags: ["account"], + summary: "Forget Password", + description: "", + requestBody: { + required: true, + content: { + "application/json": { + example: JSON.stringify({ + email: "user@gmail.com", + }), + }, + }, + }, + responses: { + 201: { description: "Forget password successfully" }, + 500: { description: "Validation error or internal server error" }, + }, + }, + }, + "/api/auth/reset-password": { + put: { + tags: ["account"], + summary: "Reset Password", + description: "", + requestBody: { + required: true, + content: { + "application/json": { + example: JSON.stringify({ + token: "dkfjadskfds", + newPass: "newpass", + }), + }, + }, + }, + responses: { + 201: { description: "Password reset successfully" }, + 500: { description: "Validation error or internal server error" }, + }, + }, + }, +}; diff --git a/dist/app/modules/account/account.validation.js b/dist/app/modules/account/account.validation.js new file mode 100644 index 0000000..c761159 --- /dev/null +++ b/dist/app/modules/account/account.validation.js @@ -0,0 +1,37 @@ +import z from "zod"; +const sign_up = z.object({ + email: z.string("Email is required."), + password: z.string("Password is required."), + shopName: z.string("Full name is required."), +}); +const sing_in = z.object({ + email: z.string("Email is required."), + password: z.string("Password is required."), +}); +const change_password = z.object({ + oldPassword: z.string("Old Password is required"), + newPassword: z.string("New Password is required"), +}); +const verify_otp = z.object({ + email: z.string("Email is required"), + otp: z.string("OTP is required"), +}); +const verify_link = z.object({ + token: z.string("Token is required "), +}); +const resend_otp = z.object({ + email: z.string("Email is required"), +}); +const reset_pass = z.object({ + token: z.string("Token is required"), + newPass: z.string("Password is required"), +}); +export const account_validation = { + sign_up, + sing_in, + change_password, + verify_otp, + resend_otp, + verify_link, + reset_pass +}; diff --git a/dist/app/modules/order/order.controller.js b/dist/app/modules/order/order.controller.js new file mode 100644 index 0000000..d35964e --- /dev/null +++ b/dist/app/modules/order/order.controller.js @@ -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, +}; diff --git a/dist/app/modules/order/order.route.js b/dist/app/modules/order/order.route.js new file mode 100644 index 0000000..6950849 --- /dev/null +++ b/dist/app/modules/order/order.route.js @@ -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; diff --git a/dist/app/modules/order/order.service.js b/dist/app/modules/order/order.service.js new file mode 100644 index 0000000..d0f67c4 --- /dev/null +++ b/dist/app/modules/order/order.service.js @@ -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: `

Your order has been created. Track your order here: Track Order

`, + }); + } + 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, +}; diff --git a/dist/app/modules/order/order.swagger.js b/dist/app/modules/order/order.swagger.js new file mode 100644 index 0000000..3567dd4 --- /dev/null +++ b/dist/app/modules/order/order.swagger.js @@ -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" }, + }, + }, + }, +}; diff --git a/dist/app/modules/order/order.validation.js b/dist/app/modules/order/order.validation.js new file mode 100644 index 0000000..0debae2 --- /dev/null +++ b/dist/app/modules/order/order.validation.js @@ -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, +}; diff --git a/dist/app/modules/plan/plan.controller.js b/dist/app/modules/plan/plan.controller.js new file mode 100644 index 0000000..b03bf85 --- /dev/null +++ b/dist/app/modules/plan/plan.controller.js @@ -0,0 +1,60 @@ +import catchAsync from "../../utils/catch_async.js"; +import manageResponse from "../../utils/manage_response.js"; +import { plan_service } from "./plan.service.js"; +const get_all_plan = catchAsync(async (req, res) => { + const result = await plan_service.get_all_plan_from_db(req); + manageResponse(res, { + success: true, + statusCode: 200, + message: "All plan fetched successfully.", + data: result, + meta: {}, + }); +}); +const get_single_plan = catchAsync(async (req, res) => { + const result = await plan_service.get_single_plan_from_db(req); + manageResponse(res, { + success: true, + statusCode: 200, + message: "Single plan fetched successfully.", + data: result, + meta: {}, + }); +}); +const create_plan = catchAsync(async (req, res) => { + const result = await plan_service.create_plan_into_db(req); + manageResponse(res, { + success: true, + statusCode: 200, + message: "plan created successfully.", + data: result, + meta: {}, + }); +}); +const update_plan = catchAsync(async (req, res) => { + const result = await plan_service.update_plan_into_db(req); + manageResponse(res, { + success: true, + statusCode: 200, + message: "plan updated successfully.", + data: result, + meta: {}, + }); +}); +const delete_plan = catchAsync(async (req, res) => { + const result = await plan_service.delete_plan_from_db(req); + manageResponse(res, { + success: true, + statusCode: 200, + message: "plan deleted successfully.", + data: result, + meta: {}, + }); +}); +export const plan_controller = { + get_all_plan, + get_single_plan, + create_plan, + update_plan, + delete_plan, +}; diff --git a/dist/app/modules/plan/plan.route.js b/dist/app/modules/plan/plan.route.js new file mode 100644 index 0000000..860b954 --- /dev/null +++ b/dist/app/modules/plan/plan.route.js @@ -0,0 +1,11 @@ +import { Router } from "express"; +import RequestValidator from "../../middlewares/request_validator.js"; +import { plan_controller } from "./plan.controller.js"; +import { plan_validations } from "./plan.validation.js"; +const router = Router(); +router.get("/", plan_controller.get_all_plan); +router.post("/", RequestValidator(plan_validations.create_plan), plan_controller.create_plan); +router.get("/:id", plan_controller.get_single_plan); +router.patch("/:id", RequestValidator(plan_validations.update_plan), plan_controller.update_plan); +router.delete("/:id", plan_controller.delete_plan); +export default router; diff --git a/dist/app/modules/plan/plan.service.js b/dist/app/modules/plan/plan.service.js new file mode 100644 index 0000000..f363cf2 --- /dev/null +++ b/dist/app/modules/plan/plan.service.js @@ -0,0 +1,68 @@ +import { prisma } from "../../lib/prisma.js"; +import { AppError } from "../../utils/app_error.js"; +const get_all_plan_from_db = async (req) => { + // define your own login here + const result = await prisma.plan.findMany(); + return result; +}; +const get_single_plan_from_db = async (req) => { + // define your own login here + const { id } = req.params; + const result = await prisma.plan.findUnique({ + where: { + id: id + } + }); + return result; +}; +const create_plan_into_db = async (req) => { + // define your own login here + const user = req.user; + if (user?.role !== "ADMIN") { + throw new AppError("You don’t have permission to create plan information.!!!", 401); + } + const result = await prisma.plan.create({ data: req.body }); + return result; +}; +const update_plan_into_db = async (req) => { + // define your own login here + const { id } = req.params; + const user = req.user; + if (user?.role !== "ADMIN") { + throw new AppError("You don’t have permission to update plan information.!!!", 401); + } + const isPlanExist = await prisma.plan.findFirst({ + where: { + id: id + } + }); + if (!isPlanExist) { + throw new AppError("The plan is not available!!!", 404); + } + const result = await prisma.plan.update({ + where: { + id: isPlanExist.id + }, + data: { + ...req.body + } + }); + return result; +}; +const delete_plan_from_db = async (req) => { + // define your own login here + const { id } = req.params; + const user = req.user; + if (user?.role !== "ADMIN") { + throw new AppError("You don’t have permission to delete plan information.!!!", 401); + } + const result = await prisma.plan.delete({ where: { id: id } }); + return result; +}; +export const plan_service = { + get_all_plan_from_db, + get_single_plan_from_db, + create_plan_into_db, + update_plan_into_db, + delete_plan_from_db, +}; diff --git a/dist/app/modules/plan/plan.swagger.js b/dist/app/modules/plan/plan.swagger.js new file mode 100644 index 0000000..eb85a64 --- /dev/null +++ b/dist/app/modules/plan/plan.swagger.js @@ -0,0 +1,125 @@ +export const planSwaggerDocs = { + "/api/plan": { + post: { + tags: ["plan"], + summary: "Create new plan", + description: "", + requestBody: { + required: true, + content: { + "application/json": { + example: JSON.stringify({ + "planName": "PRO Plan", + "price": 12, + "planType": "PRO", + "planDesc": "The plan is only for pro users", + "planFeatures": { + "storage": "10GB", + "projects": 5, + "support": "Email Support" + } + }), // put your request body + }, + }, + }, + responses: { + 201: { description: "plan created successfully" }, + 500: { description: "Validation error or internal server error" }, + }, + }, + get: { + tags: ["plan"], + summary: "Get all plan", + description: "", + parameters: [ + { + name: "page", + in: "query", + required: false, + schema: { type: "number" }, + }, + { + name: "limit", + in: "query", + required: false, + schema: { type: "number" }, + }, + ], + responses: { + 200: { description: "plan fetched successfully" }, + 401: { description: "unauthorized" }, + }, + }, + }, + "/api/plan/{id}": { + get: { + tags: ["plan"], + summary: "Get single plan", + description: "", + parameters: [ + { + name: "id", + in: "path", + required: true, + schema: { type: "string" }, + }, + ], + responses: { + 200: { description: "plan fetched successfully" }, + 401: { description: "unauthorized" }, + }, + }, + patch: { + tags: ["plan"], + summary: "Update plan", + description: "", + parameters: [ + { + name: "id", + in: "path", + required: true, + schema: { type: "string" }, + }, + ], + requestBody: { + required: true, + content: { + "application/json": { + example: JSON.stringify({ + "planName": "PRO Plan", + "price": 12, + "planType": "PRO", + "planDesc": "The plan is only for pro users", + "planFeatures": { + "storage": "10GB", + "projects": 5, + "support": "Email Support" + } + }), // put your request body + }, + }, + }, + responses: { + 200: { description: "plan updated successfully" }, + 500: { description: "Validation error or internal server error" }, + }, + }, + delete: { + tags: ["plan"], + summary: "Delete plan", + description: "", + parameters: [ + { + name: "id", + in: "path", + required: true, + schema: { type: "string" }, + }, + ], + responses: { + 200: { description: "plan delete successfully" }, + 401: { description: "unauthorized" }, + }, + }, + }, +}; diff --git a/dist/app/modules/plan/plan.validation.js b/dist/app/modules/plan/plan.validation.js new file mode 100644 index 0000000..d38d612 --- /dev/null +++ b/dist/app/modules/plan/plan.validation.js @@ -0,0 +1,33 @@ +import { z } from "zod"; +const create_plan = z.object({ + planName: z.string("Enter the plan name..."), + price: z.number("Enter the plan price..."), + planType: z.enum(["FREE", "STANDARD", "PRO"]), + planDesc: z.string("Enter the plan description..."), + planFeatures: z.union([ + z.string(), + z.number(), + z.boolean(), + z.null(), + z.array(z.any()), + z.record(z.string(), z.any()) + ]) +}); +const update_plan = z.object({ + planName: z.string(), + price: z.number(), + planType: z.enum(["FREE", "STANDARD", "PRO"]), + planDesc: z.string().optional(), + planFeatures: z.union([ + z.string(), + z.number(), + z.boolean(), + z.null(), + z.array(z.any()), + z.record(z.string(), z.any()) + ]) +}); +export const plan_validations = { + create_plan, + update_plan, +}; diff --git a/dist/app/modules/profile/profile.controller.js b/dist/app/modules/profile/profile.controller.js new file mode 100644 index 0000000..e6abe89 --- /dev/null +++ b/dist/app/modules/profile/profile.controller.js @@ -0,0 +1,15 @@ +import catchAsync from "../../utils/catch_async.js"; +import manageResponse from "../../utils/manage_response.js"; +import { profile_service } from "./profile.service.js"; +const update_profile = catchAsync(async (req, res) => { + const result = await profile_service.update_profile_into_db(req); + manageResponse(res, { + success: true, + statusCode: 200, + message: "profile updated successfully.", + data: result, + }); +}); +export const profile_controller = { + update_profile, +}; diff --git a/dist/app/modules/profile/profile.route.js b/dist/app/modules/profile/profile.route.js new file mode 100644 index 0000000..5a408a2 --- /dev/null +++ b/dist/app/modules/profile/profile.route.js @@ -0,0 +1,12 @@ +import { Router } from "express"; +import RequestValidator from "../../middlewares/request_validator.js"; +import { profile_controller } from "./profile.controller.js"; +import { profile_validations } from "./profile.validation.js"; +import auth from "../../middlewares/auth.js"; +import uploader from "../../middlewares/uploader.js"; +const router = Router(); +router.patch("/", auth("USER"), uploader.single("file"), (req, res, next) => { + req.body = JSON.parse(req?.body?.data); + next(); +}, RequestValidator(profile_validations.update_profile), profile_controller.update_profile); +export default router; diff --git a/dist/app/modules/profile/profile.service.js b/dist/app/modules/profile/profile.service.js new file mode 100644 index 0000000..17ca7e6 --- /dev/null +++ b/dist/app/modules/profile/profile.service.js @@ -0,0 +1,22 @@ +import uploadCloud from "../../utils/cloudinary.js"; +import { prisma } from "../../lib/prisma.js"; +const update_profile_into_db = async (req) => { + const user = req?.user; + const payload = req?.body; + const file = req?.file; + // check file and upload to cloud + if (file) { + const cloudRes = await uploadCloud(file); + payload.profilePhoto = cloudRes?.secure_url; + } + const result = await prisma.profile.update({ + where: { + accountId: user.accountId, + }, + data: payload, + }); + return result; +}; +export const profile_service = { + update_profile_into_db, +}; diff --git a/dist/app/modules/profile/profile.swagger.js b/dist/app/modules/profile/profile.swagger.js new file mode 100644 index 0000000..bdfe3c2 --- /dev/null +++ b/dist/app/modules/profile/profile.swagger.js @@ -0,0 +1,34 @@ +export const profileSwaggerDocs = { + "/api/profile": { + patch: { + tags: ["profile"], + summary: "Update profile", + requestBody: { + required: true, + content: { + "multipart/form-data": { + schema: { + type: "object", + properties: { + data: { + type: "object", + properties: { + fullName: { type: "string" }, + }, + }, + file: { + type: "string", + format: "binary", + }, + }, + }, + }, + }, + }, + responses: { + 200: { description: "profile updated successfully" }, + 500: { description: "Validation error or internal server error" }, + }, + }, + }, +}; diff --git a/dist/app/modules/profile/profile.validation.js b/dist/app/modules/profile/profile.validation.js new file mode 100644 index 0000000..bb0240a --- /dev/null +++ b/dist/app/modules/profile/profile.validation.js @@ -0,0 +1,7 @@ +import { z } from "zod"; +const update_profile = z.object({ + fullName: z.string().optional(), +}); +export const profile_validations = { + update_profile, +}; diff --git a/dist/app/modules/support/support.controller.js b/dist/app/modules/support/support.controller.js new file mode 100644 index 0000000..deedd95 --- /dev/null +++ b/dist/app/modules/support/support.controller.js @@ -0,0 +1,80 @@ +import catchAsync from "../../utils/catch_async.js"; +import manageResponse from "../../utils/manage_response.js"; +import { support_service } from "./support.service.js"; +const createSupport = catchAsync(async (req, res) => { + const id = req?.user?.accountId; + const data = { + ...req.body, + storeAccountId: id + }; + const result = await support_service.createSupportIntoDB(data); + manageResponse(res, { + success: true, + statusCode: 200, + message: "support created successfully.", + data: result, + meta: {}, + }); +}); +const getAllSupport = catchAsync(async (req, res) => { + const role = req?.user?.role; + const id = req?.user?.accountId; + const search = req?.query?.search; + const type = req?.query?.type; + const status = req?.query?.status; + const result = await support_service.getAllSupportFromDB(id, role, search, type, status); + manageResponse(res, { + success: true, + statusCode: 200, + message: "All support fetched successfully.", + data: result, + meta: {}, + }); +}); +const get_single_support = catchAsync(async (req, res) => { + const { id } = req.params; + const userId = req?.user?.accountId; + const role = req?.user?.role; + const result = await support_service.getSingleSupportFromDB(id, userId, role); + manageResponse(res, { + success: true, + statusCode: 200, + message: "Single support fetched successfully.", + data: result, + meta: {}, + }); +}); +const update_support = catchAsync(async (req, res) => { + const { id } = req.params; + const userId = req?.user?.accountId; + const role = req?.user?.role; + const data = req.body; + const result = await support_service.updateSupportIntoDB(id, userId, role, data); + manageResponse(res, { + success: true, + statusCode: 200, + message: "support updated successfully.", + data: result, + meta: {}, + }); +}); +const delete_support = catchAsync(async (req, res) => { + const { id } = req.params; + const userId = req?.user?.accountId; + const role = req?.user?.role; + const result = await support_service.deleteSupportFromDB(id, userId, role); + manageResponse(res, { + success: true, + statusCode: 200, + message: "support deleted successfully.", + data: result, + meta: {}, + }); +}); +export const support_controller = { + createSupport, + getAllSupport, + get_single_support, + update_support, + delete_support, +}; diff --git a/dist/app/modules/support/support.route.js b/dist/app/modules/support/support.route.js new file mode 100644 index 0000000..6babe9b --- /dev/null +++ b/dist/app/modules/support/support.route.js @@ -0,0 +1,12 @@ +import { Router } from "express"; +import RequestValidator from "../../middlewares/request_validator.js"; +import { support_controller } from "./support.controller.js"; +import { support_validations } from "./support.validation.js"; +import auth from "../../middlewares/auth.js"; +const router = Router(); +router.get("/", auth("ADMIN", "USER"), support_controller.getAllSupport); +router.post("/", auth("ADMIN", "USER"), RequestValidator(support_validations.create_support), support_controller.createSupport); +router.get("/:id", auth("ADMIN", "USER"), support_controller.get_single_support); +router.patch("/:id", auth("ADMIN", "USER"), RequestValidator(support_validations.update_support), support_controller.update_support); +router.delete("/:id", auth("ADMIN", "USER"), support_controller.delete_support); +export default router; diff --git a/dist/app/modules/support/support.service.js b/dist/app/modules/support/support.service.js new file mode 100644 index 0000000..0825aaa --- /dev/null +++ b/dist/app/modules/support/support.service.js @@ -0,0 +1,100 @@ +import { prisma } from "../../lib/prisma.js"; +import { AppError } from "../../utils/app_error.js"; +const createSupportIntoDB = async (payload) => { + const result = await prisma.support.create({ data: payload }); + return result; +}; +const getAllSupportFromDB = async (user_id, role, search, type, status) => { + const andCondition = []; + if (search) { + andCondition.push({ + OR: [ + { + issueName: { + contains: search, + mode: "insensitive", + }, + }, + { + description: { + contains: search, + mode: "insensitive", + }, + }, + ], + }); + } + if (type) { + andCondition.push({ + type: type, + }); + } + if (status) { + andCondition.push({ + status: status, + }); + } + if (role !== "ADMIN") { + andCondition.push({ + storeAccountId: user_id, + }); + } + const whereCondition = andCondition.length > 0 ? { AND: andCondition } : {}; + const result = await prisma.support.findMany({ + where: whereCondition, + orderBy: { + createdAt: "desc", + }, + }); + return result; +}; +const getSingleSupportFromDB = async (id, userId, role) => { + const support = await prisma.support.findUnique({ + where: { id }, + }); + if (!support) { + throw new AppError("Support not found", 404); + } + if (role !== "ADMIN" && support.storeAccountId !== userId) { + throw new AppError("You are not authorized", 403); + } + return support; +}; +const updateSupportIntoDB = async (id, userId, role, payload) => { + const support = await prisma.support.findUnique({ + where: { id }, + }); + if (!support) { + throw new AppError("Support not found", 404); + } + if (role !== "ADMIN" && support.storeAccountId !== userId) { + throw new AppError("You are not authorized", 403); + } + const result = await prisma.support.update({ + where: { id }, + data: payload, + }); + return result; +}; +const deleteSupportFromDB = async (id, userId, role) => { + const support = await prisma.support.findUnique({ + where: { id }, + }); + if (!support) { + throw new AppError("Support not found", 404); + } + if (role !== "ADMIN" && support.storeAccountId !== userId) { + throw new AppError("You are not authorized", 403); + } + const result = await prisma.support.delete({ + where: { id } + }); + return result; +}; +export const support_service = { + createSupportIntoDB, + getAllSupportFromDB, + getSingleSupportFromDB, + updateSupportIntoDB, + deleteSupportFromDB, +}; diff --git a/dist/app/modules/support/support.swagger.js b/dist/app/modules/support/support.swagger.js new file mode 100644 index 0000000..cd29843 --- /dev/null +++ b/dist/app/modules/support/support.swagger.js @@ -0,0 +1,109 @@ +export const supportSwaggerDocs = { + "/api/support": { + post: { + tags: ["support"], + summary: "Create new support", + description: "", + requestBody: { + required: true, + content: { + "application/json": { + example: JSON.stringify({ + "issueName": "Your issue name", + "description": "Issue description", + "type": "Issue Type" + }), // put your request body + }, + }, + }, + responses: { + 201: { description: "support created successfully" }, + 500: { description: "Validation error or internal server error" }, + }, + }, + get: { + tags: ["support"], + summary: "Get all support", + description: "", + parameters: [ + { + name: "page", + in: "query", + required: false, + schema: { type: "number" }, + }, + { + name: "limit", + in: "query", + required: false, + schema: { type: "number" }, + }, + ], + responses: { + 200: { description: "support fetched successfully" }, + 401: { description: "unauthorized" }, + }, + }, + }, + "/api/support/{id}": { + get: { + tags: ["support"], + summary: "Get single support", + description: "", + parameters: [ + { + name: "id", + in: "path", + required: true, + schema: { type: "string" }, + }, + ], + responses: { + 200: { description: "support fetched successfully" }, + 401: { description: "unauthorized" }, + }, + }, + patch: { + tags: ["support"], + summary: "Update support", + description: "", + parameters: [ + { + name: "id", + in: "path", + required: true, + schema: { type: "string" }, + }, + ], + requestBody: { + required: true, + content: { + "application/json": { + example: JSON.stringify({}), // put your request body + }, + }, + }, + responses: { + 200: { description: "support updated successfully" }, + 500: { description: "Validation error or internal server error" }, + }, + }, + delete: { + tags: ["support"], + summary: "Delete support", + description: "", + parameters: [ + { + name: "id", + in: "path", + required: true, + schema: { type: "string" }, + }, + ], + responses: { + 200: { description: "support delete successfully" }, + 401: { description: "unauthorized" }, + }, + }, + }, +}; diff --git a/dist/app/modules/support/support.validation.js b/dist/app/modules/support/support.validation.js new file mode 100644 index 0000000..4ccbd97 --- /dev/null +++ b/dist/app/modules/support/support.validation.js @@ -0,0 +1,28 @@ +import { z } from "zod"; +const create_support = z.object({ + issueName: z.string().min(1, "issueName is required"), + description: z.string().min(1, "description is required"), + type: z.enum([ + "TECHNICAL", + "BILLING", + "DOMAIN", + "TEMPLATE", + "PAYMENT", + "ACCOUNT", + "FEATURE_REQUEST", + "BUG", + "OTHER", + ]), +}); +const update_support = z.object({ + issueName: z.string().optional(), + description: z.string().optional(), + type: z.enum(["BUG", "PAYMENT", "ACCOUNT", "OTHER"]).optional(), + status: z.enum(["OPEN", "IN_PROGRESS", "RESOLVED", "REJECTED"]).optional(), + resolvedBy: z.string().optional(), + resolvedAt: z.coerce.date().optional(), +}); +export const support_validations = { + create_support, + update_support, +}; diff --git a/dist/app/queues/connection.js b/dist/app/queues/connection.js new file mode 100644 index 0000000..d7a4fc7 --- /dev/null +++ b/dist/app/queues/connection.js @@ -0,0 +1,4 @@ +export const redisConnection = { + host: "127.0.0.1", + port: 6379, +}; diff --git a/dist/app/queues/email/email.processor.js b/dist/app/queues/email/email.processor.js new file mode 100644 index 0000000..e7ddd44 --- /dev/null +++ b/dist/app/queues/email/email.processor.js @@ -0,0 +1,14 @@ +import { otpTemplate } from "../../templates/otpTemplate.js"; +import sendMail from "../../utils/mail_sender.js"; +// email.processor.ts +export const emailProcessor = async (job) => { + const payload = job.data; + await sendMail({ + to: payload.email, + subject: payload.subject, + htmlBody: otpTemplate(payload), + textBody: payload.textBody || "", + name: payload.name, + }); + console.log("Sending email job complete:", job.id); +}; diff --git a/dist/app/queues/email/email.queue.js b/dist/app/queues/email/email.queue.js new file mode 100644 index 0000000..afb2b7c --- /dev/null +++ b/dist/app/queues/email/email.queue.js @@ -0,0 +1,6 @@ +// email.queue.ts +import { Queue } from "bullmq"; +import { redisConnection } from "../connection.js"; +export const emailQueue = new Queue("email-queue", { + connection: redisConnection, +}); diff --git a/dist/app/queues/email/email.worker.js b/dist/app/queues/email/email.worker.js new file mode 100644 index 0000000..2130b4d --- /dev/null +++ b/dist/app/queues/email/email.worker.js @@ -0,0 +1,7 @@ +// email.worker.ts +import { Worker } from "bullmq"; +import { redisConnection } from "../connection.js"; +import { emailProcessor } from "./email.processor.js"; +export const emailWorker = new Worker("email-queue", async (job) => emailProcessor(job), { + connection: redisConnection, +}); diff --git a/dist/app/queues/email/order/order.email.processor.js b/dist/app/queues/email/order/order.email.processor.js new file mode 100644 index 0000000..8b0834f --- /dev/null +++ b/dist/app/queues/email/order/order.email.processor.js @@ -0,0 +1,12 @@ +import sendMail from "../../../utils/mail_sender.js"; +// email.processor.ts +export const orderEmailProcessor = async (job) => { + const payload = job.data; + await sendMail({ + to: payload.email, + subject: payload.subject, + htmlBody: payload.htmlBody, + textBody: payload.textBody, + }); + console.log("Sending email job complete:", job.id); +}; diff --git a/dist/app/queues/email/order/order.email.queue.js b/dist/app/queues/email/order/order.email.queue.js new file mode 100644 index 0000000..5e8bd36 --- /dev/null +++ b/dist/app/queues/email/order/order.email.queue.js @@ -0,0 +1,5 @@ +import { Queue } from "bullmq"; +import { redisConnection } from "../../connection.js"; +export const orderEmailQueue = new Queue("order-email-queue", { + connection: redisConnection, +}); diff --git a/dist/app/queues/email/order/order.email.worker.js b/dist/app/queues/email/order/order.email.worker.js new file mode 100644 index 0000000..4dbd0a1 --- /dev/null +++ b/dist/app/queues/email/order/order.email.worker.js @@ -0,0 +1,7 @@ +// email.worker.ts +import { Worker } from "bullmq"; +import { redisConnection } from "../../connection.js"; +import { orderEmailProcessor } from "./order.email.processor.js"; +export const emailWorker = new Worker("order-email-queue", async (job) => orderEmailProcessor(job), { + connection: redisConnection, +}); diff --git a/dist/app/queues/worker.js b/dist/app/queues/worker.js new file mode 100644 index 0000000..5801c24 --- /dev/null +++ b/dist/app/queues/worker.js @@ -0,0 +1,3 @@ +import "./email/email.worker.js"; +import "./email/order/order.email.worker.js"; +console.log("Workers running..."); diff --git a/dist/app/templates/otpTemplate.js b/dist/app/templates/otpTemplate.js new file mode 100644 index 0000000..e6902d6 --- /dev/null +++ b/dist/app/templates/otpTemplate.js @@ -0,0 +1,80 @@ +export const otpTemplate = (payload) => { + return ` +
+ + + + +
+

+ Use the following One-Time Password (OTP) to complete your + verification: +

+ + +
+ + ${payload.otp} + +
+ +

+ This OTP will expire in 5 minutes. +

+ + +
+ + +

+ Or verify using this link: +

+ +

+ + ${payload.verificationLink} + +

+
+
+ `; +}; diff --git a/dist/app/utils/JWT.js b/dist/app/utils/JWT.js new file mode 100644 index 0000000..28765e5 --- /dev/null +++ b/dist/app/utils/JWT.js @@ -0,0 +1,15 @@ +import jwt from "jsonwebtoken"; +const generateToken = (payload, secret, expiresIn) => { + const token = jwt.sign(payload, secret, { + algorithm: "HS256", + expiresIn: expiresIn, + }); + return token; +}; +const verifyToken = (token, secret) => { + return jwt.verify(token, secret); +}; +export const jwtHelpers = { + generateToken, + verifyToken, +}; diff --git a/dist/app/utils/app_error.js b/dist/app/utils/app_error.js new file mode 100644 index 0000000..fd82542 --- /dev/null +++ b/dist/app/utils/app_error.js @@ -0,0 +1,13 @@ +export class AppError extends Error { + statusCode; + constructor(message, statusCode, stack = '') { + super(message); + this.statusCode = statusCode; + if (stack) { + this.stack = stack; + } + else { + Error.captureStackTrace(this, this.constructor); + } + } +} diff --git a/dist/app/utils/catch_async.js b/dist/app/utils/catch_async.js new file mode 100644 index 0000000..15940c5 --- /dev/null +++ b/dist/app/utils/catch_async.js @@ -0,0 +1,11 @@ +const catchAsync = (fn) => { + return async (req, res, next) => { + try { + await fn(req, res, next); + } + catch (error) { + next(error); + } + }; +}; +export default catchAsync; diff --git a/dist/app/utils/cloudinary.js b/dist/app/utils/cloudinary.js new file mode 100644 index 0000000..62ec6e1 --- /dev/null +++ b/dist/app/utils/cloudinary.js @@ -0,0 +1,23 @@ +import { v2 as cloudinary } from 'cloudinary'; +import fs from 'fs'; +import { configs } from '../configs/index.js'; +// Configuration +cloudinary.config({ + cloud_name: configs.cloudinary.cloud_name, + api_key: configs.cloudinary.cloud_api_key, + api_secret: configs.cloudinary.cloud_api_secret, +}); +const uploadCloud = async (file) => { + return new Promise((resolve, reject) => { + cloudinary.uploader.upload(file.path, (error, result) => { + fs.unlinkSync(file.path); + if (error) { + reject(error); + } + else { + resolve(result); + } + }); + }); +}; +export default uploadCloud; diff --git a/dist/app/utils/mail_sender.js b/dist/app/utils/mail_sender.js new file mode 100644 index 0000000..c8b321f --- /dev/null +++ b/dist/app/utils/mail_sender.js @@ -0,0 +1,113 @@ +import nodemailer from 'nodemailer'; +import { configs } from '../configs/index.js'; +const transporter = nodemailer.createTransport({ + host: "smtp.gmail.com", + port: 465, + secure: true, // true for 465, false for other ports + auth: { + user: configs.email.app_email, + pass: configs.email.app_password, + }, +}); +// ✅ Email Sender Function +const sendMail = async (payload) => { + const info = await transporter.sendMail({ + from: 'info@digitalcreditai.com', + to: payload.to, + subject: payload.subject, + text: payload.textBody, + html: ` + + + + + Welcome Email + + + + + +
+
+

+ Hi ${payload?.name || ""}, +

+ + ${payload?.htmlBody} + +
+ Quick Launch + +

The Support Team

+

Quick Launch

+
+
+

+ This is an automated message — please do not reply to this email. +
+ If you need assistance, feel free to contact our support team. +

+ Thank you for choosing us! +

+ +
+
+ © 2026 to {{year}} Quick Launch. All rights reserved. +
+
+ + + + + `, + }); + return info; +}; +export default sendMail; diff --git a/dist/app/utils/manage_response.js b/dist/app/utils/manage_response.js new file mode 100644 index 0000000..d33865a --- /dev/null +++ b/dist/app/utils/manage_response.js @@ -0,0 +1,9 @@ +const manageResponse = (res, payload) => { + res.status(payload.statusCode).json({ + success: payload.success, + message: payload.message, + data: payload.data || undefined || null, + meta: payload.meta || undefined || null + }); +}; +export default manageResponse; diff --git a/dist/app/utils/otpGenerator.js b/dist/app/utils/otpGenerator.js new file mode 100644 index 0000000..842096d --- /dev/null +++ b/dist/app/utils/otpGenerator.js @@ -0,0 +1,23 @@ +export const otpGenerator = () => { + const upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + const lower = "abcdefghijklmnopqrstuvwxyz"; + const numbers = "0123456789"; + const symbols = "@#$%&*!?"; + const allChars = upper + lower + numbers + symbols; + let otp = ""; + // Ensure at least one character from each set + otp += upper[Math.floor(Math.random() * upper.length)]; + otp += lower[Math.floor(Math.random() * lower.length)]; + otp += numbers[Math.floor(Math.random() * numbers.length)]; + otp += symbols[Math.floor(Math.random() * symbols.length)]; + // Fill the rest randomly + for (let i = otp.length; i < 6; i++) { + otp += allChars[Math.floor(Math.random() * allChars.length)]; + } + // Shuffle to remove predictable order + otp = otp + .split("") + .sort(() => Math.random() - 0.5) + .join(""); + return otp; +}; diff --git a/dist/app/utils/pagination_helper.js b/dist/app/utils/pagination_helper.js new file mode 100644 index 0000000..63a8a8f --- /dev/null +++ b/dist/app/utils/pagination_helper.js @@ -0,0 +1,15 @@ +const paginationHelper = (options) => { + const page = Number(options?.page) || 1; + const limit = Number(options?.limit) || 10; + const skip = (page - 1) * limit; + const sortBy = options?.sortBy || "createdAt"; + const sortOrder = options?.sortOrder || "desc"; + return { + page, + limit, + skip, + sortBy, + sortOrder + }; +}; +export default paginationHelper; diff --git a/dist/routes.js b/dist/routes.js new file mode 100644 index 0000000..5185c65 --- /dev/null +++ b/dist/routes.js @@ -0,0 +1,16 @@ +import { Router } from "express"; +import accountRouter from "./app/modules/account/account.route.js"; +import orderRoute from "./app/modules/order/order.route.js"; +import planRoute from "./app/modules/plan/plan.route.js"; +import profileRoute from "./app/modules/profile/profile.route.js"; +import supportRoute from "./app/modules/support/support.route.js"; +const appRouter = Router(); +const moduleRoutes = [ + { path: "/order", route: orderRoute }, + { path: "/support", route: supportRoute }, + { path: "/plan", route: planRoute }, + { path: "/profile", route: profileRoute }, + { path: "/auth", route: accountRouter }, +]; +moduleRoutes.forEach((route) => appRouter.use(route.path, route.route)); +export default appRouter; diff --git a/dist/server.js b/dist/server.js new file mode 100644 index 0000000..f1110e5 --- /dev/null +++ b/dist/server.js @@ -0,0 +1,19 @@ +import app from "./app.js"; +import { configs } from "./app/configs/index.js"; +import { prisma } from "./app/lib/prisma.js"; +import "./app/queues/worker.js"; +async function main() { + try { + app.listen(configs.port, async () => { + await prisma.$connect(); + console.log(`Server is running on port ${configs.port}`); + }); + } + catch (error) { + console.error("Error starting server:", error); + } +} +main().catch((error) => { + console.error("Error in main function:", error); + process.exit(1); +}); diff --git a/dist/swaggerOptions.js b/dist/swaggerOptions.js new file mode 100644 index 0000000..164612c --- /dev/null +++ b/dist/swaggerOptions.js @@ -0,0 +1,48 @@ +import { fileURLToPath } from "node:url"; +import path from "path"; +import { configs } from "./app/configs/index.js"; +import { accountSwaggerDocs } from "./app/modules/account/account.swagger.js"; +import { orderSwaggerDocs } from "./app/modules/order/order.swagger.js"; +import { planSwaggerDocs } from "./app/modules/plan/plan.swagger.js"; +import { profileSwaggerDocs } from "./app/modules/profile/profile.swagger.js"; +import { supportSwaggerDocs } from "./app/modules/support/support.swagger.js"; +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +export const swaggerOptions = { + definition: { + openapi: "3.0.0", + info: { + title: "API Doc - Build with exp-node-server", + version: "1.0.0", + description: "Express + Prisma API with auto-generated Swagger docs", + }, + paths: { + ...accountSwaggerDocs, + ...planSwaggerDocs, + ...orderSwaggerDocs, + ...profileSwaggerDocs, + ...supportSwaggerDocs, + }, + servers: configs.env === "production" + ? [{ url: "https://live-url.com" }, { url: "http://localhost:5000" }] + : [{ url: "http://localhost:5000" }, { url: "https://live-url.com" }], + components: { + securitySchemes: { + AuthorizationToken: { + type: "apiKey", + in: "header", + name: "Authorization", + description: "Put your accessToken here ", + }, + }, + }, + security: [ + { + AuthorizationToken: [], + }, + ], + }, + apis: [ + path.join(__dirname, configs.env === "production" ? "./**/*.js" : "./**/*.ts"), + ], +}; diff --git a/package.json b/package.json index 8d527f7..6326e8f 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "pg": "^8.20.0", "swagger-jsdoc": "^6.2.8", "swagger-ui-express": "^5.0.1", + "typescript": "^6.0.3", "zod": "^4.1.12" }, "devDependencies": { diff --git a/prisma/schema/schema.prisma b/prisma/schema/schema.prisma index 2500a9d..6eb2dfc 100644 --- a/prisma/schema/schema.prisma +++ b/prisma/schema/schema.prisma @@ -1,6 +1,5 @@ generator client { - provider = "prisma-client" - output = "../generated/prisma" + provider = "prisma-client-js" } datasource db { diff --git a/src/app.ts b/src/app.ts index 0dbfa9f..7e60569 100644 --- a/src/app.ts +++ b/src/app.ts @@ -3,10 +3,10 @@ import cors from 'cors'; import express, { Request, Response } from 'express'; import swaggerJSDoc from 'swagger-jsdoc'; import swaggerUi from "swagger-ui-express"; -import globalErrorHandler from './app/middlewares/global_error_handler'; -import notFound from './app/middlewares/not_found_api'; -import appRouter from './routes'; -import { swaggerOptions } from './swaggerOptions'; +import globalErrorHandler from './app/middlewares/global_error_handler.js'; +import notFound from './app/middlewares/not_found_api.js'; +import appRouter from './routes.js'; +import { swaggerOptions } from './swaggerOptions.js'; // define app const app = express() diff --git a/src/app/configs/index.ts b/src/app/configs/index.ts index 54e0554..1553fd2 100644 --- a/src/app/configs/index.ts +++ b/src/app/configs/index.ts @@ -23,4 +23,5 @@ export const configs = { cloud_api_key: process.env.CLOUD_API_KEY, cloud_api_secret: process.env.CLOUD_API_SECRET, }, + redis_url: process.env.REDIS_URL, }; diff --git a/src/app/errors/zodError.ts b/src/app/errors/zodError.ts index bc221a3..f97f1d7 100644 --- a/src/app/errors/zodError.ts +++ b/src/app/errors/zodError.ts @@ -1,5 +1,5 @@ import { ZodError, ZodIssue } from 'zod' -import { TErrorSources, TGenericErrorResponse } from '../types/error' +import { TErrorSources, TGenericErrorResponse } from '../types/error.js' const handleZodError = (err: ZodError): TGenericErrorResponse => { const errorSources: TErrorSources = err.issues.map((issue: ZodIssue) => { diff --git a/src/app/lib/prisma.ts b/src/app/lib/prisma.ts index ab78aa6..8a9d118 100644 --- a/src/app/lib/prisma.ts +++ b/src/app/lib/prisma.ts @@ -1,6 +1,7 @@ import { PrismaPg } from "@prisma/adapter-pg"; +import pkg from "@prisma/client"; import "dotenv/config"; -import { PrismaClient } from "../../../prisma/generated/prisma/client"; +const { PrismaClient } = pkg; const connectionString = `${process.env.DATABASE_URL}`; diff --git a/src/app/middlewares/auth.ts b/src/app/middlewares/auth.ts index 5723cf3..5a39143 100644 --- a/src/app/middlewares/auth.ts +++ b/src/app/middlewares/auth.ts @@ -1,7 +1,7 @@ import { NextFunction, Request, Response } from "express"; -import { configs } from "../configs"; -import { AppError } from "../utils/app_error"; -import { jwtHelpers, JwtPayloadType } from "../utils/JWT"; +import { configs } from "../configs/index.js"; +import { AppError } from "../utils/app_error.js"; +import { jwtHelpers, JwtPayloadType } from "../utils/JWT.js"; type Role = "ADMIN" | "USER"; diff --git a/src/app/middlewares/global_error_handler.ts b/src/app/middlewares/global_error_handler.ts index c361f7f..511506e 100644 --- a/src/app/middlewares/global_error_handler.ts +++ b/src/app/middlewares/global_error_handler.ts @@ -1,9 +1,9 @@ import { ErrorRequestHandler } from "express"; import { ZodError } from "zod"; -import { configs } from "../configs"; -import handleZodError from "../errors/zodError"; -import { TErrorSources } from "../types/error"; -import { AppError } from "../utils/app_error"; +import { configs } from "../configs/index.js"; +import handleZodError from "../errors/zodError.js"; +import { TErrorSources } from "../types/error.js"; +import { AppError } from "../utils/app_error.js"; const globalErrorHandler: ErrorRequestHandler = (err, req, res, next) => { let statusCode = 500; diff --git a/src/app/modules/account/account.controller.ts b/src/app/modules/account/account.controller.ts index f0a467d..114f159 100644 --- a/src/app/modules/account/account.controller.ts +++ b/src/app/modules/account/account.controller.ts @@ -1,7 +1,7 @@ -import { configs } from "../../configs"; -import catchAsync from "../../utils/catch_async"; -import manageResponse from "../../utils/manage_response"; -import { account_services } from "./account.service"; +import { configs } from "../../configs/index.js"; +import catchAsync from "../../utils/catch_async.js"; +import manageResponse from "../../utils/manage_response.js"; +import { account_services } from "./account.service.js"; const create_account = catchAsync(async (req, res) => { const result = await account_services.create_account_into_db(req); diff --git a/src/app/modules/account/account.route.ts b/src/app/modules/account/account.route.ts index a8300d8..3f171b2 100644 --- a/src/app/modules/account/account.route.ts +++ b/src/app/modules/account/account.route.ts @@ -1,8 +1,8 @@ import { Router } from "express"; -import auth from "../../middlewares/auth"; -import RequestValidator from "../../middlewares/request_validator"; -import { account_controller } from "./account.controller"; -import { account_validation } from "./account.validation"; +import auth from "../../middlewares/auth.js"; +import RequestValidator from "../../middlewares/request_validator.js"; +import { account_controller } from "./account.controller.js"; +import { account_validation } from "./account.validation.js"; const accountRouter = Router(); diff --git a/src/app/modules/account/account.service.ts b/src/app/modules/account/account.service.ts index 33868fa..1ce71a7 100644 --- a/src/app/modules/account/account.service.ts +++ b/src/app/modules/account/account.service.ts @@ -1,12 +1,12 @@ import bcrypt from "bcrypt"; import { Request } from "express"; -import { configs } from "../../configs"; -import { prisma } from "../../lib/prisma"; -import { emailQueue } from "../../queues/email/email.queue"; -import { AppError } from "../../utils/app_error"; -import { jwtHelpers } from "../../utils/JWT"; -import sendMail from "../../utils/mail_sender"; -import { otpGenerator } from "../../utils/otpGenerator"; +import { configs } from "../../configs/index.js"; +import { prisma } from "../../lib/prisma.js"; +import { emailQueue } from "../../queues/email/email.queue.js"; +import { AppError } from "../../utils/app_error.js"; +import { jwtHelpers } from "../../utils/JWT.js"; +import sendMail from "../../utils/mail_sender.js"; +import { otpGenerator } from "../../utils/otpGenerator.js"; const create_account_into_db = async (req: Request) => { const payload = req?.body; @@ -22,7 +22,7 @@ const create_account_into_db = async (req: Request) => { const hashPassword = bcrypt.hashSync(payload.password, 10); // create account and profile - const result = await prisma.$transaction(async (tx) => { + const result = await prisma.$transaction(async (tx:any) => { const account = await tx.account.create({ data: { email: payload.email, diff --git a/src/app/modules/order/order.controller.ts b/src/app/modules/order/order.controller.ts index 01d2222..e0b3a6c 100644 --- a/src/app/modules/order/order.controller.ts +++ b/src/app/modules/order/order.controller.ts @@ -1,7 +1,7 @@ -import catchAsync from "../../utils/catch_async"; -import manageResponse from "../../utils/manage_response"; -import { order_service } from "./order.service"; +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); diff --git a/src/app/modules/order/order.route.ts b/src/app/modules/order/order.route.ts index d002096..a6ae228 100644 --- a/src/app/modules/order/order.route.ts +++ b/src/app/modules/order/order.route.ts @@ -1,9 +1,9 @@ import { Router } from "express"; -import RequestValidator from "../../middlewares/request_validator"; -import { order_controller } from "./order.controller"; -import { order_validations } from "./order.validation"; -import auth from "../../middlewares/auth"; +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(); diff --git a/src/app/modules/order/order.service.ts b/src/app/modules/order/order.service.ts index 0b492fe..4a14a5b 100644 --- a/src/app/modules/order/order.service.ts +++ b/src/app/modules/order/order.service.ts @@ -1,9 +1,9 @@ import { Request } from "express"; -import { configs } from "../../configs"; -import { prisma } from "../../lib/prisma"; -import { orderEmailQueue } from "../../queues/email/order/order.email.queue"; -import { AppError } from "../../utils/app_error"; -import paginationHelper from "../../utils/pagination_helper"; +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: Request) => { // define your own login here diff --git a/src/app/modules/plan/plan.controller.ts b/src/app/modules/plan/plan.controller.ts index 06f8efd..170c849 100644 --- a/src/app/modules/plan/plan.controller.ts +++ b/src/app/modules/plan/plan.controller.ts @@ -1,7 +1,7 @@ -import catchAsync from "../../utils/catch_async"; -import manageResponse from "../../utils/manage_response"; -import { plan_service } from "./plan.service"; +import catchAsync from "../../utils/catch_async.js"; +import manageResponse from "../../utils/manage_response.js"; +import { plan_service } from "./plan.service.js"; const get_all_plan = catchAsync(async (req, res) => { const result = await plan_service.get_all_plan_from_db(req); diff --git a/src/app/modules/plan/plan.route.ts b/src/app/modules/plan/plan.route.ts index b05a285..2aae558 100644 --- a/src/app/modules/plan/plan.route.ts +++ b/src/app/modules/plan/plan.route.ts @@ -1,8 +1,8 @@ import { Router } from "express"; -import RequestValidator from "../../middlewares/request_validator"; -import { plan_controller } from "./plan.controller"; -import { plan_validations } from "./plan.validation"; +import RequestValidator from "../../middlewares/request_validator.js"; +import { plan_controller } from "./plan.controller.js"; +import { plan_validations } from "./plan.validation.js"; const router = Router(); diff --git a/src/app/modules/plan/plan.service.ts b/src/app/modules/plan/plan.service.ts index ec420ed..a3ca07d 100644 --- a/src/app/modules/plan/plan.service.ts +++ b/src/app/modules/plan/plan.service.ts @@ -1,7 +1,7 @@ import { Request } from "express"; -import { prisma } from "../../lib/prisma"; -import { AppError } from "../../utils/app_error"; +import { prisma } from "../../lib/prisma.js"; +import { AppError } from "../../utils/app_error.js"; const get_all_plan_from_db = async (req: Request) => { // define your own login here diff --git a/src/app/modules/profile/profile.controller.ts b/src/app/modules/profile/profile.controller.ts index 65a0b55..5af424e 100644 --- a/src/app/modules/profile/profile.controller.ts +++ b/src/app/modules/profile/profile.controller.ts @@ -1,6 +1,6 @@ -import catchAsync from "../../utils/catch_async"; -import manageResponse from "../../utils/manage_response"; -import { profile_service } from "./profile.service"; +import catchAsync from "../../utils/catch_async.js"; +import manageResponse from "../../utils/manage_response.js"; +import { profile_service } from "./profile.service.js"; const update_profile = catchAsync(async (req, res) => { const result = await profile_service.update_profile_into_db(req); diff --git a/src/app/modules/profile/profile.route.ts b/src/app/modules/profile/profile.route.ts index 7095e3b..3a67d24 100644 --- a/src/app/modules/profile/profile.route.ts +++ b/src/app/modules/profile/profile.route.ts @@ -1,9 +1,9 @@ import { Router } from "express"; -import RequestValidator from "../../middlewares/request_validator"; -import { profile_controller } from "./profile.controller"; -import { profile_validations } from "./profile.validation"; -import auth from "../../middlewares/auth"; -import uploader from "../../middlewares/uploader"; +import RequestValidator from "../../middlewares/request_validator.js"; +import { profile_controller } from "./profile.controller.js"; +import { profile_validations } from "./profile.validation.js"; +import auth from "../../middlewares/auth.js"; +import uploader from "../../middlewares/uploader.js"; const router = Router(); diff --git a/src/app/modules/profile/profile.service.ts b/src/app/modules/profile/profile.service.ts index 06d8a84..b93e02d 100644 --- a/src/app/modules/profile/profile.service.ts +++ b/src/app/modules/profile/profile.service.ts @@ -1,7 +1,7 @@ import { Request } from "express"; -import uploadCloud from "../../utils/cloudinary"; -import { prisma } from "../../lib/prisma"; -import { JwtPayloadType } from "../../utils/JWT"; +import uploadCloud from "../../utils/cloudinary.js"; +import { prisma } from "../../lib/prisma.js"; +import { JwtPayloadType } from "../../utils/JWT.js"; const update_profile_into_db = async (req: Request) => { const user = req?.user as JwtPayloadType; diff --git a/src/app/modules/support/support.controller.ts b/src/app/modules/support/support.controller.ts index 25faf3f..7c4e8e5 100644 --- a/src/app/modules/support/support.controller.ts +++ b/src/app/modules/support/support.controller.ts @@ -1,8 +1,8 @@ import { Request, Response } from "express"; -import catchAsync from "../../utils/catch_async"; -import manageResponse from "../../utils/manage_response"; -import { support_service } from "./support.service"; +import catchAsync from "../../utils/catch_async.js"; +import manageResponse from "../../utils/manage_response.js"; +import { support_service } from "./support.service.js"; diff --git a/src/app/modules/support/support.route.ts b/src/app/modules/support/support.route.ts index bf447e1..db5c0ea 100644 --- a/src/app/modules/support/support.route.ts +++ b/src/app/modules/support/support.route.ts @@ -1,9 +1,9 @@ import { Router } from "express"; -import RequestValidator from "../../middlewares/request_validator"; -import { support_controller } from "./support.controller"; -import { support_validations } from "./support.validation"; -import auth from "../../middlewares/auth"; +import RequestValidator from "../../middlewares/request_validator.js"; +import { support_controller } from "./support.controller.js"; +import { support_validations } from "./support.validation.js"; +import auth from "../../middlewares/auth.js"; const router = Router(); diff --git a/src/app/modules/support/support.service.ts b/src/app/modules/support/support.service.ts index c970fbe..43e2e0b 100644 --- a/src/app/modules/support/support.service.ts +++ b/src/app/modules/support/support.service.ts @@ -1,6 +1,6 @@ -import { prisma } from "../../lib/prisma"; -import { Prisma } from "../../../../prisma/generated/prisma/client"; -import { AppError } from "../../utils/app_error"; +import { prisma } from "../../lib/prisma.js"; +import { Prisma } from "@prisma/client"; +import { AppError } from "../../utils/app_error.js"; const createSupportIntoDB = async (payload: any) => { const result = await prisma.support.create({ data: payload }); diff --git a/src/app/queues/connection.ts b/src/app/queues/connection.ts index dd536c2..22cab1d 100644 --- a/src/app/queues/connection.ts +++ b/src/app/queues/connection.ts @@ -1,6 +1,7 @@ -import { QueueOptions } from "bullmq"; +import { Redis } from "ioredis"; +import { configs } from "../configs/index.js"; -export const redisConnection: QueueOptions["connection"] = { - host: "127.0.0.1", - port: 6379, -}; \ No newline at end of file +export const redisConnection = new Redis(configs.redis_url as string, { + tls: {}, + maxRetriesPerRequest: null, +}); \ No newline at end of file diff --git a/src/app/queues/email/email.processor.ts b/src/app/queues/email/email.processor.ts index 496f4db..0097270 100644 --- a/src/app/queues/email/email.processor.ts +++ b/src/app/queues/email/email.processor.ts @@ -1,6 +1,6 @@ -import { otpTemplate } from "../../templates/otpTemplate"; -import sendMail from "../../utils/mail_sender"; -import { TEmailQueue } from "./email.queue"; +import { otpTemplate } from "../../templates/otpTemplate.js"; +import sendMail from "../../utils/mail_sender.js"; +import { TEmailQueue } from "./email.queue.js"; // email.processor.ts export const emailProcessor = async (job: any) => { diff --git a/src/app/queues/email/email.queue.ts b/src/app/queues/email/email.queue.ts index 31c25f4..7fe1cc3 100644 --- a/src/app/queues/email/email.queue.ts +++ b/src/app/queues/email/email.queue.ts @@ -1,6 +1,6 @@ // email.queue.ts import { Queue } from "bullmq"; -import { redisConnection } from "../connection"; +import { redisConnection } from "../connection.js"; export type TEmailQueue = { email: string; diff --git a/src/app/queues/email/email.worker.ts b/src/app/queues/email/email.worker.ts index 89a66e2..62e4e1d 100644 --- a/src/app/queues/email/email.worker.ts +++ b/src/app/queues/email/email.worker.ts @@ -1,8 +1,8 @@ // email.worker.ts import { Worker } from "bullmq"; -import { redisConnection } from "../connection"; -import { emailProcessor } from "./email.processor"; -import { TEmailQueue } from "./email.queue"; +import { redisConnection } from "../connection.js"; +import { emailProcessor } from "./email.processor.js"; +import { TEmailQueue } from "./email.queue.js"; export const emailWorker = new Worker( "email-queue", diff --git a/src/app/queues/email/order/order.email.processor.ts b/src/app/queues/email/order/order.email.processor.ts index 1a81c35..4d5b74b 100644 --- a/src/app/queues/email/order/order.email.processor.ts +++ b/src/app/queues/email/order/order.email.processor.ts @@ -1,5 +1,5 @@ -import sendMail from "../../../utils/mail_sender"; -import { TOrderEmailQueue } from "./order.email.queue"; +import sendMail from "../../../utils/mail_sender.js"; +import { TOrderEmailQueue } from "./order.email.queue.js"; // email.processor.ts export const orderEmailProcessor = async (job: any) => { diff --git a/src/app/queues/email/order/order.email.queue.ts b/src/app/queues/email/order/order.email.queue.ts index db7d087..ea2cfcb 100644 --- a/src/app/queues/email/order/order.email.queue.ts +++ b/src/app/queues/email/order/order.email.queue.ts @@ -1,5 +1,5 @@ import { Queue } from "bullmq"; -import { redisConnection } from "../../connection"; +import { redisConnection } from "../../connection.js"; export type TOrderEmailQueue = { email: string; diff --git a/src/app/queues/email/order/order.email.worker.ts b/src/app/queues/email/order/order.email.worker.ts index eb3e400..663840b 100644 --- a/src/app/queues/email/order/order.email.worker.ts +++ b/src/app/queues/email/order/order.email.worker.ts @@ -1,8 +1,8 @@ // email.worker.ts import { Worker } from "bullmq"; -import { redisConnection } from "../../connection"; -import { orderEmailProcessor } from "./order.email.processor"; -import { TOrderEmailQueue } from "./order.email.queue"; +import { redisConnection } from "../../connection.js"; +import { orderEmailProcessor } from "./order.email.processor.js"; +import { TOrderEmailQueue } from "./order.email.queue.js"; export const emailWorker = new Worker( diff --git a/src/app/queues/worker.ts b/src/app/queues/worker.ts index 71a333d..7aeafe5 100644 --- a/src/app/queues/worker.ts +++ b/src/app/queues/worker.ts @@ -1,4 +1,4 @@ -import "./email/email.worker"; -import "./email/order/order.email.worker"; +import "./email/email.worker.js"; +import "./email/order/order.email.worker.js"; console.log("Workers running..."); \ No newline at end of file diff --git a/src/app/templates/otpTemplate.ts b/src/app/templates/otpTemplate.ts index ef9d04c..92622ba 100644 --- a/src/app/templates/otpTemplate.ts +++ b/src/app/templates/otpTemplate.ts @@ -1,4 +1,4 @@ -import { TEmailQueue } from "../queues/email/email.queue" +import { TEmailQueue } from "../queues/email/email.queue.js" export const otpTemplate = (payload: TEmailQueue) => { return ` diff --git a/src/app/utils/cloudinary.ts b/src/app/utils/cloudinary.ts index ae77b6b..0ef6d23 100644 --- a/src/app/utils/cloudinary.ts +++ b/src/app/utils/cloudinary.ts @@ -1,6 +1,6 @@ import { v2 as cloudinary } from 'cloudinary'; import fs from 'fs'; -import { configs } from '../configs'; +import { configs } from '../configs/index.js'; type ICloudinaryResponse = { asset_id: string; diff --git a/src/app/utils/mail_sender.ts b/src/app/utils/mail_sender.ts index 9a45052..7257106 100644 --- a/src/app/utils/mail_sender.ts +++ b/src/app/utils/mail_sender.ts @@ -1,5 +1,5 @@ import nodemailer from 'nodemailer'; -import { configs } from '../configs'; +import { configs } from '../configs/index.js'; type TMailContent = { to: string, subject: string, diff --git a/src/routes.ts b/src/routes.ts index 7dcbaa3..a7ac1a2 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -1,9 +1,9 @@ import { Router } from "express"; -import accountRouter from "./app/modules/account/account.route"; -import orderRoute from "./app/modules/order/order.route"; -import planRoute from "./app/modules/plan/plan.route"; -import profileRoute from "./app/modules/profile/profile.route"; -import supportRoute from "./app/modules/support/support.route"; +import accountRouter from "./app/modules/account/account.route.js"; +import orderRoute from "./app/modules/order/order.route.js"; +import planRoute from "./app/modules/plan/plan.route.js"; +import profileRoute from "./app/modules/profile/profile.route.js"; +import supportRoute from "./app/modules/support/support.route.js"; const appRouter = Router(); diff --git a/src/server.ts b/src/server.ts index b265e0d..bf6d3ab 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,7 +1,7 @@ -import app from "./app"; -import { configs } from "./app/configs/index"; -import { prisma } from "./app/lib/prisma"; -import "./app/queues/worker"; +import app from "./app.js"; +import { configs } from "./app/configs/index.js"; +import { prisma } from "./app/lib/prisma.js"; +import "./app/queues/worker.js"; async function main() { try { diff --git a/src/swaggerOptions.ts b/src/swaggerOptions.ts index 2b898f7..93859d5 100644 --- a/src/swaggerOptions.ts +++ b/src/swaggerOptions.ts @@ -1,11 +1,11 @@ import { fileURLToPath } from "node:url"; import path from "path"; -import { configs } from "./app/configs"; -import { accountSwaggerDocs } from "./app/modules/account/account.swagger"; -import { orderSwaggerDocs } from "./app/modules/order/order.swagger"; -import { planSwaggerDocs } from "./app/modules/plan/plan.swagger"; -import { profileSwaggerDocs } from "./app/modules/profile/profile.swagger"; -import { supportSwaggerDocs } from "./app/modules/support/support.swagger"; +import { configs } from "./app/configs/index.js"; +import { accountSwaggerDocs } from "./app/modules/account/account.swagger.js"; +import { orderSwaggerDocs } from "./app/modules/order/order.swagger.js"; +import { planSwaggerDocs } from "./app/modules/plan/plan.swagger.js"; +import { profileSwaggerDocs } from "./app/modules/profile/profile.swagger.js"; +import { supportSwaggerDocs } from "./app/modules/support/support.swagger.js"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -27,8 +27,8 @@ export const swaggerOptions = { }, servers: configs.env === "production" - ? [{ url: "https://live-url.com" }, { url: "http://localhost:5000" }] - : [{ url: "http://localhost:5000" }, { url: "https://live-url.com" }], + ? [{ url: "https://quicklunch-server.onrender.com" }, { url: "http://localhost:5000" }] + : [{ url: "http://localhost:5000" }, { url: "https://quicklunch-server.onrender.com" }], components: { securitySchemes: { AuthorizationToken: { diff --git a/tsconfig.json b/tsconfig.json index f40b4eb..0c4d9a7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,9 +1,9 @@ { "compilerOptions": { "target": "ES2023", - "module": "ESNext", - "moduleResolution": "bundler", - "rootDir": "./", + "module": "nodenext", + "moduleResolution": "nodenext", + "rootDir": "./src", "outDir": "./dist", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, @@ -11,8 +11,7 @@ "skipLibCheck": true }, "include": [ - "src/**/*", - "prisma/**/*" + "src/**/*" ], "exclude": [ "node_modules",