Compare commits
20 Commits
164951321b
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7bf4bc93f1 | |||
| f224ff6bf0 | |||
| 4468c00abf | |||
| 0a8fe573ae | |||
| c05266a522 | |||
| 1abecc9b8f | |||
| d2b320f3b1 | |||
| b09fdfc255 | |||
| 47d30d96eb | |||
| 61fd639faf | |||
| 2d54031c33 | |||
| f886c392aa | |||
| 107b94bc97 | |||
| e227c42f7d | |||
| 0f7af70b90 | |||
| c881efea0f | |||
| 4c1614601a | |||
| 1bc1fae274 | |||
| 739e3d1ad6 | |||
| ba04c54c5b |
+5
-4
@@ -1,8 +1,9 @@
|
||||
node_modules
|
||||
npm-debug.log
|
||||
Dockerfile
|
||||
dist
|
||||
.git
|
||||
.gitignore
|
||||
README.md
|
||||
Dockerfile
|
||||
docker-compose.yml
|
||||
*.log
|
||||
.env
|
||||
dist
|
||||
uploads
|
||||
+2
-1
@@ -4,4 +4,5 @@ node_modules
|
||||
.env.example
|
||||
.env.prod
|
||||
package-lock.json
|
||||
prisma/generated/
|
||||
prisma/generated/
|
||||
dist
|
||||
+21
-6
@@ -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
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "T_SupportType" AS ENUM ('TECHNICAL', 'BILLING', 'DOMAIN', 'TEMPLATE', 'PAYMENT', 'ACCOUNT', 'FEATURE_REQUEST', 'BUG', 'OTHER');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "T_SupportStatus" AS ENUM ('OPEN', 'IN_PROGRESS', 'RESOLVED', 'CLOSED');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Support" (
|
||||
"id" TEXT NOT NULL,
|
||||
"issueName" TEXT NOT NULL,
|
||||
"description" TEXT NOT NULL,
|
||||
"type" "T_SupportType" NOT NULL,
|
||||
"status" "T_SupportStatus" NOT NULL DEFAULT 'OPEN',
|
||||
"resolvedBy" TEXT,
|
||||
"resolvedAt" TIMESTAMP(3),
|
||||
"storeAccountId" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Support_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Support_storeAccountId_idx" ON "Support"("storeAccountId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Support_storeAccountId_status_idx" ON "Support"("storeAccountId", "status");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Support" ADD CONSTRAINT "Support_storeAccountId_fkey" FOREIGN KEY ("storeAccountId") REFERENCES "Account"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- The values [CLOSED] on the enum `T_SupportStatus` will be removed. If these variants are still used in the database, this will fail.
|
||||
|
||||
*/
|
||||
-- AlterEnum
|
||||
BEGIN;
|
||||
CREATE TYPE "T_SupportStatus_new" AS ENUM ('OPEN', 'IN_PROGRESS', 'RESOLVED', 'REJECTED');
|
||||
ALTER TABLE "public"."Support" ALTER COLUMN "status" DROP DEFAULT;
|
||||
ALTER TABLE "Support" ALTER COLUMN "status" TYPE "T_SupportStatus_new" USING ("status"::text::"T_SupportStatus_new");
|
||||
ALTER TYPE "T_SupportStatus" RENAME TO "T_SupportStatus_old";
|
||||
ALTER TYPE "T_SupportStatus_new" RENAME TO "T_SupportStatus";
|
||||
DROP TYPE "public"."T_SupportStatus_old";
|
||||
ALTER TABLE "Support" ALTER COLUMN "status" SET DEFAULT 'OPEN';
|
||||
COMMIT;
|
||||
@@ -0,0 +1,207 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "Template" (
|
||||
"id" TEXT NOT NULL,
|
||||
"language" TEXT NOT NULL,
|
||||
"deliveryCharge" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Template_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Banner" (
|
||||
"id" TEXT NOT NULL,
|
||||
"isVisible" BOOLEAN NOT NULL,
|
||||
"bannerTitle" TEXT NOT NULL,
|
||||
"bannerDesc" TEXT NOT NULL,
|
||||
"bannerImage" TEXT NOT NULL,
|
||||
"templateId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "Banner_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Address" (
|
||||
"id" TEXT NOT NULL,
|
||||
"isVisible" BOOLEAN NOT NULL,
|
||||
"phoneNumber" TEXT NOT NULL,
|
||||
"shopLocation" TEXT NOT NULL,
|
||||
"shopEmail" TEXT NOT NULL,
|
||||
"templateId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "Address_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Ingredient" (
|
||||
"id" TEXT NOT NULL,
|
||||
"isVisible" BOOLEAN NOT NULL,
|
||||
"templateId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "Ingredient_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ingredientOptions" (
|
||||
"id" TEXT NOT NULL,
|
||||
"ingrImg" TEXT NOT NULL,
|
||||
"ingrTitle" TEXT NOT NULL,
|
||||
"ingrDes" TEXT NOT NULL,
|
||||
"ingredientId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "ingredientOptions_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Instruction" (
|
||||
"id" TEXT NOT NULL,
|
||||
"isVisible" BOOLEAN NOT NULL,
|
||||
"instBanner" TEXT NOT NULL,
|
||||
"templateId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "Instruction_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "InstructionOptions" (
|
||||
"id" TEXT NOT NULL,
|
||||
"hint" TEXT NOT NULL,
|
||||
"detail" TEXT NOT NULL,
|
||||
"instructionId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "InstructionOptions_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "FAQ" (
|
||||
"id" TEXT NOT NULL,
|
||||
"isVisible" BOOLEAN NOT NULL,
|
||||
"templateId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "FAQ_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "FAQOptions" (
|
||||
"id" TEXT NOT NULL,
|
||||
"index" INTEGER NOT NULL,
|
||||
"text" TEXT NOT NULL,
|
||||
"faqId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "FAQOptions_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Tips" (
|
||||
"id" TEXT NOT NULL,
|
||||
"isVisible" BOOLEAN NOT NULL,
|
||||
"tipsBanner" TEXT NOT NULL,
|
||||
"templateId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "Tips_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "TipsOption" (
|
||||
"id" TEXT NOT NULL,
|
||||
"index" INTEGER NOT NULL,
|
||||
"text" TEXT NOT NULL,
|
||||
"tipsId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "TipsOption_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "PriceCombo" (
|
||||
"id" TEXT NOT NULL,
|
||||
"isVisible" BOOLEAN NOT NULL,
|
||||
"templateId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "PriceCombo_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "PriceOption" (
|
||||
"id" TEXT NOT NULL,
|
||||
"quantity" TEXT NOT NULL,
|
||||
"price" TEXT NOT NULL,
|
||||
"comboId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "PriceOption_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Product" (
|
||||
"id" TEXT NOT NULL,
|
||||
"isVisible" BOOLEAN NOT NULL,
|
||||
"price" TEXT NOT NULL,
|
||||
"discount" INTEGER NOT NULL,
|
||||
"productName" TEXT NOT NULL,
|
||||
"templateId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "Product_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Banner_templateId_key" ON "Banner"("templateId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Address_templateId_key" ON "Address"("templateId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Ingredient_templateId_key" ON "Ingredient"("templateId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Instruction_templateId_key" ON "Instruction"("templateId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "FAQ_templateId_key" ON "FAQ"("templateId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Tips_templateId_key" ON "Tips"("templateId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "PriceCombo_templateId_key" ON "PriceCombo"("templateId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Product_templateId_key" ON "Product"("templateId");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Banner" ADD CONSTRAINT "Banner_templateId_fkey" FOREIGN KEY ("templateId") REFERENCES "Template"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Address" ADD CONSTRAINT "Address_templateId_fkey" FOREIGN KEY ("templateId") REFERENCES "Template"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Ingredient" ADD CONSTRAINT "Ingredient_templateId_fkey" FOREIGN KEY ("templateId") REFERENCES "Template"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "ingredientOptions" ADD CONSTRAINT "ingredientOptions_ingredientId_fkey" FOREIGN KEY ("ingredientId") REFERENCES "Ingredient"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Instruction" ADD CONSTRAINT "Instruction_templateId_fkey" FOREIGN KEY ("templateId") REFERENCES "Template"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "InstructionOptions" ADD CONSTRAINT "InstructionOptions_instructionId_fkey" FOREIGN KEY ("instructionId") REFERENCES "Instruction"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "FAQ" ADD CONSTRAINT "FAQ_templateId_fkey" FOREIGN KEY ("templateId") REFERENCES "Template"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "FAQOptions" ADD CONSTRAINT "FAQOptions_faqId_fkey" FOREIGN KEY ("faqId") REFERENCES "FAQ"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Tips" ADD CONSTRAINT "Tips_templateId_fkey" FOREIGN KEY ("templateId") REFERENCES "Template"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "TipsOption" ADD CONSTRAINT "TipsOption_tipsId_fkey" FOREIGN KEY ("tipsId") REFERENCES "Tips"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "PriceCombo" ADD CONSTRAINT "PriceCombo_templateId_fkey" FOREIGN KEY ("templateId") REFERENCES "Template"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "PriceOption" ADD CONSTRAINT "PriceOption_comboId_fkey" FOREIGN KEY ("comboId") REFERENCES "PriceCombo"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Product" ADD CONSTRAINT "Product_templateId_fkey" FOREIGN KEY ("templateId") REFERENCES "Template"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,14 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "ShopStatus" AS ENUM ('ACTIVE', 'SUSPENDED', 'DELETED');
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Profile" ADD COLUMN "status" "ShopStatus" NOT NULL DEFAULT 'ACTIVE';
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Users" (
|
||||
"id" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Users_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterEnum
|
||||
ALTER TYPE "ShopStatus" ADD VALUE 'INACTIVE';
|
||||
@@ -21,4 +21,5 @@ model Account {
|
||||
profile Profile? //one-to-one
|
||||
|
||||
orders Order[] //one-to-many
|
||||
supports Support[]
|
||||
}
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
model Profile {
|
||||
id String @id @default(uuid())
|
||||
accountId String @unique
|
||||
account Account @relation(fields: [accountId], references: [id], onDelete: Cascade)
|
||||
enum ShopStatus {
|
||||
ACTIVE
|
||||
SUSPENDED
|
||||
DELETED
|
||||
INACTIVE
|
||||
}
|
||||
|
||||
model Profile {
|
||||
id String @id @default(uuid())
|
||||
accountId String @unique
|
||||
account Account @relation(fields: [accountId], references: [id], onDelete: Cascade)
|
||||
shopName String
|
||||
shopLogo String?
|
||||
contactNumber String?
|
||||
shopAddress String?
|
||||
shopMapLocation String?
|
||||
shopCategory String?
|
||||
|
||||
status ShopStatus @default(ACTIVE)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
generator client {
|
||||
provider = "prisma-client"
|
||||
output = "../generated/prisma"
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
enum T_SupportType {
|
||||
TECHNICAL
|
||||
BILLING
|
||||
DOMAIN
|
||||
TEMPLATE
|
||||
PAYMENT
|
||||
ACCOUNT
|
||||
FEATURE_REQUEST
|
||||
BUG
|
||||
OTHER
|
||||
}
|
||||
|
||||
enum T_SupportStatus {
|
||||
OPEN
|
||||
IN_PROGRESS
|
||||
RESOLVED
|
||||
REJECTED
|
||||
}
|
||||
|
||||
|
||||
|
||||
model Support {
|
||||
id String @id @default(uuid())
|
||||
|
||||
issueName String
|
||||
description String
|
||||
|
||||
type T_SupportType
|
||||
status T_SupportStatus @default(OPEN)
|
||||
|
||||
resolvedBy String?
|
||||
resolvedAt DateTime?
|
||||
|
||||
storeAccountId String
|
||||
|
||||
storeAccount Account @relation(fields: [storeAccountId], references: [id])
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([storeAccountId])
|
||||
@@index([storeAccountId, status])
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
model Template {
|
||||
id String @id @default(uuid())
|
||||
language String
|
||||
deliveryCharge String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
address Address?
|
||||
banner Banner?
|
||||
ingredient Ingredient?
|
||||
instruction Instruction?
|
||||
faq FAQ?
|
||||
tips Tips?
|
||||
priceCombo PriceCombo?
|
||||
product Product?
|
||||
}
|
||||
|
||||
// banner model
|
||||
model Banner {
|
||||
id String @id @default(uuid())
|
||||
isVisible Boolean
|
||||
bannerTitle String
|
||||
bannerDesc String
|
||||
bannerImage String
|
||||
templateId String @unique
|
||||
template Template @relation(fields: [templateId], references: [id])
|
||||
}
|
||||
|
||||
// address model
|
||||
model Address {
|
||||
id String @id @default(uuid())
|
||||
isVisible Boolean
|
||||
phoneNumber String
|
||||
shopLocation String
|
||||
shopEmail String
|
||||
templateId String @unique
|
||||
template Template @relation(fields: [templateId], references: [id])
|
||||
}
|
||||
|
||||
//ingredient model
|
||||
model Ingredient {
|
||||
id String @id @default(uuid())
|
||||
isVisible Boolean
|
||||
templateId String @unique
|
||||
template Template @relation(fields: [templateId], references: [id])
|
||||
options ingredientOptions[]
|
||||
}
|
||||
|
||||
//ingredient options model
|
||||
model ingredientOptions {
|
||||
id String @id @default(uuid())
|
||||
ingrImg String
|
||||
ingrTitle String
|
||||
ingrDes String
|
||||
ingredientId String
|
||||
ingredient Ingredient @relation(fields: [ingredientId], references: [id])
|
||||
}
|
||||
|
||||
//instruction model & instruction options
|
||||
model Instruction {
|
||||
id String @id @default(uuid())
|
||||
isVisible Boolean
|
||||
instBanner String
|
||||
templateId String @unique
|
||||
template Template @relation(fields: [templateId], references: [id])
|
||||
options InstructionOptions[]
|
||||
}
|
||||
|
||||
model InstructionOptions {
|
||||
id String @id @default(uuid())
|
||||
hint String
|
||||
detail String
|
||||
instructionId String
|
||||
instruction Instruction @relation(fields: [instructionId], references: [id])
|
||||
}
|
||||
|
||||
//FAQ & FAQOptions model
|
||||
model FAQ {
|
||||
id String @id @default(uuid())
|
||||
isVisible Boolean
|
||||
templateId String @unique
|
||||
template Template @relation(fields: [templateId], references: [id])
|
||||
options FAQOptions[]
|
||||
}
|
||||
|
||||
model FAQOptions {
|
||||
id String @id @default(uuid())
|
||||
index Int
|
||||
text String
|
||||
|
||||
faqId String
|
||||
faq FAQ @relation(fields: [faqId], references: [id])
|
||||
}
|
||||
|
||||
//Tips & Tips options model
|
||||
model Tips {
|
||||
id String @id @default(uuid())
|
||||
isVisible Boolean
|
||||
tipsBanner String
|
||||
templateId String @unique
|
||||
template Template @relation(fields: [templateId], references: [id])
|
||||
options TipsOption[]
|
||||
}
|
||||
|
||||
model TipsOption {
|
||||
id String @id @default(uuid())
|
||||
index Int
|
||||
text String
|
||||
|
||||
tipsId String
|
||||
tips Tips @relation(fields: [tipsId], references: [id])
|
||||
}
|
||||
|
||||
//PriceCombo & PriceComboOptions model
|
||||
model PriceCombo {
|
||||
id String @id @default(uuid())
|
||||
isVisible Boolean
|
||||
templateId String @unique
|
||||
template Template @relation(fields: [templateId], references: [id])
|
||||
options PriceOption[]
|
||||
}
|
||||
|
||||
model PriceOption {
|
||||
id String @id @default(uuid())
|
||||
quantity String
|
||||
price String
|
||||
comboId String
|
||||
combo PriceCombo @relation(fields: [comboId], references: [id])
|
||||
}
|
||||
|
||||
//product model
|
||||
model Product {
|
||||
id String @id @default(uuid())
|
||||
isVisible Boolean
|
||||
price String
|
||||
discount Int
|
||||
productName String
|
||||
templateId String @unique
|
||||
template Template @relation(fields: [templateId], references: [id])
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
model Users {
|
||||
id String @id @default(uuid())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
+5
-5
@@ -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()
|
||||
@@ -15,7 +15,7 @@ app.use("/docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec));
|
||||
|
||||
// middleware
|
||||
app.use(cors({
|
||||
origin: ["http://localhost:3000"],
|
||||
origin: ["http://localhost:5173"],
|
||||
methods: ["GET", "POST", "PATCH", "DELETE", "PUT"],
|
||||
credentials: true
|
||||
}))
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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}`;
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
@@ -37,7 +37,7 @@ 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, {
|
||||
res.cookie("access_token", result.accessToken, {
|
||||
secure: configs.env === "production",
|
||||
httpOnly: true,
|
||||
});
|
||||
@@ -46,9 +46,7 @@ const login_user = catchAsync(async (req, res) => {
|
||||
statusCode: 200,
|
||||
success: true,
|
||||
message: "User logged in successfully",
|
||||
data: {
|
||||
accessToken: result,
|
||||
},
|
||||
data: result,
|
||||
});
|
||||
});
|
||||
const get_user_account = catchAsync(async (req, res) => {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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,
|
||||
@@ -71,6 +71,10 @@ const create_account_into_db = async (req: Request) => {
|
||||
subject: "Welcome to Quick Launch - Verification OTP",
|
||||
email: payload.email,
|
||||
textBody: "You can use otp or verification link for verifying your account"
|
||||
}, {
|
||||
attempts: 1,
|
||||
removeOnComplete: true,
|
||||
removeOnFail: true,
|
||||
})
|
||||
return null;
|
||||
};
|
||||
@@ -160,6 +164,15 @@ const login_user_into_db = async (req: Request) => {
|
||||
where: {
|
||||
email: payload.email,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
role: true,
|
||||
isAccountVerified: true,
|
||||
isDeleted: true,
|
||||
password: true,
|
||||
profile: true,
|
||||
},
|
||||
});
|
||||
|
||||
// check if account exists
|
||||
@@ -196,7 +209,21 @@ const login_user_into_db = async (req: Request) => {
|
||||
configs.jwt.access_token as string,
|
||||
configs.jwt.access_expires as string,
|
||||
);
|
||||
return accessToken;
|
||||
|
||||
|
||||
const finalOutputData = {
|
||||
id: account.id,
|
||||
email: account.email,
|
||||
role: account.role,
|
||||
shopName: account?.profile?.shopName,
|
||||
shopLogo: account?.profile?.shopLogo,
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
accessToken,
|
||||
profile: finalOutputData
|
||||
};
|
||||
};
|
||||
|
||||
const get_user_account_from_db = async (req: Request) => {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,8 +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 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();
|
||||
|
||||
@@ -14,7 +15,7 @@ router.post(
|
||||
);
|
||||
router.get("/:id", order_controller.get_single_order);
|
||||
router.patch(
|
||||
"/:id",
|
||||
"/:id",auth("ADMIN"),
|
||||
RequestValidator(order_validations.update_order),
|
||||
order_controller.update_order,
|
||||
);
|
||||
|
||||
@@ -1,61 +1,124 @@
|
||||
|
||||
import { Request } from "express";
|
||||
import { configs } from "../../configs";
|
||||
import { prisma } from "../../lib/prisma";
|
||||
import { orderEmailQueue } from "../../queues/email/order/order.email.queue";
|
||||
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
|
||||
const search=req.query.search as string
|
||||
const customerName=req.query.customerName as string
|
||||
const productName=req.query.productName as string
|
||||
console.log(productName)
|
||||
const andCondition=[] as any[]
|
||||
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"
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
console.log(search)
|
||||
const search = req.query.search as string;
|
||||
const customerName = req.query.customerName as string;
|
||||
const productName = req.query.productName as string;
|
||||
|
||||
const result = await prisma.order.findMany({
|
||||
where:{
|
||||
AND:andCondition
|
||||
}
|
||||
// for date filter
|
||||
|
||||
const startDate = req.query.startDate as string;
|
||||
const endDate = req.query.endDate as string;
|
||||
|
||||
const status = (req.query.status as string) || undefined;
|
||||
const { page, limit, skip, sortBy, sortOrder } = paginationHelper(req.query);
|
||||
|
||||
const andCondition = [] as any[];
|
||||
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: any = {};
|
||||
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,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
customerName: true,
|
||||
productQuantity: true,
|
||||
productPrice: true,
|
||||
status: true,
|
||||
createdAt: true,
|
||||
},
|
||||
orderBy: {
|
||||
[sortBy as string]: sortOrder,
|
||||
},
|
||||
});
|
||||
return result;
|
||||
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: Request) => {
|
||||
@@ -67,9 +130,9 @@ const get_single_order_from_db = async (req: Request) => {
|
||||
|
||||
const create_order_into_db = async (req: Request) => {
|
||||
const payload = req?.body;
|
||||
console.log(payload)
|
||||
|
||||
payload.status = "INITIATED";
|
||||
payload.paymentType = "COD"
|
||||
payload.paymentType = "COD";
|
||||
|
||||
// nwo init order
|
||||
const result = await prisma.order.create({ data: payload });
|
||||
@@ -81,15 +144,23 @@ const create_order_into_db = async (req: Request) => {
|
||||
email: payload.customerEmail,
|
||||
subject: "Order Tracking",
|
||||
textBody: `Your order has been created. Track your order here: ${trackingLink}`,
|
||||
htmlBody: `<p>Your order has been created. Track your order here: <a href="${trackingLink}">Track Order</a></p>`
|
||||
})
|
||||
htmlBody: `<p>Your order has been created. Track your order here: <a href="${trackingLink}">Track Order</a></p>`,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const update_order_into_db = async (req: Request) => {
|
||||
// define your own login here
|
||||
const user = req.user;
|
||||
if (user?.role !== "ADMIN") {
|
||||
throw new AppError("You are not authorized to perform this action", 403);
|
||||
}
|
||||
const { id } = req.params as { id: string };
|
||||
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;
|
||||
};
|
||||
@@ -97,6 +168,10 @@ const update_order_into_db = async (req: Request) => {
|
||||
const delete_order_from_db = async (req: Request) => {
|
||||
// define your own login here
|
||||
const { id } = req.params as { id: string };
|
||||
const user = req.user;
|
||||
if (user?.role !== "ADMIN") {
|
||||
throw new AppError("You are not authorized to perform this action", 403);
|
||||
}
|
||||
const result = await prisma.order.delete({ where: { id } });
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
|
||||
export const orderSwaggerDocs = {
|
||||
"/api/order": {
|
||||
post: {
|
||||
tags: ["order"],
|
||||
summary: "Create new order",
|
||||
description: "",
|
||||
description: ` INITIATED
|
||||
CONFIRMED
|
||||
ONGOING
|
||||
DELIVERED
|
||||
CANCELLED`,
|
||||
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"
|
||||
})
|
||||
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",
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -63,6 +66,32 @@ export const orderSwaggerDocs = {
|
||||
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" },
|
||||
@@ -91,7 +120,7 @@ export const orderSwaggerDocs = {
|
||||
},
|
||||
patch: {
|
||||
tags: ["order"],
|
||||
summary: "Update order",
|
||||
summary: "Update order -(Admin route)",
|
||||
description: "",
|
||||
parameters: [
|
||||
{
|
||||
@@ -106,14 +135,7 @@ export const orderSwaggerDocs = {
|
||||
content: {
|
||||
"application/json": {
|
||||
example: JSON.stringify({
|
||||
"shopAccountId": "",
|
||||
"productPrice": 1500,
|
||||
"productQuantity": 2,
|
||||
"productName": "Wireless Mouse",
|
||||
"customerName": "Rahim Uddin",
|
||||
"customerPhone": "+8801712345678",
|
||||
"customerAddress": "Rangpur, Bangladesh",
|
||||
"customerNote": "Please deliver between 3-5 PM"
|
||||
status: "INITIATED",
|
||||
}), // put your request body
|
||||
},
|
||||
},
|
||||
@@ -142,5 +164,3 @@ export const orderSwaggerDocs = {
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 { Router } from "express";
|
||||
import auth from "../../middlewares/auth.js";
|
||||
import RequestValidator from "../../middlewares/request_validator.js";
|
||||
import { plan_controller } from "./plan.controller.js";
|
||||
import { plan_validations } from "./plan.validation.js";
|
||||
|
||||
const router = Router();
|
||||
|
||||
@@ -10,15 +10,16 @@ router.get("/", plan_controller.get_all_plan);
|
||||
router.post(
|
||||
"/",
|
||||
RequestValidator(plan_validations.create_plan),
|
||||
auth("ADMIN"),
|
||||
plan_controller.create_plan,
|
||||
);
|
||||
router.get("/:id", plan_controller.get_single_plan);
|
||||
router.patch(
|
||||
"/:id",
|
||||
RequestValidator(plan_validations.update_plan),
|
||||
auth("ADMIN"),
|
||||
plan_controller.update_plan,
|
||||
);
|
||||
router.delete("/:id", plan_controller.delete_plan);
|
||||
router.delete("/:id", auth("ADMIN"), plan_controller.delete_plan);
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
|
||||
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
|
||||
const result = await prisma.plan.findMany();
|
||||
const result = await prisma.plan.findMany({
|
||||
select:{
|
||||
planName: true,
|
||||
price: true,
|
||||
planType: true,
|
||||
planDesc: true,
|
||||
planFeatures: true,
|
||||
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -22,7 +31,7 @@ const get_single_plan_from_db = async (req: Request) => {
|
||||
|
||||
const create_plan_into_db = async (req: Request) => {
|
||||
// define your own login here
|
||||
const user = req.user
|
||||
const user = req?.user
|
||||
if (user?.role !== "ADMIN") {
|
||||
throw new AppError("You don’t have permission to create plan information.!!!", 401)
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -13,7 +13,7 @@ export const profileSwaggerDocs = {
|
||||
data: {
|
||||
type: "object",
|
||||
properties: {
|
||||
fullName: { type: "string" },
|
||||
shopName: { type: "string" },
|
||||
},
|
||||
},
|
||||
file: {
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import { z } from "zod";
|
||||
const update_profile = z.object({
|
||||
fullName: z.string().optional(),
|
||||
shopName: z.string().optional(),
|
||||
shopAddress: z.string().optional(),
|
||||
shopPhone: z.string().optional(),
|
||||
shopLocation: z.string().optional(),
|
||||
shopImage: z.string().optional(),
|
||||
shopMapLocation: z.string().optional(),
|
||||
contactNumber: z.string().optional(),
|
||||
shopCategory: z.string().optional(),
|
||||
|
||||
|
||||
});
|
||||
|
||||
export const profile_validations = {
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
|
||||
export const staticticsSwaggerDocs = {
|
||||
"/api/statictics": {
|
||||
post: {
|
||||
tags: ["statictics"],
|
||||
summary: "Create new statictics",
|
||||
description: "",
|
||||
requestBody: {
|
||||
|
||||
required: true,
|
||||
content: {
|
||||
"application/json": {
|
||||
example: JSON.stringify({}), // put your request body
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
201: { description: "statictics created successfully" },
|
||||
500: { description: "Validation error or internal server error" },
|
||||
},
|
||||
},
|
||||
get: {
|
||||
tags: ["statictics"],
|
||||
summary: "Get all statictics",
|
||||
description: "",
|
||||
parameters: [
|
||||
{
|
||||
name: "page",
|
||||
in: "query",
|
||||
required: false,
|
||||
schema: { type: "number" },
|
||||
},
|
||||
{
|
||||
name: "limit",
|
||||
in: "query",
|
||||
required: false,
|
||||
schema: { type: "number" },
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
200: { description: "statictics fetched successfully" },
|
||||
401: { description: "unauthorized" },
|
||||
},
|
||||
},
|
||||
},
|
||||
"/api/statictics/seller": {
|
||||
get: {
|
||||
tags: ["statistics"],
|
||||
summary: "Get seller statistics",
|
||||
description: "Fetch seller dashboard stats (7d, 30d, all)",
|
||||
|
||||
parameters: [
|
||||
{
|
||||
name: "range",
|
||||
in: "query",
|
||||
required: false,
|
||||
schema: {
|
||||
type: "string",
|
||||
enum: ["7d", "30d", "all"],
|
||||
default: "7d",
|
||||
},
|
||||
description: "Time range for statistics",
|
||||
},
|
||||
],
|
||||
|
||||
responses: {
|
||||
200: {
|
||||
description: "Statistics fetched successfully",
|
||||
},
|
||||
401: {
|
||||
description: "Unauthorized",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
"/api/statictics/{id}": {
|
||||
get: {
|
||||
tags: ["statictics"],
|
||||
summary: "Get single statictics",
|
||||
description: "",
|
||||
parameters: [
|
||||
{
|
||||
name: "id",
|
||||
in: "path",
|
||||
required: true,
|
||||
schema: { type: "string" },
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
200: { description: "statictics fetched successfully" },
|
||||
401: { description: "unauthorized" },
|
||||
},
|
||||
},
|
||||
patch: {
|
||||
tags: ["statictics"],
|
||||
summary: "Update statictics",
|
||||
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: "statictics updated successfully" },
|
||||
500: { description: "Validation error or internal server error" },
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
tags: ["statictics"],
|
||||
summary: "Delete statictics",
|
||||
description: "",
|
||||
parameters: [
|
||||
{
|
||||
name: "id",
|
||||
in: "path",
|
||||
required: true,
|
||||
schema: { type: "string" },
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
200: { description: "statictics delete successfully" },
|
||||
401: { description: "unauthorized" },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,99 @@
|
||||
|
||||
import { Request, Response } from "express";
|
||||
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: Request, res: Response) => {
|
||||
const id = req?.user?.accountId;
|
||||
const data = {
|
||||
...req.body,
|
||||
storeAccountId: id as string
|
||||
}
|
||||
|
||||
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: Request, res: Response) => {
|
||||
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 as string, role as string, search as string, type as string, status as string);
|
||||
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 as string, userId as string, role as string);
|
||||
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 as string, userId as string, role as string, 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 as string, userId as string, role as string);
|
||||
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,
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
|
||||
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;
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
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 });
|
||||
return result;
|
||||
};
|
||||
|
||||
const getAllSupportFromDB = async (
|
||||
user_id: string,
|
||||
role: string,
|
||||
search?: string,
|
||||
type?: string,
|
||||
status?: string,
|
||||
) => {
|
||||
const andCondition: Prisma.SupportWhereInput[] = [];
|
||||
|
||||
if (search) {
|
||||
andCondition.push({
|
||||
OR: [
|
||||
{
|
||||
issueName: {
|
||||
contains: search,
|
||||
mode: "insensitive",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: {
|
||||
contains: search,
|
||||
mode: "insensitive",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
if (type) {
|
||||
andCondition.push({
|
||||
type: type as any,
|
||||
});
|
||||
}
|
||||
|
||||
if (status) {
|
||||
andCondition.push({
|
||||
status: status as any,
|
||||
});
|
||||
}
|
||||
|
||||
if (role !== "ADMIN") {
|
||||
andCondition.push({
|
||||
storeAccountId: user_id,
|
||||
});
|
||||
}
|
||||
|
||||
const whereCondition: Prisma.SupportWhereInput =
|
||||
andCondition.length > 0 ? { AND: andCondition } : {};
|
||||
|
||||
const result = await prisma.support.findMany({
|
||||
where: whereCondition,
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const getSingleSupportFromDB = async (
|
||||
id: string,
|
||||
userId: string,
|
||||
role: string,
|
||||
) => {
|
||||
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: string,
|
||||
userId: string,
|
||||
role: string,
|
||||
payload: any,
|
||||
) => {
|
||||
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: string,
|
||||
userId: string,
|
||||
role: string,
|
||||
) => {
|
||||
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,
|
||||
};
|
||||
@@ -0,0 +1,114 @@
|
||||
|
||||
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" },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
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,
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
import catchAsync from "../../utils/catch_async.js";
|
||||
import manageResponse from "../../utils/manage_response.js";
|
||||
import { template_service } from "./template.service.js";
|
||||
|
||||
const get_all_template = catchAsync(async (req, res) => {
|
||||
const result = await template_service.get_all_template_from_db(req);
|
||||
manageResponse(res, {
|
||||
success: true,
|
||||
statusCode: 200,
|
||||
message: "All template fetched successfully.",
|
||||
data: result,
|
||||
meta: {},
|
||||
});
|
||||
});
|
||||
|
||||
const get_single_template = catchAsync(async (req, res) => {
|
||||
const result = await template_service.get_single_template_from_db(req);
|
||||
manageResponse(res, {
|
||||
success: true,
|
||||
statusCode: 200,
|
||||
message: "Single template fetched successfully.",
|
||||
data: result,
|
||||
meta: {},
|
||||
});
|
||||
});
|
||||
|
||||
const create_template = catchAsync(async (req, res) => {
|
||||
const result = await template_service.create_template_into_db(req);
|
||||
manageResponse(res, {
|
||||
success: true,
|
||||
statusCode: 200,
|
||||
message: "template created successfully.",
|
||||
data: result,
|
||||
meta: {},
|
||||
});
|
||||
});
|
||||
|
||||
const update_template = catchAsync(async (req, res) => {
|
||||
const result = await template_service.update_template_into_db(req);
|
||||
manageResponse(res, {
|
||||
success: true,
|
||||
statusCode: 200,
|
||||
message: "template updated successfully.",
|
||||
data: result,
|
||||
meta: {},
|
||||
});
|
||||
});
|
||||
|
||||
const delete_template = catchAsync(async (req, res) => {
|
||||
const result = await template_service.delete_template_from_db(req);
|
||||
manageResponse(res, {
|
||||
success: true,
|
||||
statusCode: 200,
|
||||
message: "template deleted successfully.",
|
||||
data: result,
|
||||
meta: {},
|
||||
});
|
||||
});
|
||||
|
||||
export const template_controller = {
|
||||
get_all_template,
|
||||
get_single_template,
|
||||
create_template,
|
||||
update_template,
|
||||
delete_template,
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Router } from "express";
|
||||
import RequestValidator from "../../middlewares/request_validator.js";
|
||||
import { template_controller } from "./template.controller.js";
|
||||
import { template_validations } from "./template.validation.js";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", template_controller.get_all_template);
|
||||
router.post(
|
||||
"/",
|
||||
RequestValidator(template_validations.create_template),
|
||||
template_controller.create_template,
|
||||
);
|
||||
router.get("/:id", template_controller.get_single_template);
|
||||
router.patch(
|
||||
"/:id",
|
||||
RequestValidator(template_validations.update_template),
|
||||
template_controller.update_template,
|
||||
);
|
||||
router.delete("/:id", template_controller.delete_template);
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,143 @@
|
||||
import { Request } from "express";
|
||||
import { prisma } from "../../lib/prisma.js";
|
||||
|
||||
const get_all_template_from_db = async (req: Request) => {
|
||||
// define your own login here
|
||||
const result = await prisma.template.findMany({});
|
||||
return result;
|
||||
};
|
||||
|
||||
const get_single_template_from_db = async (req: Request) => {
|
||||
// define your own login here
|
||||
const { id } = req.params as { id: string };
|
||||
const result = await prisma.template.findUnique({
|
||||
where: { id },
|
||||
select: {
|
||||
id: true,
|
||||
language: true,
|
||||
deliveryCharge: true,
|
||||
banner: true,
|
||||
address: true,
|
||||
ingredient: true,
|
||||
instruction: true,
|
||||
faq: true,
|
||||
tips: true,
|
||||
priceCombo: true,
|
||||
product: true,
|
||||
},
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
const create_template_into_db = async (req: Request) => {
|
||||
// define your own login here
|
||||
const payload = req.body;
|
||||
console.log(payload);
|
||||
const result = await prisma.template.create({
|
||||
data: {
|
||||
language: payload.language,
|
||||
deliveryCharge: payload.deliveryCharge,
|
||||
|
||||
banner: {
|
||||
create: payload.banner,
|
||||
},
|
||||
|
||||
address: {
|
||||
create: payload.address,
|
||||
},
|
||||
|
||||
ingredient: {
|
||||
create: {
|
||||
isVisible: payload.ingredient.isVisible,
|
||||
options: {
|
||||
create: payload.ingredient.options,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
instruction: {
|
||||
create: {
|
||||
isVisible: payload.instruction.isVisible,
|
||||
instBanner: payload.instruction.instBanner,
|
||||
options: {
|
||||
create: payload.instruction.options,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
faq: {
|
||||
create: {
|
||||
isVisible: payload.faq.isVisible,
|
||||
options: {
|
||||
create: payload.faq.options,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
tips: {
|
||||
create: {
|
||||
isVisible: payload.tips.isVisible,
|
||||
tipsBanner: payload.tips.tipsBanner,
|
||||
options: {
|
||||
create: payload.tips.options,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
priceCombo: {
|
||||
create: {
|
||||
isVisible: payload.priceCombo.isVisible,
|
||||
options: {
|
||||
create: payload.priceCombo.options,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
product: {
|
||||
create: {
|
||||
isVisible: payload.product.isVisible,
|
||||
price: payload.product.price,
|
||||
discount: payload.product.discount,
|
||||
productName: payload.product.productName,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
include: {
|
||||
banner: true,
|
||||
address: true,
|
||||
ingredient: { include: { options: true } },
|
||||
instruction: { include: { options: true } },
|
||||
faq: { include: { options: true } },
|
||||
tips: { include: { options: true } },
|
||||
priceCombo: { include: { options: true } },
|
||||
product: true,
|
||||
},
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
const update_template_into_db = async (req: Request) => {
|
||||
// define your own login here
|
||||
const { id } = req.params as { id: string };
|
||||
const result = await prisma.template.update({
|
||||
where: { id },
|
||||
data: req.body,
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
const delete_template_from_db = async (req: Request) => {
|
||||
// define your own login here
|
||||
const { id } = req.params as { id: string };
|
||||
const result = await prisma.template.delete({ where: { id } });
|
||||
return result;
|
||||
};
|
||||
|
||||
export const template_service = {
|
||||
get_all_template_from_db,
|
||||
get_single_template_from_db,
|
||||
create_template_into_db,
|
||||
update_template_into_db,
|
||||
delete_template_from_db,
|
||||
};
|
||||
@@ -0,0 +1,197 @@
|
||||
export const templateSwaggerDocs = {
|
||||
"/api/template": {
|
||||
post: {
|
||||
tags: ["template"],
|
||||
summary: "Create new template",
|
||||
description: "",
|
||||
requestBody: {
|
||||
required: true,
|
||||
content: {
|
||||
"application/json": {
|
||||
example: JSON.stringify({
|
||||
language: "en",
|
||||
deliveryCharge: "60",
|
||||
banner: {
|
||||
isVisible: true,
|
||||
bannerTitle: "Delicious Homemade Food",
|
||||
bannerDesc:
|
||||
"Fresh, healthy and tasty meals delivered to your door.",
|
||||
bannerImage: "https://example.com/banner.jpg",
|
||||
},
|
||||
address: {
|
||||
isVisible: true,
|
||||
phoneNumber: "+8801712345678",
|
||||
shopLocation: "Dhaka, Bangladesh",
|
||||
shopEmail: "foodshop@example.com",
|
||||
},
|
||||
ingredient: {
|
||||
isVisible: true,
|
||||
options: [
|
||||
{
|
||||
ingrImg: "https://example.com/tomato.jpg",
|
||||
ingrTitle: "Tomato",
|
||||
ingrDes: "Fresh organic tomatoes",
|
||||
},
|
||||
{
|
||||
ingrImg: "https://example.com/chicken.jpg",
|
||||
ingrTitle: "Chicken",
|
||||
ingrDes: "Premium quality chicken",
|
||||
},
|
||||
],
|
||||
},
|
||||
instruction: {
|
||||
isVisible: true,
|
||||
instBanner: "https://example.com/instruction-banner.jpg",
|
||||
options: [
|
||||
{
|
||||
hint: "Step 1",
|
||||
detail: "Wash all ingredients properly",
|
||||
},
|
||||
{
|
||||
hint: "Step 2",
|
||||
detail: "Cook on medium heat for 20 minutes",
|
||||
},
|
||||
],
|
||||
},
|
||||
faq: {
|
||||
isVisible: true,
|
||||
options: [
|
||||
{
|
||||
index: 1,
|
||||
text: "Is the food fresh?",
|
||||
},
|
||||
{
|
||||
index: 2,
|
||||
text: "Do you offer home delivery?",
|
||||
},
|
||||
],
|
||||
},
|
||||
tips: {
|
||||
isVisible: true,
|
||||
tipsBanner: "https://example.com/tips-banner.jpg",
|
||||
options: [
|
||||
{
|
||||
index: 1,
|
||||
text: "Use fresh ingredients for best taste",
|
||||
},
|
||||
{
|
||||
index: 2,
|
||||
text: "Serve hot for better flavor",
|
||||
},
|
||||
],
|
||||
},
|
||||
priceCombo: {
|
||||
isVisible: true,
|
||||
options: [
|
||||
{
|
||||
quantity: "1",
|
||||
price: "120",
|
||||
},
|
||||
{
|
||||
quantity: "2",
|
||||
price: "220",
|
||||
},
|
||||
],
|
||||
},
|
||||
product: {
|
||||
isVisible: true,
|
||||
price: "120",
|
||||
discount: 10,
|
||||
productName: "Chicken Burger",
|
||||
},
|
||||
}), // put your request body
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
201: { description: "template created successfully" },
|
||||
500: { description: "Validation error or internal server error" },
|
||||
},
|
||||
},
|
||||
get: {
|
||||
tags: ["template"],
|
||||
summary: "Get all template",
|
||||
description: "",
|
||||
parameters: [
|
||||
{
|
||||
name: "page",
|
||||
in: "query",
|
||||
required: false,
|
||||
schema: { type: "number" },
|
||||
},
|
||||
{
|
||||
name: "limit",
|
||||
in: "query",
|
||||
required: false,
|
||||
schema: { type: "number" },
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
200: { description: "template fetched successfully" },
|
||||
401: { description: "unauthorized" },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
"/api/template/{id}": {
|
||||
get: {
|
||||
tags: ["template"],
|
||||
summary: "Get single template",
|
||||
description: "",
|
||||
parameters: [
|
||||
{
|
||||
name: "id",
|
||||
in: "path",
|
||||
required: true,
|
||||
schema: { type: "string" },
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
200: { description: "template fetched successfully" },
|
||||
401: { description: "unauthorized" },
|
||||
},
|
||||
},
|
||||
patch: {
|
||||
tags: ["template"],
|
||||
summary: "Update template",
|
||||
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: "template updated successfully" },
|
||||
500: { description: "Validation error or internal server error" },
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
tags: ["template"],
|
||||
summary: "Delete template",
|
||||
description: "",
|
||||
parameters: [
|
||||
{
|
||||
name: "id",
|
||||
in: "path",
|
||||
required: true,
|
||||
schema: { type: "string" },
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
200: { description: "template delete successfully" },
|
||||
401: { description: "unauthorized" },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,87 @@
|
||||
import { z } from "zod";
|
||||
|
||||
// Template Options that we need to use for validation.
|
||||
|
||||
// Ingredient Option
|
||||
const ingredientOptionSchema = z.object({
|
||||
ingrImg: z.string().url().or(z.string()),
|
||||
ingrTitle: z.string().min(1),
|
||||
ingrDes: z.string().min(1),
|
||||
});
|
||||
|
||||
// Instruction Option
|
||||
const instructionOptionSchema = z.object({
|
||||
hint: z.string().min(1),
|
||||
detail: z.string().min(1),
|
||||
});
|
||||
|
||||
// FAQ Option
|
||||
const faqOptionSchema = z.object({
|
||||
index: z.number(),
|
||||
text: z.string().min(1),
|
||||
});
|
||||
|
||||
// Tips Option
|
||||
const tipsOptionSchema = z.object({
|
||||
index: z.number(),
|
||||
text: z.string().min(1),
|
||||
});
|
||||
|
||||
// Price Option
|
||||
const priceOptionSchema = z.object({
|
||||
quantity: z.string().min(1),
|
||||
price: z.string().min(1),
|
||||
});
|
||||
|
||||
|
||||
// Create the main template schema validation
|
||||
const create_template = z.object({
|
||||
language: z.string().min(1),
|
||||
deliveryCharge: z.string().min(1),
|
||||
banner: z.object({
|
||||
isVisible: z.boolean(),
|
||||
bannerTitle: z.string().min(1),
|
||||
bannerDesc: z.string().min(1),
|
||||
bannerImage: z.string(),
|
||||
}),
|
||||
address: z.object({
|
||||
isVisible: z.boolean(),
|
||||
phoneNumber: z.string().min(1),
|
||||
shopLocation: z.string().min(1),
|
||||
shopEmail: z.string().email(),
|
||||
}),
|
||||
ingredient: z.object({
|
||||
isVisible: z.boolean(),
|
||||
options: z.array(ingredientOptionSchema).min(1),
|
||||
}),
|
||||
instruction: z.object({
|
||||
isVisible: z.boolean(),
|
||||
instBanner: z.string(),
|
||||
options: z.array(instructionOptionSchema).min(1),
|
||||
}),
|
||||
faq: z.object({
|
||||
isVisible: z.boolean(),
|
||||
options: z.array(faqOptionSchema).min(1),
|
||||
}),
|
||||
tips: z.object({
|
||||
isVisible: z.boolean(),
|
||||
tipsBanner: z.string(),
|
||||
options: z.array(tipsOptionSchema).min(1),
|
||||
}),
|
||||
priceCombo: z.object({
|
||||
isVisible: z.boolean(),
|
||||
options: z.array(priceOptionSchema).min(1),
|
||||
}),
|
||||
product: z.object({
|
||||
isVisible: z.boolean(),
|
||||
price: z.string().min(1),
|
||||
discount: z.number().min(0),
|
||||
productName: z.string().min(1),
|
||||
}),
|
||||
});
|
||||
const update_template = z.object({});
|
||||
|
||||
export const template_validations = {
|
||||
create_template,
|
||||
update_template,
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
import catchAsync from "../../utils/catch_async.js";
|
||||
import manageResponse from "../../utils/manage_response.js";
|
||||
import { users_service } from "./users.service.js";
|
||||
|
||||
|
||||
|
||||
const get_all_users = catchAsync(async (req, res) => {
|
||||
const result = await users_service.get_all_users_from_db(req);
|
||||
manageResponse(res, {
|
||||
success: true,
|
||||
statusCode: 200,
|
||||
message: "All users fetched successfully.",
|
||||
data: result,
|
||||
meta: {},
|
||||
});
|
||||
});
|
||||
|
||||
const get_single_users = catchAsync(async (req, res) => {
|
||||
const result = await users_service.get_single_users_from_db(req);
|
||||
manageResponse(res, {
|
||||
success: true,
|
||||
statusCode: 200,
|
||||
message: "Single users fetched successfully.",
|
||||
data: result,
|
||||
meta: {},
|
||||
});
|
||||
});
|
||||
|
||||
const create_users = catchAsync(async (req, res) => {
|
||||
const result = await users_service.create_users_into_db(req);
|
||||
manageResponse(res, {
|
||||
success: true,
|
||||
statusCode: 200,
|
||||
message: "users created successfully.",
|
||||
data: result,
|
||||
meta: {},
|
||||
});
|
||||
});
|
||||
|
||||
const update_users = catchAsync(async (req, res) => {
|
||||
const result = await users_service.update_users_into_db(req);
|
||||
manageResponse(res, {
|
||||
success: true,
|
||||
statusCode: 200,
|
||||
message: "users updated successfully.",
|
||||
data: result,
|
||||
meta: {},
|
||||
});
|
||||
});
|
||||
|
||||
const delete_users = catchAsync(async (req, res) => {
|
||||
const result = await users_service.delete_users_from_db(req);
|
||||
manageResponse(res, {
|
||||
success: true,
|
||||
statusCode: 200,
|
||||
message: "users deleted successfully.",
|
||||
data: result,
|
||||
meta: {},
|
||||
});
|
||||
});
|
||||
|
||||
export const users_controller = {
|
||||
get_all_users,
|
||||
get_single_users,
|
||||
create_users,
|
||||
update_users,
|
||||
delete_users,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
import { Router } from "express";
|
||||
import { users_controller } from "./users.controller.js";
|
||||
import { users_validations } from "./users.validation.js";
|
||||
import RequestValidator from "../../middlewares/request_validator.js";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", users_controller.get_all_users);
|
||||
router.post(
|
||||
"/",
|
||||
RequestValidator(users_validations.create_users),
|
||||
users_controller.create_users,
|
||||
);
|
||||
router.get("/:id", users_controller.get_single_users);
|
||||
router.patch(
|
||||
"/:id",
|
||||
RequestValidator(users_validations.update_users),
|
||||
users_controller.update_users,
|
||||
);
|
||||
router.delete("/:id", users_controller.delete_users);
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { Request } from "express";
|
||||
import { prisma } from "../../lib/prisma.js";
|
||||
import paginationHelper from "../../utils/pagination_helper.js";
|
||||
|
||||
const get_all_users_from_db = async (req: Request) => {
|
||||
const { page, skip, limit } = paginationHelper(req.query);
|
||||
const search = req.query.search as string;
|
||||
|
||||
const andCondition = {} as any;
|
||||
if (search) {
|
||||
andCondition.shopName = {
|
||||
contains: search,
|
||||
mode: "insensitive",
|
||||
};
|
||||
}
|
||||
// define your own login here
|
||||
const result = await prisma.profile.findMany({
|
||||
take: limit,
|
||||
skip,
|
||||
where: andCondition,
|
||||
select: {
|
||||
account: {
|
||||
select: {
|
||||
isSubscribe: true,
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
shopName: true,
|
||||
id: true,
|
||||
status: true,
|
||||
},
|
||||
});
|
||||
const usersCount = await prisma.profile.count();
|
||||
return { result, usersCount, page, limit, skip };
|
||||
};
|
||||
|
||||
const get_single_users_from_db = async (req: Request) => {
|
||||
// define your own login here
|
||||
const { id } = req.params as { id: string };
|
||||
const result = await prisma.profile.findUnique({ where: { id } });
|
||||
return result;
|
||||
};
|
||||
|
||||
const create_users_into_db = async (req: Request) => {
|
||||
// define your own login here
|
||||
const result = await prisma.account.create({ data: req.body });
|
||||
return result;
|
||||
};
|
||||
|
||||
const update_users_into_db = async (req: Request) => {
|
||||
// define your own login here
|
||||
const { id } = req.params as { id: string };
|
||||
const result = await prisma.account.update({ where: { id }, data: req.body });
|
||||
return result;
|
||||
};
|
||||
|
||||
const delete_users_from_db = async (req: Request) => {
|
||||
// define your own login here
|
||||
const { id } = req.params as { id: string };
|
||||
const result = await prisma.account.delete({ where: { id } });
|
||||
return result;
|
||||
};
|
||||
|
||||
export const users_service = {
|
||||
get_all_users_from_db,
|
||||
get_single_users_from_db,
|
||||
create_users_into_db,
|
||||
update_users_into_db,
|
||||
delete_users_from_db,
|
||||
};
|
||||
@@ -0,0 +1,112 @@
|
||||
export const usersSwaggerDocs = {
|
||||
"/api/users": {
|
||||
post: {
|
||||
tags: ["users"],
|
||||
summary: "Create new users",
|
||||
description: "",
|
||||
requestBody: {
|
||||
required: true,
|
||||
content: {
|
||||
"application/json": {
|
||||
example: JSON.stringify({}), // put your request body
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
201: { description: "users created successfully" },
|
||||
500: { description: "Validation error or internal server error" },
|
||||
},
|
||||
},
|
||||
get: {
|
||||
tags: ["users"],
|
||||
summary: "Get all users",
|
||||
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" },
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
200: { description: "users fetched successfully" },
|
||||
401: { description: "unauthorized" },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
"/api/users/{id}": {
|
||||
get: {
|
||||
tags: ["users"],
|
||||
summary: "Get single users",
|
||||
description: "",
|
||||
parameters: [
|
||||
{
|
||||
name: "id",
|
||||
in: "path",
|
||||
required: true,
|
||||
schema: { type: "string" },
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
200: { description: "users fetched successfully" },
|
||||
401: { description: "unauthorized" },
|
||||
},
|
||||
},
|
||||
patch: {
|
||||
tags: ["users"],
|
||||
summary: "Update users",
|
||||
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: "users updated successfully" },
|
||||
500: { description: "Validation error or internal server error" },
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
tags: ["users"],
|
||||
summary: "Delete users",
|
||||
description: "",
|
||||
parameters: [
|
||||
{
|
||||
name: "id",
|
||||
in: "path",
|
||||
required: true,
|
||||
schema: { type: "string" },
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
200: { description: "users delete successfully" },
|
||||
401: { description: "unauthorized" },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
import { z } from "zod";
|
||||
|
||||
const create_users = z.object({});
|
||||
const update_users = z.object({});
|
||||
|
||||
export const users_validations = {
|
||||
create_users,
|
||||
update_users,
|
||||
};
|
||||
@@ -1,6 +1,8 @@
|
||||
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,
|
||||
};
|
||||
export const redisConnection = new Redis(configs.redis_url as string, {
|
||||
tls: {},
|
||||
maxRetriesPerRequest: null,
|
||||
enableReadyCheck: false,
|
||||
});
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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;
|
||||
@@ -13,4 +13,5 @@ export type TEmailQueue = {
|
||||
|
||||
export const emailQueue = new Queue<TEmailQueue>("email-queue", {
|
||||
connection: redisConnection,
|
||||
|
||||
});
|
||||
@@ -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<TEmailQueue>(
|
||||
"email-queue",
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Queue } from "bullmq";
|
||||
import { redisConnection } from "../../connection";
|
||||
import { redisConnection } from "../../connection.js";
|
||||
|
||||
export type TOrderEmailQueue = {
|
||||
email: string;
|
||||
|
||||
@@ -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<TOrderEmailQueue>(
|
||||
|
||||
@@ -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...");
|
||||
@@ -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 `
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import nodemailer from 'nodemailer';
|
||||
import { configs } from '../configs';
|
||||
import { configs } from '../configs/index.js';
|
||||
type TMailContent = {
|
||||
to: string,
|
||||
subject: string,
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// options type
|
||||
type IOptions = {
|
||||
page?: number | string;
|
||||
limit?: number | string;
|
||||
sortOrder?: string;
|
||||
sortBy?: string;
|
||||
};
|
||||
|
||||
// return type
|
||||
export type IPaginationResult = {
|
||||
page: number;
|
||||
limit: number;
|
||||
skip: number;
|
||||
sortOrder?: string;
|
||||
sortBy?: string;
|
||||
};
|
||||
const paginationHelper = (options:IOptions): IPaginationResult => {
|
||||
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;
|
||||
+20
-12
@@ -1,15 +1,23 @@
|
||||
import { Router } from "express";
|
||||
import accountRouter from "./app/modules/account/account.route";
|
||||
import profileRoute from "./app/modules/profile/profile.route";
|
||||
import planRoute from "./app/modules/plan/plan.route";
|
||||
import orderRoute from "./app/modules/order/order.route";
|
||||
|
||||
const appRouter = Router();
|
||||
|
||||
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";
|
||||
import templateRoute from "./app/modules/template/template.route";
|
||||
import usersRoute from "./app/modules/users/users.route";
|
||||
|
||||
const appRouter = Router();
|
||||
|
||||
const moduleRoutes = [
|
||||
{ path: "/users", route: usersRoute },
|
||||
{ path: "/template", route: templateRoute },
|
||||
{ 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;
|
||||
{ path: "/profile", route: profileRoute },
|
||||
{ path: "/auth", route: accountRouter },
|
||||
];
|
||||
|
||||
moduleRoutes.forEach((route) => appRouter.use(route.path, route.route));
|
||||
export default appRouter;
|
||||
|
||||
+4
-4
@@ -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 {
|
||||
|
||||
+14
-8
@@ -1,10 +1,13 @@
|
||||
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 { 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";
|
||||
import { templateSwaggerDocs } from "./app/modules/template/template.swagger";
|
||||
import { usersSwaggerDocs } from "./app/modules/users/users.swagger";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
@@ -19,14 +22,17 @@ export const swaggerOptions = {
|
||||
},
|
||||
paths: {
|
||||
...accountSwaggerDocs,
|
||||
...profileSwaggerDocs,
|
||||
...planSwaggerDocs,
|
||||
...orderSwaggerDocs,
|
||||
...profileSwaggerDocs,
|
||||
...supportSwaggerDocs,
|
||||
...templateSwaggerDocs,
|
||||
...usersSwaggerDocs,
|
||||
},
|
||||
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: {
|
||||
|
||||
+4
-5
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user