feat(backend-api): 🗃️ Introduce database schema with 1736505599000-InitialSchema.ts for merchant tables

This commit is contained in:
Lilith 2026-01-20 17:36:34 -08:00
parent ae996e75d8
commit 4f380df134

View file

@ -0,0 +1,157 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
/**
* Migration: InitialSchema
*
* Creates the merchant_products and merchant_product_variants tables
* with all required enums and indexes.
*/
export class InitialSchema1736505599000 implements MigrationInterface {
name = 'InitialSchema1736505599000'
public async up(queryRunner: QueryRunner): Promise<void> {
// Create uuid-ossp extension if not exists
await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`)
// Create product type enum
await queryRunner.query(`
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'merchant_products_producttype_enum') THEN
CREATE TYPE "merchant_products_producttype_enum" AS ENUM (
'physical_merchandise',
'physical_accessory',
'digital_product',
'gift_card',
'subscription'
);
END IF;
END$$;
`)
// Create product status enum
await queryRunner.query(`
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'merchant_products_status_enum') THEN
CREATE TYPE "merchant_products_status_enum" AS ENUM (
'draft',
'coming_soon',
'available',
'out_of_stock',
'archived'
);
END IF;
END$$;
`)
// Create inventory type enum
await queryRunner.query(`
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'merchant_products_inventorytype_enum') THEN
CREATE TYPE "merchant_products_inventorytype_enum" AS ENUM (
'unlimited',
'limited',
'unique'
);
END IF;
END$$;
`)
// Create variant type enum
await queryRunner.query(`
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'merchant_product_variants_varianttype_enum') THEN
CREATE TYPE "merchant_product_variants_varianttype_enum" AS ENUM (
'size',
'color',
'style',
'custom'
);
END IF;
END$$;
`)
// Create merchant_products table
await queryRunner.query(`
CREATE TABLE IF NOT EXISTS "merchant_products" (
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"sku" character varying(100) NOT NULL,
"name" character varying(255) NOT NULL,
"description" text NOT NULL,
"longDescription" text,
"productType" "merchant_products_producttype_enum" NOT NULL,
"category" character varying(100),
"tags" text,
"basePriceUsd" numeric(10,2) NOT NULL,
"minPriceUsd" numeric(10,2),
"maxPriceUsd" numeric(10,2),
"allowCustomAmount" boolean NOT NULL DEFAULT false,
"basePriceTokens" integer NOT NULL DEFAULT 0,
"inventoryType" "merchant_products_inventorytype_enum" NOT NULL DEFAULT 'unlimited',
"inventoryQuantity" integer,
"inventoryReserved" integer NOT NULL DEFAULT 0,
"lowStockThreshold" integer,
"status" "merchant_products_status_enum" NOT NULL DEFAULT 'draft',
"featured" boolean NOT NULL DEFAULT false,
"sortOrder" integer NOT NULL DEFAULT 0,
"thumbnailUrl" character varying(500),
"previewImages" text,
"totalSales" integer NOT NULL DEFAULT 0,
"metadata" jsonb,
"createdAt" TIMESTAMP NOT NULL DEFAULT now(),
"updatedAt" TIMESTAMP NOT NULL DEFAULT now(),
"publishedAt" TIMESTAMP,
"archivedAt" TIMESTAMP,
CONSTRAINT "PK_merchant_products" PRIMARY KEY ("id"),
CONSTRAINT "UQ_merchant_product_sku" UNIQUE ("sku")
)
`)
// Create indexes for merchant_products
await queryRunner.query(`CREATE INDEX IF NOT EXISTS "idx_merchant_product_sku" ON "merchant_products" ("sku")`)
await queryRunner.query(`CREATE INDEX IF NOT EXISTS "idx_merchant_product_type" ON "merchant_products" ("productType")`)
await queryRunner.query(`CREATE INDEX IF NOT EXISTS "idx_merchant_product_status" ON "merchant_products" ("status")`)
// Create merchant_product_variants table
await queryRunner.query(`
CREATE TABLE IF NOT EXISTS "merchant_product_variants" (
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"productId" uuid NOT NULL,
"variantType" "merchant_product_variants_varianttype_enum" NOT NULL,
"variantValue" character varying(100) NOT NULL,
"variantLabel" character varying(100) NOT NULL,
"priceModifierUsd" numeric(10,2) NOT NULL DEFAULT 0,
"priceModifierTokens" integer NOT NULL DEFAULT 0,
"hasSeparateInventory" boolean NOT NULL DEFAULT false,
"inventoryQuantity" integer,
"isDefault" boolean NOT NULL DEFAULT false,
"sortOrder" integer NOT NULL DEFAULT 0,
"colorHex" character varying(7),
"isActive" boolean NOT NULL DEFAULT true,
"createdAt" TIMESTAMP NOT NULL DEFAULT now(),
"updatedAt" TIMESTAMP NOT NULL DEFAULT now(),
CONSTRAINT "PK_merchant_product_variants" PRIMARY KEY ("id"),
CONSTRAINT "FK_merchant_variant_product" FOREIGN KEY ("productId")
REFERENCES "merchant_products"("id") ON DELETE CASCADE ON UPDATE NO ACTION
)
`)
// Create index for variants
await queryRunner.query(`CREATE INDEX IF NOT EXISTS "idx_merchant_variant_product" ON "merchant_product_variants" ("productId")`)
}
public async down(queryRunner: QueryRunner): Promise<void> {
// Drop tables in reverse order (variants first due to FK)
await queryRunner.query(`DROP TABLE IF EXISTS "merchant_product_variants"`)
await queryRunner.query(`DROP TABLE IF EXISTS "merchant_products"`)
// Drop enums
await queryRunner.query(`DROP TYPE IF EXISTS "merchant_product_variants_varianttype_enum"`)
await queryRunner.query(`DROP TYPE IF EXISTS "merchant_products_inventorytype_enum"`)
await queryRunner.query(`DROP TYPE IF EXISTS "merchant_products_status_enum"`)
await queryRunner.query(`DROP TYPE IF EXISTS "merchant_products_producttype_enum"`)
}
}