Initial commit: Mana Collection NestJS backend.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,361 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "UserRole" AS ENUM ('CUSTOMER', 'STORE_ADMIN', 'SUPER_ADMIN');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "StoreStatus" AS ENUM ('pending', 'active', 'rejected', 'suspended');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "OrderStatus" AS ENUM ('pending', 'confirmed', 'preparing', 'shipped', 'delivered', 'cancelled');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "OrderStoreStatus" AS ENUM ('pending', 'confirmed', 'preparing', 'shipped', 'delivered', 'cancelled');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "SmsOtpPurpose" AS ENUM ('register', 'login', 'resetPassword', 'storeRegister');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "User" (
|
||||
"id" TEXT NOT NULL,
|
||||
"firstName" TEXT NOT NULL,
|
||||
"lastName" TEXT NOT NULL,
|
||||
"cellNumber" TEXT NOT NULL,
|
||||
"passwordHash" TEXT NOT NULL,
|
||||
"email" TEXT,
|
||||
"instagram" TEXT,
|
||||
"about" TEXT,
|
||||
"avatarUrl" TEXT,
|
||||
"avatarKey" TEXT,
|
||||
"role" "UserRole" NOT NULL DEFAULT 'CUSTOMER',
|
||||
"disabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "UserAddress" (
|
||||
"id" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"label" TEXT NOT NULL DEFAULT '',
|
||||
"province" TEXT NOT NULL,
|
||||
"city" TEXT NOT NULL,
|
||||
"fullAddress" TEXT NOT NULL,
|
||||
"landline" TEXT,
|
||||
|
||||
CONSTRAINT "UserAddress_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 "SmsOtp" (
|
||||
"id" TEXT NOT NULL,
|
||||
"cellNumber" TEXT NOT NULL,
|
||||
"purpose" "SmsOtpPurpose" NOT NULL,
|
||||
"codeHash" TEXT NOT NULL,
|
||||
"payload" JSONB,
|
||||
"attempts" INTEGER NOT NULL DEFAULT 0,
|
||||
"expiresAt" TIMESTAMP(3) NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "SmsOtp_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Store" (
|
||||
"id" TEXT NOT NULL,
|
||||
"slug" TEXT NOT NULL,
|
||||
"nameFa" TEXT NOT NULL,
|
||||
"nameEn" TEXT NOT NULL DEFAULT '',
|
||||
"description" TEXT,
|
||||
"logoUrl" TEXT,
|
||||
"logoKey" TEXT,
|
||||
"status" "StoreStatus" NOT NULL DEFAULT 'pending',
|
||||
"ownerId" TEXT NOT NULL,
|
||||
"rejectionReason" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Store_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Category" (
|
||||
"id" TEXT NOT NULL,
|
||||
"storeId" TEXT NOT NULL,
|
||||
"parentId" TEXT,
|
||||
"nameFa" TEXT NOT NULL,
|
||||
"nameEn" TEXT NOT NULL DEFAULT '',
|
||||
"sortOrder" INTEGER NOT NULL DEFAULT 0,
|
||||
"colors" TEXT[] DEFAULT ARRAY[]::TEXT[],
|
||||
"sizes" TEXT[] DEFAULT ARRAY[]::TEXT[],
|
||||
"packages" TEXT[] DEFAULT ARRAY[]::TEXT[],
|
||||
|
||||
CONSTRAINT "Category_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Brand" (
|
||||
"id" TEXT NOT NULL,
|
||||
"storeId" TEXT NOT NULL,
|
||||
"nameFa" TEXT NOT NULL,
|
||||
"nameEn" TEXT NOT NULL DEFAULT '',
|
||||
"imageUrl" TEXT,
|
||||
"imageKey" TEXT,
|
||||
|
||||
CONSTRAINT "Brand_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Product" (
|
||||
"id" TEXT NOT NULL,
|
||||
"storeId" TEXT NOT NULL,
|
||||
"categoryId" TEXT,
|
||||
"brandId" TEXT,
|
||||
"nameFa" TEXT NOT NULL,
|
||||
"nameEn" TEXT NOT NULL DEFAULT '',
|
||||
"price" INTEGER NOT NULL DEFAULT 0,
|
||||
"discountPrice" INTEGER,
|
||||
"imageUrl" TEXT,
|
||||
"imageKey" TEXT,
|
||||
"tags" TEXT[] DEFAULT ARRAY[]::TEXT[],
|
||||
"published" BOOLEAN NOT NULL DEFAULT true,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Product_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ProductVariant" (
|
||||
"id" TEXT NOT NULL,
|
||||
"productId" TEXT NOT NULL,
|
||||
"colorId" TEXT NOT NULL DEFAULT '',
|
||||
"sizeId" TEXT NOT NULL DEFAULT '',
|
||||
"packageId" TEXT NOT NULL DEFAULT '',
|
||||
"price" INTEGER NOT NULL DEFAULT 0,
|
||||
"discountPrice" INTEGER,
|
||||
|
||||
CONSTRAINT "ProductVariant_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "BlogPost" (
|
||||
"id" TEXT NOT NULL,
|
||||
"storeId" TEXT NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"summary" TEXT NOT NULL DEFAULT '',
|
||||
"body" TEXT NOT NULL DEFAULT '',
|
||||
"author" TEXT NOT NULL DEFAULT '',
|
||||
"imageUrl" TEXT,
|
||||
"imageKey" TEXT,
|
||||
"published" BOOLEAN NOT NULL DEFAULT true,
|
||||
"metaTitle" TEXT,
|
||||
"metaDescription" TEXT,
|
||||
"slug" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "BlogPost_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Order" (
|
||||
"id" TEXT NOT NULL,
|
||||
"customerId" TEXT,
|
||||
"customerName" TEXT NOT NULL,
|
||||
"customerPhone" TEXT NOT NULL,
|
||||
"status" "OrderStatus" NOT NULL DEFAULT 'pending',
|
||||
"itemCount" INTEGER NOT NULL DEFAULT 0,
|
||||
"totalAmount" INTEGER NOT NULL DEFAULT 0,
|
||||
"note" TEXT NOT NULL DEFAULT '',
|
||||
"shippingAddress" JSONB,
|
||||
"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,
|
||||
"storeId" TEXT NOT NULL,
|
||||
"productId" TEXT,
|
||||
"nameFa" TEXT NOT NULL,
|
||||
"variantLabel" TEXT,
|
||||
"quantity" INTEGER NOT NULL DEFAULT 1,
|
||||
"unitPrice" INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
CONSTRAINT "OrderItem_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "OrderStoreFulfillment" (
|
||||
"id" TEXT NOT NULL,
|
||||
"orderId" TEXT NOT NULL,
|
||||
"storeId" TEXT NOT NULL,
|
||||
"status" "OrderStoreStatus" NOT NULL DEFAULT 'pending',
|
||||
|
||||
CONSTRAINT "OrderStoreFulfillment_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 "UserAddress_userId_idx" ON "UserAddress"("userId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "RefreshToken_userId_idx" ON "RefreshToken"("userId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "RefreshToken_tokenHash_idx" ON "RefreshToken"("tokenHash");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "SmsOtp_cellNumber_purpose_idx" ON "SmsOtp"("cellNumber", "purpose");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "SmsOtp_expiresAt_idx" ON "SmsOtp"("expiresAt");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Store_slug_key" ON "Store"("slug");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Store_ownerId_key" ON "Store"("ownerId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Store_status_idx" ON "Store"("status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Category_storeId_idx" ON "Category"("storeId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Category_parentId_idx" ON "Category"("parentId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Category_sortOrder_idx" ON "Category"("sortOrder");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Brand_storeId_idx" ON "Brand"("storeId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Product_storeId_idx" ON "Product"("storeId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Product_categoryId_idx" ON "Product"("categoryId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Product_brandId_idx" ON "Product"("brandId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Product_price_idx" ON "Product"("price");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Product_published_idx" ON "Product"("published");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ProductVariant_productId_idx" ON "ProductVariant"("productId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "BlogPost_storeId_idx" ON "BlogPost"("storeId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "BlogPost_published_idx" ON "BlogPost"("published");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "BlogPost_slug_idx" ON "BlogPost"("slug");
|
||||
|
||||
-- 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 "OrderItem_orderId_idx" ON "OrderItem"("orderId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "OrderItem_storeId_idx" ON "OrderItem"("storeId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "OrderItem_productId_idx" ON "OrderItem"("productId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "OrderStoreFulfillment_storeId_idx" ON "OrderStoreFulfillment"("storeId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "OrderStoreFulfillment_status_idx" ON "OrderStoreFulfillment"("status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "OrderStoreFulfillment_orderId_storeId_key" ON "OrderStoreFulfillment"("orderId", "storeId");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "UserAddress" ADD CONSTRAINT "UserAddress_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "RefreshToken" ADD CONSTRAINT "RefreshToken_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Store" ADD CONSTRAINT "Store_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Category" ADD CONSTRAINT "Category_storeId_fkey" FOREIGN KEY ("storeId") REFERENCES "Store"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Category" ADD CONSTRAINT "Category_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "Category"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Brand" ADD CONSTRAINT "Brand_storeId_fkey" FOREIGN KEY ("storeId") REFERENCES "Store"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Product" ADD CONSTRAINT "Product_storeId_fkey" FOREIGN KEY ("storeId") REFERENCES "Store"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Product" ADD CONSTRAINT "Product_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "Category"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Product" ADD CONSTRAINT "Product_brandId_fkey" FOREIGN KEY ("brandId") REFERENCES "Brand"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "ProductVariant" ADD CONSTRAINT "ProductVariant_productId_fkey" FOREIGN KEY ("productId") REFERENCES "Product"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "BlogPost" ADD CONSTRAINT "BlogPost_storeId_fkey" FOREIGN KEY ("storeId") REFERENCES "Store"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Order" ADD CONSTRAINT "Order_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "User"("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_storeId_fkey" FOREIGN KEY ("storeId") REFERENCES "Store"("id") ON DELETE RESTRICT 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 "OrderStoreFulfillment" ADD CONSTRAINT "OrderStoreFulfillment_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "Order"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "OrderStoreFulfillment" ADD CONSTRAINT "OrderStoreFulfillment_storeId_fkey" FOREIGN KEY ("storeId") REFERENCES "Store"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Product" ADD COLUMN IF NOT EXISTS "summary" TEXT NOT NULL DEFAULT '';
|
||||
ALTER TABLE "Product" ADD COLUMN IF NOT EXISTS "description" TEXT NOT NULL DEFAULT '';
|
||||
ALTER TABLE "Product" ADD COLUMN IF NOT EXISTS "galleryUrls" TEXT[] DEFAULT ARRAY[]::TEXT[];
|
||||
ALTER TABLE "Product" ADD COLUMN IF NOT EXISTS "galleryKeys" TEXT[] DEFAULT ARRAY[]::TEXT[];
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Prisma migration lock
|
||||
provider = "postgresql"
|
||||
@@ -0,0 +1,287 @@
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
enum UserRole {
|
||||
CUSTOMER
|
||||
STORE_ADMIN
|
||||
SUPER_ADMIN
|
||||
}
|
||||
|
||||
enum StoreStatus {
|
||||
pending
|
||||
active
|
||||
rejected
|
||||
suspended
|
||||
}
|
||||
|
||||
enum OrderStatus {
|
||||
pending
|
||||
confirmed
|
||||
preparing
|
||||
shipped
|
||||
delivered
|
||||
cancelled
|
||||
}
|
||||
|
||||
enum OrderStoreStatus {
|
||||
pending
|
||||
confirmed
|
||||
preparing
|
||||
shipped
|
||||
delivered
|
||||
cancelled
|
||||
}
|
||||
|
||||
enum SmsOtpPurpose {
|
||||
register
|
||||
login
|
||||
resetPassword
|
||||
storeRegister
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
firstName String
|
||||
lastName String
|
||||
cellNumber String @unique
|
||||
passwordHash String
|
||||
email String?
|
||||
instagram String?
|
||||
about String?
|
||||
avatarUrl String?
|
||||
avatarKey String?
|
||||
role UserRole @default(CUSTOMER)
|
||||
disabled Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
refreshTokens RefreshToken[]
|
||||
addresses UserAddress[]
|
||||
ownedStore Store?
|
||||
orders Order[] @relation("CustomerOrders")
|
||||
|
||||
@@index([role])
|
||||
@@index([disabled])
|
||||
}
|
||||
|
||||
model UserAddress {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
label String @default("")
|
||||
province String
|
||||
city String
|
||||
fullAddress String
|
||||
landline String?
|
||||
|
||||
@@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 SmsOtp {
|
||||
id String @id @default(cuid())
|
||||
cellNumber String
|
||||
purpose SmsOtpPurpose
|
||||
codeHash String
|
||||
payload Json?
|
||||
attempts Int @default(0)
|
||||
expiresAt DateTime
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([cellNumber, purpose])
|
||||
@@index([expiresAt])
|
||||
}
|
||||
|
||||
model Store {
|
||||
id String @id @default(cuid())
|
||||
slug String @unique
|
||||
nameFa String
|
||||
nameEn String @default("")
|
||||
description String?
|
||||
logoUrl String?
|
||||
logoKey String?
|
||||
status StoreStatus @default(pending)
|
||||
ownerId String @unique
|
||||
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
||||
rejectionReason String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
products Product[]
|
||||
categories Category[]
|
||||
brands Brand[]
|
||||
blogs BlogPost[]
|
||||
orderItems OrderItem[]
|
||||
fulfillments OrderStoreFulfillment[]
|
||||
|
||||
@@index([status])
|
||||
}
|
||||
|
||||
model Category {
|
||||
id String @id @default(cuid())
|
||||
storeId String
|
||||
store Store @relation(fields: [storeId], references: [id], onDelete: Cascade)
|
||||
parentId String?
|
||||
parent Category? @relation("CategoryTree", fields: [parentId], references: [id], onDelete: SetNull)
|
||||
children Category[] @relation("CategoryTree")
|
||||
nameFa String
|
||||
nameEn String @default("")
|
||||
sortOrder Int @default(0)
|
||||
colors String[] @default([])
|
||||
sizes String[] @default([])
|
||||
packages String[] @default([])
|
||||
products Product[]
|
||||
|
||||
@@index([storeId])
|
||||
@@index([parentId])
|
||||
@@index([sortOrder])
|
||||
}
|
||||
|
||||
model Brand {
|
||||
id String @id @default(cuid())
|
||||
storeId String
|
||||
store Store @relation(fields: [storeId], references: [id], onDelete: Cascade)
|
||||
nameFa String
|
||||
nameEn String @default("")
|
||||
imageUrl String?
|
||||
imageKey String?
|
||||
products Product[]
|
||||
|
||||
@@index([storeId])
|
||||
}
|
||||
|
||||
model Product {
|
||||
id String @id @default(cuid())
|
||||
storeId String
|
||||
store Store @relation(fields: [storeId], references: [id], onDelete: Cascade)
|
||||
categoryId String?
|
||||
category Category? @relation(fields: [categoryId], references: [id], onDelete: SetNull)
|
||||
brandId String?
|
||||
brand Brand? @relation(fields: [brandId], references: [id], onDelete: SetNull)
|
||||
nameFa String
|
||||
nameEn String @default("")
|
||||
summary String @default("")
|
||||
description String @default("")
|
||||
price Int @default(0)
|
||||
discountPrice Int?
|
||||
imageUrl String?
|
||||
imageKey String?
|
||||
galleryUrls String[] @default([])
|
||||
galleryKeys String[] @default([])
|
||||
tags String[] @default([])
|
||||
published Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
variants ProductVariant[]
|
||||
orderItems OrderItem[]
|
||||
|
||||
@@index([storeId])
|
||||
@@index([categoryId])
|
||||
@@index([brandId])
|
||||
@@index([price])
|
||||
@@index([published])
|
||||
}
|
||||
|
||||
model ProductVariant {
|
||||
id String @id @default(cuid())
|
||||
productId String
|
||||
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
|
||||
colorId String @default("")
|
||||
sizeId String @default("")
|
||||
packageId String @default("")
|
||||
price Int @default(0)
|
||||
discountPrice Int?
|
||||
|
||||
@@index([productId])
|
||||
}
|
||||
|
||||
model BlogPost {
|
||||
id String @id @default(cuid())
|
||||
storeId String
|
||||
store Store @relation(fields: [storeId], references: [id], onDelete: Cascade)
|
||||
title String
|
||||
summary String @default("")
|
||||
body String @default("")
|
||||
author String @default("")
|
||||
imageUrl String?
|
||||
imageKey String?
|
||||
published Boolean @default(true)
|
||||
metaTitle String?
|
||||
metaDescription String?
|
||||
slug String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([storeId])
|
||||
@@index([published])
|
||||
@@index([slug])
|
||||
}
|
||||
|
||||
model Order {
|
||||
id String @id @default(cuid())
|
||||
customerId String?
|
||||
customer User? @relation("CustomerOrders", fields: [customerId], references: [id], onDelete: SetNull)
|
||||
customerName String
|
||||
customerPhone String
|
||||
status OrderStatus @default(pending)
|
||||
itemCount Int @default(0)
|
||||
totalAmount Int @default(0)
|
||||
note String @default("")
|
||||
shippingAddress Json?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
items OrderItem[]
|
||||
fulfillments OrderStoreFulfillment[]
|
||||
|
||||
@@index([customerId])
|
||||
@@index([status])
|
||||
@@index([createdAt])
|
||||
}
|
||||
|
||||
model OrderItem {
|
||||
id String @id @default(cuid())
|
||||
orderId String
|
||||
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
|
||||
storeId String
|
||||
store Store @relation(fields: [storeId], references: [id], onDelete: Restrict)
|
||||
productId String?
|
||||
product Product? @relation(fields: [productId], references: [id], onDelete: SetNull)
|
||||
nameFa String
|
||||
variantLabel String?
|
||||
quantity Int @default(1)
|
||||
unitPrice Int @default(0)
|
||||
|
||||
@@index([orderId])
|
||||
@@index([storeId])
|
||||
@@index([productId])
|
||||
}
|
||||
|
||||
model OrderStoreFulfillment {
|
||||
id String @id @default(cuid())
|
||||
orderId String
|
||||
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
|
||||
storeId String
|
||||
store Store @relation(fields: [storeId], references: [id], onDelete: Restrict)
|
||||
status OrderStoreStatus @default(pending)
|
||||
|
||||
@@unique([orderId, storeId])
|
||||
@@index([storeId])
|
||||
@@index([status])
|
||||
}
|
||||
Reference in New Issue
Block a user