Initial commit of Balout Pastry NestJS API.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-08-02 17:21:34 +03:30
co-authored by Cursor
commit f40075fd5e
84 changed files with 15435 additions and 0 deletions
@@ -0,0 +1,219 @@
-- CreateSchema
CREATE SCHEMA IF NOT EXISTS "public";
-- CreateEnum
CREATE TYPE "UserRole" AS ENUM ('customer', 'admin', 'superAdmin');
-- CreateEnum
CREATE TYPE "SellUnit" AS ENUM ('unit', 'kilo');
-- CreateTable
CREATE TABLE "User" (
"id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"firstName" TEXT NOT NULL,
"lastName" TEXT NOT NULL,
"category" TEXT,
"cellNumber" TEXT NOT NULL,
"passwordHash" TEXT NOT NULL,
"role" "UserRole" NOT NULL DEFAULT 'customer',
"disabled" BOOLEAN NOT NULL DEFAULT false,
"totalOrders" INTEGER NOT NULL DEFAULT 0,
"totalTransaction" INTEGER NOT NULL DEFAULT 0,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "RefreshToken" (
"id" TEXT NOT NULL,
"tokenHash" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"expiresAt" TIMESTAMP(3) NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "RefreshToken_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Flavor" (
"id" TEXT NOT NULL,
"nameFa" TEXT NOT NULL,
"nameEn" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Flavor_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Category" (
"id" TEXT NOT NULL,
"nameFa" TEXT NOT NULL,
"nameEn" TEXT NOT NULL,
"parentId" TEXT,
"sortOrder" INTEGER NOT NULL DEFAULT 0,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Category_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "CategoryOptionBlock" (
"id" TEXT NOT NULL,
"categoryId" TEXT NOT NULL,
"flavorId" TEXT NOT NULL,
"sortOrder" INTEGER NOT NULL DEFAULT 0,
CONSTRAINT "CategoryOptionBlock_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "CategoryOptionEntry" (
"id" TEXT NOT NULL,
"blockId" TEXT NOT NULL,
"amount" TEXT NOT NULL,
"price" INTEGER NOT NULL,
"sortOrder" INTEGER NOT NULL DEFAULT 0,
CONSTRAINT "CategoryOptionEntry_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Product" (
"id" TEXT NOT NULL,
"nameFa" TEXT NOT NULL,
"nameEn" TEXT NOT NULL,
"price" INTEGER NOT NULL DEFAULT 0,
"sellUnit" "SellUnit" NOT NULL DEFAULT 'unit',
"categoryId" TEXT NOT NULL,
"intro" TEXT NOT NULL DEFAULT '',
"description" TEXT NOT NULL DEFAULT '',
"mainImageUrl" TEXT,
"mainImageKey" TEXT,
"tags" TEXT[] DEFAULT ARRAY[]::TEXT[],
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Product_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ProductGalleryImage" (
"id" TEXT NOT NULL,
"productId" TEXT NOT NULL,
"url" TEXT NOT NULL,
"storageKey" TEXT NOT NULL,
"sortOrder" INTEGER NOT NULL DEFAULT 0,
CONSTRAINT "ProductGalleryImage_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ProductOption" (
"id" TEXT NOT NULL,
"productId" TEXT NOT NULL,
"flavorId" TEXT NOT NULL,
"amount" TEXT NOT NULL,
"price" INTEGER NOT NULL,
CONSTRAINT "ProductOption_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ShippingException" (
"id" TEXT NOT NULL,
"district" TEXT NOT NULL,
"price" INTEGER NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ShippingException_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Branch" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"district" TEXT NOT NULL,
"address" TEXT NOT NULL,
"landline" TEXT NOT NULL DEFAULT '',
"cellNumber" TEXT NOT NULL DEFAULT '',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Branch_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "User_cellNumber_key" ON "User"("cellNumber");
-- CreateIndex
CREATE INDEX "User_role_idx" ON "User"("role");
-- CreateIndex
CREATE INDEX "User_disabled_idx" ON "User"("disabled");
-- CreateIndex
CREATE INDEX "RefreshToken_userId_idx" ON "RefreshToken"("userId");
-- CreateIndex
CREATE INDEX "RefreshToken_tokenHash_idx" ON "RefreshToken"("tokenHash");
-- CreateIndex
CREATE INDEX "Category_parentId_idx" ON "Category"("parentId");
-- CreateIndex
CREATE INDEX "CategoryOptionBlock_categoryId_idx" ON "CategoryOptionBlock"("categoryId");
-- CreateIndex
CREATE INDEX "CategoryOptionBlock_flavorId_idx" ON "CategoryOptionBlock"("flavorId");
-- CreateIndex
CREATE INDEX "CategoryOptionEntry_blockId_idx" ON "CategoryOptionEntry"("blockId");
-- CreateIndex
CREATE INDEX "Product_categoryId_idx" ON "Product"("categoryId");
-- CreateIndex
CREATE INDEX "ProductGalleryImage_productId_idx" ON "ProductGalleryImage"("productId");
-- CreateIndex
CREATE INDEX "ProductOption_productId_idx" ON "ProductOption"("productId");
-- CreateIndex
CREATE INDEX "ProductOption_flavorId_idx" ON "ProductOption"("flavorId");
-- CreateIndex
CREATE UNIQUE INDEX "ShippingException_district_key" ON "ShippingException"("district");
-- AddForeignKey
ALTER TABLE "RefreshToken" ADD CONSTRAINT "RefreshToken_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Category" ADD CONSTRAINT "Category_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "Category"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "CategoryOptionBlock" ADD CONSTRAINT "CategoryOptionBlock_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "Category"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "CategoryOptionBlock" ADD CONSTRAINT "CategoryOptionBlock_flavorId_fkey" FOREIGN KEY ("flavorId") REFERENCES "Flavor"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "CategoryOptionEntry" ADD CONSTRAINT "CategoryOptionEntry_blockId_fkey" FOREIGN KEY ("blockId") REFERENCES "CategoryOptionBlock"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Product" ADD CONSTRAINT "Product_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "Category"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ProductGalleryImage" ADD CONSTRAINT "ProductGalleryImage_productId_fkey" FOREIGN KEY ("productId") REFERENCES "Product"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ProductOption" ADD CONSTRAINT "ProductOption_productId_fkey" FOREIGN KEY ("productId") REFERENCES "Product"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ProductOption" ADD CONSTRAINT "ProductOption_flavorId_fkey" FOREIGN KEY ("flavorId") REFERENCES "Flavor"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
@@ -0,0 +1,117 @@
-- CreateEnum
CREATE TYPE "OrderStatus" AS ENUM ('pending', 'confirmed', 'preparing', 'ready', 'delivered', 'cancelled');
-- CreateEnum
CREATE TYPE "DeliveryType" AS ENUM ('shipping', 'pickup');
-- AlterTable
CREATE INDEX "Product_price_idx" ON "Product"("price");
-- CreateTable
CREATE TABLE "UserAddress" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"name" TEXT NOT NULL,
"district" TEXT NOT NULL,
"address" TEXT NOT NULL,
"landline" TEXT NOT NULL DEFAULT '',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "UserAddress_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Order" (
"id" TEXT NOT NULL,
"number" SERIAL NOT NULL,
"customerId" TEXT NOT NULL,
"customerName" TEXT NOT NULL,
"customerPhone" TEXT NOT NULL,
"status" "OrderStatus" NOT NULL DEFAULT 'pending',
"deliveryType" "DeliveryType" NOT NULL,
"branchId" TEXT,
"branchName" TEXT,
"shippingAddressId" TEXT,
"shippingName" TEXT,
"shippingDistrict" TEXT,
"shippingAddressLine" TEXT,
"shippingLandline" TEXT,
"note" TEXT NOT NULL DEFAULT '',
"itemCount" INTEGER NOT NULL,
"totalPrice" INTEGER NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Order_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "OrderItem" (
"id" TEXT NOT NULL,
"orderId" TEXT NOT NULL,
"productId" TEXT,
"nameFa" TEXT NOT NULL,
"quantity" INTEGER NOT NULL,
"unitPrice" INTEGER NOT NULL,
"sellUnit" "SellUnit" NOT NULL,
"sortOrder" INTEGER NOT NULL DEFAULT 0,
CONSTRAINT "OrderItem_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "OrderItemOption" (
"id" TEXT NOT NULL,
"orderItemId" TEXT NOT NULL,
"name" TEXT NOT NULL,
"price" INTEGER NOT NULL,
"sortOrder" INTEGER NOT NULL DEFAULT 0,
CONSTRAINT "OrderItemOption_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "UserAddress_userId_idx" ON "UserAddress"("userId");
-- CreateIndex
CREATE UNIQUE INDEX "Order_number_key" ON "Order"("number");
-- CreateIndex
CREATE INDEX "Order_customerId_idx" ON "Order"("customerId");
-- CreateIndex
CREATE INDEX "Order_status_idx" ON "Order"("status");
-- CreateIndex
CREATE INDEX "Order_createdAt_idx" ON "Order"("createdAt");
-- CreateIndex
CREATE INDEX "Order_branchId_idx" ON "Order"("branchId");
-- CreateIndex
CREATE INDEX "OrderItem_orderId_idx" ON "OrderItem"("orderId");
-- CreateIndex
CREATE INDEX "OrderItem_productId_idx" ON "OrderItem"("productId");
-- CreateIndex
CREATE INDEX "OrderItemOption_orderItemId_idx" ON "OrderItemOption"("orderItemId");
-- AddForeignKey
ALTER TABLE "UserAddress" ADD CONSTRAINT "UserAddress_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Order" ADD CONSTRAINT "Order_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Order" ADD CONSTRAINT "Order_branchId_fkey" FOREIGN KEY ("branchId") REFERENCES "Branch"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "OrderItem" ADD CONSTRAINT "OrderItem_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "Order"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "OrderItem" ADD CONSTRAINT "OrderItem_productId_fkey" FOREIGN KEY ("productId") REFERENCES "Product"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "OrderItemOption" ADD CONSTRAINT "OrderItemOption_orderItemId_fkey" FOREIGN KEY ("orderItemId") REFERENCES "OrderItem"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+1
View File
@@ -0,0 +1 @@
provider = "postgresql"
+256
View File
@@ -0,0 +1,256 @@
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
enum UserRole {
customer
admin
superAdmin
}
enum SellUnit {
unit
kilo
}
enum OrderStatus {
pending
confirmed
preparing
ready
delivered
cancelled
}
enum DeliveryType {
shipping
pickup
}
model User {
id String @id @default(cuid())
title String
firstName String
lastName String
category String?
cellNumber String @unique
passwordHash String
role UserRole @default(customer)
disabled Boolean @default(false)
totalOrders Int @default(0)
totalTransaction Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
refreshTokens RefreshToken[]
addresses UserAddress[]
orders Order[]
@@index([role])
@@index([disabled])
}
model UserAddress {
id String @id @default(cuid())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
name String
district String
address String
landline String @default("")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
}
model RefreshToken {
id String @id @default(cuid())
tokenHash String
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
expiresAt DateTime
createdAt DateTime @default(now())
@@index([userId])
@@index([tokenHash])
}
model Flavor {
id String @id @default(cuid())
nameFa String
nameEn String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
categoryOptionBlocks CategoryOptionBlock[]
productOptions ProductOption[]
}
model Category {
id String @id @default(cuid())
nameFa String
nameEn String
parentId String?
parent Category? @relation("CategoryTree", fields: [parentId], references: [id], onDelete: Restrict)
children Category[] @relation("CategoryTree")
sortOrder Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
optionBlocks CategoryOptionBlock[]
products Product[]
@@index([parentId])
}
model CategoryOptionBlock {
id String @id @default(cuid())
categoryId String
flavorId String
sortOrder Int @default(0)
category Category @relation(fields: [categoryId], references: [id], onDelete: Cascade)
flavor Flavor @relation(fields: [flavorId], references: [id], onDelete: Restrict)
entries CategoryOptionEntry[]
@@index([categoryId])
@@index([flavorId])
}
model CategoryOptionEntry {
id String @id @default(cuid())
blockId String
amount String
price Int
sortOrder Int @default(0)
block CategoryOptionBlock @relation(fields: [blockId], references: [id], onDelete: Cascade)
@@index([blockId])
}
model Product {
id String @id @default(cuid())
nameFa String
nameEn String
price Int @default(0)
sellUnit SellUnit @default(unit)
categoryId String
category Category @relation(fields: [categoryId], references: [id], onDelete: Restrict)
intro String @default("")
description String @default("")
mainImageUrl String?
mainImageKey String?
tags String[] @default([])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
gallery ProductGalleryImage[]
options ProductOption[]
orderItems OrderItem[]
@@index([categoryId])
@@index([price])
}
model ProductGalleryImage {
id String @id @default(cuid())
productId String
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
url String
storageKey String
sortOrder Int @default(0)
@@index([productId])
}
model ProductOption {
id String @id @default(cuid())
productId String
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
flavorId String
flavor Flavor @relation(fields: [flavorId], references: [id], onDelete: Restrict)
amount String
price Int
@@index([productId])
@@index([flavorId])
}
model ShippingException {
id String @id @default(cuid())
district String @unique
price Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Branch {
id String @id @default(cuid())
name String
district String
address String
landline String @default("")
cellNumber String @default("")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
orders Order[]
}
model Order {
id String @id @default(cuid())
number Int @unique @default(autoincrement())
customerId String
customer User @relation(fields: [customerId], references: [id], onDelete: Restrict)
customerName String
customerPhone String
status OrderStatus @default(pending)
deliveryType DeliveryType
branchId String?
branch Branch? @relation(fields: [branchId], references: [id], onDelete: SetNull)
branchName String?
shippingAddressId String?
shippingName String?
shippingDistrict String?
shippingAddressLine String?
shippingLandline String?
note String @default("")
itemCount Int
totalPrice Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
items OrderItem[]
@@index([customerId])
@@index([status])
@@index([createdAt])
@@index([branchId])
}
model OrderItem {
id String @id @default(cuid())
orderId String
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
productId String?
product Product? @relation(fields: [productId], references: [id], onDelete: SetNull)
nameFa String
quantity Int
unitPrice Int
sellUnit SellUnit
sortOrder Int @default(0)
options OrderItemOption[]
@@index([orderId])
@@index([productId])
}
model OrderItemOption {
id String @id @default(cuid())
orderItemId String
orderItem OrderItem @relation(fields: [orderItemId], references: [id], onDelete: Cascade)
name String
price Int
sortOrder Int @default(0)
@@index([orderItemId])
}