Add SMS OTP auth, discounts, customer orders, and category slugs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-08-05 15:15:10 +03:30
co-authored by Cursor
parent 58380ab81d
commit d09eca8702
48 changed files with 1719 additions and 38 deletions
@@ -0,0 +1,5 @@
-- AlterTable
ALTER TABLE "OrderItem" ALTER COLUMN "quantity" SET DATA TYPE DOUBLE PRECISION;
-- AlterTable
ALTER TABLE "Order" ALTER COLUMN "itemCount" SET DATA TYPE DOUBLE PRECISION;
@@ -0,0 +1,27 @@
-- Renumber existing low order numbers to start at 1000, then seed the sequence.
DO $$
DECLARE
seq_name text;
seed bigint;
r record;
i integer := 0;
BEGIN
-- Move current numbers out of the way to avoid unique collisions
UPDATE "Order" SET number = number + 1000000 WHERE number < 1000;
-- Assign contiguous numbers from 1000 for any that were shifted
FOR r IN
SELECT id FROM "Order" WHERE number >= 1000000 ORDER BY number ASC
LOOP
UPDATE "Order" SET number = 1000 + i WHERE id = r.id;
i := i + 1;
END LOOP;
seq_name := pg_get_serial_sequence('"Order"', 'number');
IF seq_name IS NULL THEN
RAISE EXCEPTION 'Order.number sequence not found';
END IF;
SELECT GREATEST(999, COALESCE(MAX(number), 999)) INTO seed FROM "Order";
PERFORM setval(seq_name, seed, true);
END $$;
@@ -0,0 +1,38 @@
-- CreateTable
CREATE TABLE "Discount" (
"id" TEXT NOT NULL,
"code" TEXT NOT NULL,
"categoryId" TEXT,
"userId" TEXT NOT NULL,
"minOrderAmount" INTEGER NOT NULL DEFAULT 0,
"expiresAt" TIMESTAMP(3) NOT NULL,
"percent" INTEGER NOT NULL,
"maxValue" INTEGER NOT NULL,
"active" BOOLEAN NOT NULL DEFAULT true,
"createdByAdminId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Discount_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "Discount_code_key" ON "Discount"("code");
-- CreateIndex
CREATE INDEX "Discount_userId_idx" ON "Discount"("userId");
-- CreateIndex
CREATE INDEX "Discount_categoryId_idx" ON "Discount"("categoryId");
-- CreateIndex
CREATE INDEX "Discount_active_expiresAt_idx" ON "Discount"("active", "expiresAt");
-- AddForeignKey
ALTER TABLE "Discount" ADD CONSTRAINT "Discount_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "Category"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Discount" ADD CONSTRAINT "Discount_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Discount" ADD CONSTRAINT "Discount_createdByAdminId_fkey" FOREIGN KEY ("createdByAdminId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Discount" ALTER COLUMN "userId" DROP NOT NULL;
@@ -0,0 +1,38 @@
-- AlterTable
ALTER TABLE "Category" ADD COLUMN "slug" TEXT;
-- Backfill from nameEn (latin slug). Keep unique with id suffix on collision.
UPDATE "Category" AS c
SET "slug" = lower(
regexp_replace(
regexp_replace(trim(c."nameEn"), '[^a-zA-Z0-9]+', '-', 'g'),
'(^-+|-+$)',
'',
'g'
)
);
UPDATE "Category"
SET "slug" = 'category'
WHERE "slug" IS NULL OR "slug" = '';
UPDATE "Category" AS c
SET "slug" = c."slug" || '-' || right(c."id", 4)
WHERE c."id" IN (
SELECT id FROM (
SELECT id, ROW_NUMBER() OVER (PARTITION BY slug ORDER BY "createdAt", id) AS rn
FROM "Category"
) d
WHERE rn > 1
);
ALTER TABLE "Category" ALTER COLUMN "slug" SET NOT NULL;
-- Prefer readable Persian-romanized top-level slugs
UPDATE "Category" SET "slug" = 'shirini' WHERE "nameFa" = 'شیرینی' AND "parentId" IS NULL;
UPDATE "Category" SET "slug" = 'cake' WHERE "nameFa" = 'کیک' AND "parentId" IS NULL;
UPDATE "Category" SET "slug" = 'shirini-tar' WHERE "nameFa" = 'شیرینی تر';
UPDATE "Category" SET "slug" = 'shirini-khoshk' WHERE "nameFa" = 'شیرینی خشک';
UPDATE "Category" SET "slug" = 'cream-cake' WHERE "nameFa" = 'کیک خامه';
CREATE UNIQUE INDEX "Category_slug_key" ON "Category"("slug");
@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "Order" ADD COLUMN "discountCode" TEXT;
ALTER TABLE "Order" ADD COLUMN "discountAmount" INTEGER NOT NULL DEFAULT 0;
@@ -0,0 +1,22 @@
-- CreateEnum
CREATE TYPE "SmsOtpPurpose" AS ENUM ('register');
-- 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")
);
-- CreateIndex
CREATE INDEX "SmsOtp_cellNumber_purpose_idx" ON "SmsOtp"("cellNumber", "purpose");
-- CreateIndex
CREATE INDEX "SmsOtp_expiresAt_idx" ON "SmsOtp"("expiresAt");
@@ -0,0 +1,3 @@
-- AlterEnum
ALTER TYPE "SmsOtpPurpose" ADD VALUE 'login';
ALTER TYPE "SmsOtpPurpose" ADD VALUE 'resetPassword';
+2
View File
@@ -1 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
provider = "postgresql"