diff --git a/apps/api/__tests__/integration/trpc/catalog-fan-out.test.ts b/apps/api/__tests__/integration/trpc/catalog-fan-out.test.ts index 17fa44ea..4c228739 100644 --- a/apps/api/__tests__/integration/trpc/catalog-fan-out.test.ts +++ b/apps/api/__tests__/integration/trpc/catalog-fan-out.test.ts @@ -1,53 +1,28 @@ /** - * Integration Tests: Catalog Router Fan-Out + * Integration Tests: Catalog Router Dirty Marking * - * Verifies that catalog deletes enqueue background fan-out jobs with the - * affected published product IDs captured before destructive FK updates. + * Verifies that catalog mutations mark affected published passports dirty + * inline instead of queueing a background fan-out task. */ // Load setup first (loads .env.test and configures cleanup) import "../../setup"; -import { beforeEach, describe, expect, it, mock } from "bun:test"; +import { beforeEach, describe, expect, it } from "bun:test"; +import { createPassportForVariant } from "@v1/db/queries/products"; import * as schema from "@v1/db/schema"; import { createTestBrand, createTestUser, testDb } from "@v1/db/testing"; -import { eq } from "drizzle-orm"; +import { eq, inArray } from "drizzle-orm"; import type { AuthenticatedTRPCContext } from "../../../src/trpc/init"; +import { catalogRouter } from "../../../src/trpc/routers/catalog"; -type TriggerCall = { - id: string; - payload: { - brandId: string; - entityType: string; - entityId: string; - productIds?: string[]; - }; - options?: { - concurrencyKey?: string; - delay?: string; - }; +type PassportFixture = { + productId: string; + variantId: string; + passportId: string; }; -const triggerCalls: TriggerCall[] = []; - -const triggerMock = mock( - async ( - id: string, - payload: TriggerCall["payload"], - options?: TriggerCall["options"], - ) => { - triggerCalls.push({ id, payload, options }); - return { id: `run_${triggerCalls.length}` } as const; - }, -); - -mock.module("@trigger.dev/sdk/v3", () => ({ - tasks: { - trigger: triggerMock, - }, -})); - -import { catalogRouter } from "../../../src/trpc/routers/catalog"; +type ProductStatus = "published" | "scheduled" | "unpublished"; /** * Build a stable short suffix for test record names. @@ -107,9 +82,9 @@ async function createProduct(options: { brandId: string; manufacturerId?: string | null; name: string; - status: "published" | "scheduled" | "unpublished"; + status: ProductStatus; }): Promise { - // Seed a product row that can participate in catalog fan-out lookups. + // Seed a product row that can participate in catalog dirty-marking lookups. const productId = crypto.randomUUID(); await testDb.insert(schema.products).values({ @@ -127,35 +102,136 @@ async function createProduct(options: { /** * Insert a variant for the supplied product. */ -async function createVariant(productId: string): Promise { - // Create a variant row for variant-level material fan-out paths. +async function createVariant(productId: string): Promise<{ + id: string; + upid: string; + sku: string; + barcode: string; +}> { + // Create a variant row plus identifiers that can be copied onto the passport. const variantId = crypto.randomUUID(); + const upid = `UPID-${randomSuffix()}`; + const sku = `SKU-${randomSuffix()}`; + const barcode = `BARCODE-${randomSuffix()}`; await testDb.insert(schema.productVariants).values({ id: variantId, productId, - sku: `SKU-${randomSuffix()}`, - upid: `UPID-${randomSuffix()}`, + sku, + barcode, + upid, }); - return variantId; + return { + id: variantId, + upid, + sku, + barcode, + }; +} + +/** + * Create a product, variant, and passport fixture in one helper call. + */ +async function createPassportFixture(options: { + brandId: string; + manufacturerId?: string | null; + name: string; + status: ProductStatus; +}): Promise { + // Keep the fixture setup concise for each catalog dirty-marking test. + const productId = await createProduct(options); + const variant = await createVariant(productId); + const passport = await createPassportForVariant( + testDb, + variant.id, + options.brandId, + { + upid: variant.upid, + sku: variant.sku, + barcode: variant.barcode, + }, + ); + + if (!passport) { + throw new Error("Failed to create passport fixture"); + } + + return { + productId, + variantId: variant.id, + passportId: passport.id, + }; +} + +/** + * Load dirty-flag state for a set of passports. + */ +async function listPassportDirtyStates(passportIds: string[]) { + // Read the current dirty flags after a catalog mutation completes. + return testDb + .select({ + id: schema.productPassports.id, + dirty: schema.productPassports.dirty, + }) + .from(schema.productPassports) + .where(inArray(schema.productPassports.id, passportIds)); } -describe("Catalog Router Fan-Out", () => { +describe("Catalog Router Dirty Marking", () => { let brandId: string; let userEmail: string; let userId: string; beforeEach(async () => { - // Reset the queued trigger calls for each test case. - triggerCalls.length = 0; - - brandId = await createTestBrand("Catalog Fan-Out Router Brand"); - userEmail = `catalog-fan-out-${randomSuffix()}@example.com`; + // Create a fresh brand-scoped caller for each test case. + brandId = await createTestBrand("Catalog Dirty Router Brand"); + userEmail = `catalog-dirty-${randomSuffix()}@example.com`; userId = await createTestUser(userEmail); await createBrandMembership(brandId, userId); }); + it("marks published manufacturer-linked passports dirty when updating a manufacturer", async () => { + // Update a manufacturer and ensure only published linked products are dirtied. + const manufacturerId = crypto.randomUUID(); + await testDb.insert(schema.brandManufacturers).values({ + id: manufacturerId, + brandId, + name: `Manufacturer ${randomSuffix()}`, + }); + + const publishedFixture = await createPassportFixture({ + brandId, + manufacturerId, + name: "Published Manufacturer Product", + status: "published", + }); + const unpublishedFixture = await createPassportFixture({ + brandId, + manufacturerId, + name: "Unpublished Manufacturer Product", + status: "unpublished", + }); + + const ctx = createMockContext({ brandId, userEmail, userId }); + await catalogRouter.createCaller(ctx).manufacturers.update({ + id: manufacturerId, + name: `Updated Manufacturer ${randomSuffix()}`, + }); + + const dirtyRows = await listPassportDirtyStates([ + publishedFixture.passportId, + unpublishedFixture.passportId, + ]); + + expect( + dirtyRows.find((row) => row.id === publishedFixture.passportId)?.dirty, + ).toBe(true); + expect( + dirtyRows.find((row) => row.id === unpublishedFixture.passportId)?.dirty, + ).toBe(false); + }); + it("captures affected published products before deleting a manufacturer", async () => { // Delete a manufacturer after linking it to both published and unpublished products. const manufacturerId = crypto.randomUUID(); @@ -165,13 +241,13 @@ describe("Catalog Router Fan-Out", () => { name: `Manufacturer ${randomSuffix()}`, }); - const publishedProductId = await createProduct({ + const publishedFixture = await createPassportFixture({ brandId, manufacturerId, name: "Published Manufacturer Product", status: "published", }); - await createProduct({ + const unpublishedFixture = await createPassportFixture({ brandId, manufacturerId, name: "Unpublished Manufacturer Product", @@ -183,27 +259,24 @@ describe("Catalog Router Fan-Out", () => { id: manufacturerId, }); - expect(triggerCalls).toHaveLength(1); - expect(triggerCalls[0]).toEqual({ - id: "catalog-fan-out", - payload: { - brandId, - entityType: "manufacturer", - entityId: manufacturerId, - productIds: [publishedProductId], - }, - options: { - concurrencyKey: brandId, - delay: "45s", - }, - }); + const dirtyRows = await listPassportDirtyStates([ + publishedFixture.passportId, + unpublishedFixture.passportId, + ]); + + expect( + dirtyRows.find((row) => row.id === publishedFixture.passportId)?.dirty, + ).toBe(true); + expect( + dirtyRows.find((row) => row.id === unpublishedFixture.passportId)?.dirty, + ).toBe(false); - const [product] = await testDb + const [publishedProduct] = await testDb .select({ manufacturerId: schema.products.manufacturerId }) .from(schema.products) - .where(eq(schema.products.id, publishedProductId)); + .where(eq(schema.products.id, publishedFixture.productId)); - expect(product?.manufacturerId).toBeNull(); + expect(publishedProduct?.manufacturerId).toBeNull(); }); it("captures published product and variant material references before deleting a certification", async () => { @@ -233,17 +306,17 @@ describe("Catalog Router Fan-Out", () => { }, ]); - const productLinkedProductId = await createProduct({ + const productLinkedFixture = await createPassportFixture({ brandId, name: "Published Product Material Product", status: "published", }); - const variantLinkedProductId = await createProduct({ + const variantLinkedFixture = await createPassportFixture({ brandId, name: "Published Variant Material Product", status: "published", }); - const unpublishedProductId = await createProduct({ + const unpublishedFixture = await createPassportFixture({ brandId, name: "Unpublished Certification Product", status: "unpublished", @@ -251,18 +324,17 @@ describe("Catalog Router Fan-Out", () => { await testDb.insert(schema.productMaterials).values([ { - productId: productLinkedProductId, + productId: productLinkedFixture.productId, brandMaterialId: productMaterialId, }, { - productId: unpublishedProductId, + productId: unpublishedFixture.productId, brandMaterialId: productMaterialId, }, ]); - const variantId = await createVariant(variantLinkedProductId); await testDb.insert(schema.variantMaterials).values({ - variantId, + variantId: variantLinkedFixture.variantId, brandMaterialId: variantMaterialId, }); @@ -271,24 +343,21 @@ describe("Catalog Router Fan-Out", () => { id: certificationId, }); - expect(triggerCalls).toHaveLength(1); + const dirtyRows = await listPassportDirtyStates([ + productLinkedFixture.passportId, + variantLinkedFixture.passportId, + unpublishedFixture.passportId, + ]); - const queuedProductIds = [...(triggerCalls[0]?.payload.productIds ?? [])].sort(); - expect(triggerCalls[0]).toMatchObject({ - id: "catalog-fan-out", - payload: { - brandId, - entityType: "certification", - entityId: certificationId, - }, - options: { - concurrencyKey: brandId, - delay: "45s", - }, - }); - expect(queuedProductIds).toEqual( - [productLinkedProductId, variantLinkedProductId].sort(), - ); + expect( + dirtyRows.find((row) => row.id === productLinkedFixture.passportId)?.dirty, + ).toBe(true); + expect( + dirtyRows.find((row) => row.id === variantLinkedFixture.passportId)?.dirty, + ).toBe(true); + expect( + dirtyRows.find((row) => row.id === unpublishedFixture.passportId)?.dirty, + ).toBe(false); const materials = await testDb .select({ @@ -296,7 +365,9 @@ describe("Catalog Router Fan-Out", () => { id: schema.brandMaterials.id, }) .from(schema.brandMaterials) - .where(eq(schema.brandMaterials.brandId, brandId)); + .where( + inArray(schema.brandMaterials.id, [productMaterialId, variantMaterialId]), + ); expect(materials).toEqual( expect.arrayContaining([ diff --git a/apps/api/src/trpc/routers/catalog/index.ts b/apps/api/src/trpc/routers/catalog/index.ts index 40f7e7c3..574cc659 100644 --- a/apps/api/src/trpc/routers/catalog/index.ts +++ b/apps/api/src/trpc/routers/catalog/index.ts @@ -14,7 +14,6 @@ * All endpoints follow a consistent CRUD pattern using shared helper * functions to minimize code duplication and ensure uniform error handling. */ -import { tasks } from "@trigger.dev/sdk/v3"; import type { Database } from "@v1/db/client"; import { batchCreateBrandAttributeValues, @@ -57,6 +56,9 @@ import { import { findPublishedProductIdsByCertification, findPublishedProductIdsByManufacturer, + findPublishedProductIdsByMaterial, + findPublishedProductIdsByOperator, + markPassportsDirtyByProductIds, } from "@v1/db/queries/products"; import { batchCreateBrandAttributeValuesSchema, @@ -122,21 +124,15 @@ import { /** tRPC context with guaranteed brand ID from middleware */ type BrandContext = AuthenticatedTRPCContext & { brandId: string }; -type CatalogFanOutEntityType = - | "manufacturer" - | "material" - | "certification" - | "operator"; - -type CatalogDeleteProductIdsResolver = ( +type CatalogDirtyProductIdsResolver = ( db: Database, brandId: string, entityId: string, ) => Promise; -type CatalogFanOutConfig = { - entityType: CatalogFanOutEntityType; - resolveDeleteProductIds?: CatalogDeleteProductIdsResolver; +type CatalogDirtyConfig = { + resolveProductIds: CatalogDirtyProductIdsResolver; + resolveDeleteProductIds?: CatalogDirtyProductIdsResolver; }; type CreateProcedureOptions = { @@ -168,6 +164,25 @@ type DeleteProcedureOptions Promise | void; }; +/** + * Mark published passports dirty for a catalog mutation result. + */ +async function markCatalogProductsDirty( + brandCtx: BrandContext, + productIds: string[] | undefined, +): Promise { + // Skip empty result sets so unrelated catalog edits stay cheap. + if (!productIds || productIds.length === 0) { + return; + } + + await markPassportsDirtyByProductIds( + brandCtx.db, + brandCtx.brandId, + productIds, + ); +} + /** * Creates a standardized list procedure for brand catalog resources. * @@ -359,45 +374,6 @@ function createDeleteProcedure< }); } -/** - * Enqueue a catalog fan-out job for the given entity. - * - * Fire-and-forget: trigger failures are logged but do not propagate, - * since fan-out is a background concern and should never fail a catalog write. - * - * Uses a 45-second delay so that rapid consecutive edits within the same window - * arrive at the task with the latest data. Multiple triggers within the window - * will each schedule a run, but publish is content-hash-deduplicated so only - * genuine content changes produce new versions (redundant runs are no-ops). - */ -function enqueueCatalogFanOut( - brandId: string, - entityType: CatalogFanOutEntityType, - entityId: string, - options?: { - productIds?: string[]; - }, -): void { - // Schedule the fan-out on a per-brand queue so related writes serialize. - tasks - .trigger( - "catalog-fan-out", - { - brandId, - entityType, - entityId, - productIds: options?.productIds, - }, - { delay: "45s", concurrencyKey: brandId }, - ) - .catch((err) => { - console.error( - `[CatalogFanOut] Failed to enqueue fan-out for ${entityType} ${entityId}:`, - err, - ); - }); -} - /** * Factory function creating a complete CRUD router for catalog resources. * @@ -447,10 +423,12 @@ function createCatalogResourceRouter( delete: (db: Database, brandId: string, id: string) => Promise; }, transformInput?: (input: any) => any, - fanOutConfig?: CatalogFanOutConfig, + dirtyConfig?: CatalogDirtyConfig, ) { - // Compose the shared CRUD helpers with optional catalog fan-out hooks. - const resolveDeleteProductIds = fanOutConfig?.resolveDeleteProductIds; + // Compose the shared CRUD helpers with optional inline dirty-marking hooks. + const resolveDirtyProductIds = dirtyConfig?.resolveProductIds; + const resolveDeleteProductIds = + dirtyConfig?.resolveDeleteProductIds ?? resolveDirtyProductIds; return createTRPCRouter({ list: createListProcedure( @@ -464,14 +442,15 @@ function createCatalogResourceRouter( operations.create, resourceName, transformInput, - fanOutConfig + dirtyConfig ? { - afterSuccess: ({ brandCtx, result }) => { - enqueueCatalogFanOut( + afterSuccess: async ({ brandCtx, result }) => { + const productIds = await resolveDirtyProductIds!( + brandCtx.db, brandCtx.brandId, - fanOutConfig.entityType, (result as { id: string }).id, ); + await markCatalogProductsDirty(brandCtx, productIds); }, } : undefined, @@ -481,14 +460,15 @@ function createCatalogResourceRouter( operations.update, resourceName, transformInput, - fanOutConfig + dirtyConfig ? { - afterSuccess: ({ brandCtx, input }) => { - enqueueCatalogFanOut( + afterSuccess: async ({ brandCtx, input }) => { + const productIds = await resolveDirtyProductIds!( + brandCtx.db, brandCtx.brandId, - fanOutConfig.entityType, input.id, ); + await markCatalogProductsDirty(brandCtx, productIds); }, } : undefined, @@ -497,19 +477,14 @@ function createCatalogResourceRouter( schemas.delete, operations.delete, resourceName, - fanOutConfig + dirtyConfig ? { beforeDelete: resolveDeleteProductIds ? ({ brandCtx, input }) => resolveDeleteProductIds(brandCtx.db, brandCtx.brandId, input.id) : undefined, - afterSuccess: ({ brandCtx, input, beforeDeleteData }) => { - enqueueCatalogFanOut( - brandCtx.brandId, - fanOutConfig.entityType, - input.id, - { productIds: beforeDeleteData }, - ); + afterSuccess: async ({ brandCtx, beforeDeleteData }) => { + await markCatalogProductsDirty(brandCtx, beforeDeleteData); }, } : undefined, @@ -735,7 +710,7 @@ export const catalogRouter = createTRPCRouter({ delete: deleteMaterial, }, transformMaterialInput, - { entityType: "material" }, + { resolveProductIds: findPublishedProductIdsByMaterial }, ), /** @@ -780,7 +755,7 @@ export const catalogRouter = createTRPCRouter({ delete: deleteOperator, }, transformOperatorInput, - { entityType: "operator" }, + { resolveProductIds: findPublishedProductIdsByOperator }, ), /** @@ -805,7 +780,7 @@ export const catalogRouter = createTRPCRouter({ }, transformManufacturerInput, { - entityType: "manufacturer", + resolveProductIds: findPublishedProductIdsByManufacturer, resolveDeleteProductIds: findPublishedProductIdsByManufacturer, }, ), @@ -831,7 +806,7 @@ export const catalogRouter = createTRPCRouter({ }, transformCertificationInput, { - entityType: "certification", + resolveProductIds: findPublishedProductIdsByCertification, resolveDeleteProductIds: findPublishedProductIdsByCertification, }, ), diff --git a/apps/api/src/trpc/routers/dpp-public/index.ts b/apps/api/src/trpc/routers/dpp-public/index.ts index c6740b6c..5464ad3d 100644 --- a/apps/api/src/trpc/routers/dpp-public/index.ts +++ b/apps/api/src/trpc/routers/dpp-public/index.ts @@ -1,12 +1,13 @@ import { getBrandBySlug, getBrandTheme } from "@v1/db/queries"; import { getPublicDppByUpid } from "@v1/db/queries/dpp"; +import { projectSinglePassport } from "@v1/db/queries/products"; import { brandCustomDomains, brands, productPassports, } from "@v1/db/schema"; import { getPublicUrl } from "@v1/supabase/storage"; -import { and, eq, inArray, isNotNull } from "drizzle-orm"; +import { and, eq, inArray } from "drizzle-orm"; /** * Public DPP (Digital Product Passport) router. * @@ -21,7 +22,12 @@ import { and, eq, inArray, isNotNull } from "drizzle-orm"; import { z } from "zod"; import { resolveThemeConfigImageUrls } from "../../../utils/theme-config-images.js"; import { slugSchema } from "../../../schemas/_shared/primitives.js"; -import { createTRPCRouter, publicProcedure } from "../../init.js"; +import { internalServerError } from "../../../utils/errors.js"; +import { + createTRPCRouter, + publicProcedure, + type TRPCContext, +} from "../../init.js"; const PRODUCTS_BUCKET = "products"; @@ -104,6 +110,101 @@ function resolveSnapshotProductImageUrl( ); } +/** + * Format a public passport query result into the router response shape. + */ +function buildPublicPassportResponse( + storageClient: TRPCContext["supabase"], + result: Awaited>, +) { + // The router only calls this helper once a materialized snapshot exists. + if (!result.snapshot) { + return null; + } + + const stylesheetUrl = result.theme?.stylesheetPath + ? getPublicUrl(storageClient, "dpp-themes", result.theme.stylesheetPath) + : null; + const productImageUrl = resolveSnapshotProductImageUrl( + storageClient, + result.snapshot.productAttributes?.image, + ); + const resolvedThemeConfig = resolveThemeConfigImageUrls( + storageClient, + (result.theme?.config as Record) ?? null, + ); + + return { + dppData: { + ...result.snapshot, + productAttributes: { + ...result.snapshot.productAttributes, + image: productImageUrl ?? "", + }, + }, + themeConfig: resolvedThemeConfig, + themeStyles: result.theme?.styles ?? null, + stylesheetUrl, + googleFontsUrl: result.theme?.googleFontsUrl ?? null, + passport: { + upid: result.upid, + isInactive: result.isInactive, + version: result.version, + }, + }; +} + +/** + * Load a public passport, projecting inline when the working snapshot is dirty. + */ +async function resolvePublicPassportResponse( + ctx: Pick, + upid: string, +) { + // Start from the publishing-layer read model so both public endpoints share the same logic. + let result = await getPublicDppByUpid(ctx.db, upid); + + if (!result.found || !result.passport) { + return null; + } + + const isWorkingPassport = result.passport.workingVariantId !== null; + if (isWorkingPassport && result.productStatus !== "published") { + return null; + } + + if ( + result.passport.dirty && + result.passport.workingVariantId && + result.productStatus === "published" + ) { + const projection = await projectSinglePassport(ctx.db, result.passport.id); + + if (projection.error) { + console.error("[resolvePublicPassportResponse] inline projection failed", { + passportId: result.passport.id, + upid, + error: projection.error, + }); + } + + result = await getPublicDppByUpid(ctx.db, upid); + + if (!result.found || !result.passport) { + return null; + } + if (result.passport.workingVariantId && result.productStatus !== "published") { + return null; + } + + if (projection.error && !result.snapshot) { + throw internalServerError("Failed to materialize passport"); + } + } + + return buildPublicPassportResponse(ctx.supabase, result); +} + export const dppPublicRouter = createTRPCRouter({ /** * Fetch theme data for screenshot preview. @@ -167,51 +268,8 @@ export const dppPublicRouter = createTRPCRouter({ }), ) .query(async ({ ctx, input }) => { - const { upid } = input; - - // Fetch from the immutable publishing layer - const result = await getPublicDppByUpid(ctx.db, upid); - - if (!result.found || !result.snapshot) { - return null; - } - - // Resolve stylesheet URL if present - const stylesheetUrl = result.theme?.stylesheetPath - ? getPublicUrl(ctx.supabase, "dpp-themes", result.theme.stylesheetPath) - : null; - - // Resolve product image in the snapshot to a current public URL. - const productImageUrl = resolveSnapshotProductImageUrl( - ctx.supabase, - result.snapshot.productAttributes?.image, - ); - - // Resolve image paths in themeConfig to full URLs - const resolvedThemeConfig = resolveThemeConfigImageUrls( - ctx.supabase, - (result.theme?.config as Record) ?? null, - ); - - // Return the snapshot data with theme information - return { - dppData: { - ...result.snapshot, - productAttributes: { - ...result.snapshot.productAttributes, - image: productImageUrl ?? "", - }, - }, - themeConfig: resolvedThemeConfig, - themeStyles: result.theme?.styles ?? null, - stylesheetUrl, - googleFontsUrl: result.theme?.googleFontsUrl ?? null, - passport: { - upid: result.upid, - isInactive: result.isInactive, - version: result.version, - }, - }; + // Serve the public passport, projecting inline if the snapshot is stale. + return resolvePublicPassportResponse(ctx, input.upid); }), /** @@ -298,7 +356,6 @@ export const dppPublicRouter = createTRPCRouter({ and( eq(productPassports.brandId, input.brandId), inArray(productPassports.barcode, barcodes), - isNotNull(productPassports.currentVersionId), ), ) .limit(1); @@ -307,49 +364,8 @@ export const dppPublicRouter = createTRPCRouter({ return null; } - // Delegate to existing getPublicDppByUpid for full data retrieval - const result = await getPublicDppByUpid(ctx.db, passport.upid); - - if (!result.found || !result.snapshot) { - return null; - } - - // Resolve stylesheet URL if present - const stylesheetUrl = result.theme?.stylesheetPath - ? getPublicUrl(ctx.supabase, "dpp-themes", result.theme.stylesheetPath) - : null; - - // Resolve product image in the snapshot to a current public URL. - const productImageUrl = resolveSnapshotProductImageUrl( - ctx.supabase, - result.snapshot.productAttributes?.image, - ); - - // Resolve image paths in themeConfig to full URLs - const resolvedThemeConfig = resolveThemeConfigImageUrls( - ctx.supabase, - (result.theme?.config as Record) ?? null, - ); - - // Return same structure as getByPassportUpid - return { - dppData: { - ...result.snapshot, - productAttributes: { - ...result.snapshot.productAttributes, - image: productImageUrl ?? "", - }, - }, - themeConfig: resolvedThemeConfig, - themeStyles: result.theme?.styles ?? null, - stylesheetUrl, - googleFontsUrl: result.theme?.googleFontsUrl ?? null, - passport: { - upid: result.upid, - isInactive: result.isInactive, - version: result.version, - }, - }; + // Delegate to the same dirty-aware public resolver as the UPID route. + return resolvePublicPassportResponse(ctx, passport.upid); }), }); diff --git a/apps/api/src/trpc/routers/products/index.ts b/apps/api/src/trpc/routers/products/index.ts index a2087ee0..4a611ac7 100644 --- a/apps/api/src/trpc/routers/products/index.ts +++ b/apps/api/src/trpc/routers/products/index.ts @@ -13,7 +13,6 @@ import { and, eq, inArray } from "@v1/db/queries"; import { bulkDeleteProductsByFilter, bulkDeleteProductsByIds, - bulkPublishProducts, bulkUpdateProductsByFilter, bulkUpdateProductsByIds, createProduct, @@ -21,8 +20,8 @@ import { getProductWithIncludes, getProductSelectionCounts, listProductsWithIncludes, + markPassportsDirtyByProductIds, resolveSelectedProductIds, - publishProduct, setProductJourneySteps, setProductTags, updateProduct, @@ -37,6 +36,7 @@ import { qrExportJobs, } from "@v1/db/schema"; import { + revalidateBarcodes, revalidatePassports, revalidateProduct, } from "../../../lib/dpp-revalidation.js"; @@ -80,6 +80,106 @@ function normalizeBrandId(value: unknown): string | undefined { return typeof value === "string" ? value : undefined; } +/** + * Collect public passport identifiers for a set of products. + */ +async function collectPublicProductVariantIdentifiers( + ctx: BrandContext, + brandId: string, + productIds: string[], +): Promise<{ upids: string[]; barcodes: string[] }> { + // Load current variant identifiers so unpublish operations can invalidate DPP caches. + if (productIds.length === 0) { + return { upids: [], barcodes: [] }; + } + + const rows = await ctx.db + .select({ + upid: productVariants.upid, + barcode: productVariants.barcode, + }) + .from(productVariants) + .innerJoin(products, eq(productVariants.productId, products.id)) + .where( + and( + eq(products.brandId, brandId), + inArray(productVariants.productId, productIds), + ), + ); + + return { + upids: Array.from( + new Set( + rows + .map((row) => row.upid?.trim() ?? null) + .filter((upid): upid is string => Boolean(upid)), + ), + ), + barcodes: Array.from( + new Set( + rows + .map((row) => row.barcode?.trim() ?? null) + .filter((barcode): barcode is string => Boolean(barcode)), + ), + ), + }; +} + +/** + * Determine whether a single-product update touched snapshot-backed fields. + */ +function hasSingleProductSnapshotChanges( + input: unknown, +): boolean { + // Treat any working-data change that feeds the passport snapshot as dirtying. + const fields = + input && typeof input === "object" + ? (input as Record) + : {}; + + return ( + fields.name !== undefined || + fields.description !== undefined || + fields.category_id !== undefined || + fields.season_id !== undefined || + fields.manufacturer_id !== undefined || + fields.image_path !== undefined || + fields.status !== undefined || + fields.materials !== undefined || + fields.journey_steps !== undefined || + fields.environment !== undefined || + fields.weight !== undefined || + fields.tag_ids !== undefined + ); +} + +/** + * Determine whether a bulk product update touched snapshot-backed fields. + */ +function hasBulkProductSnapshotChanges( + input: unknown, +): boolean { + // Bulk updates only expose these three snapshot-relevant fields today. + const fields = + input && typeof input === "object" + ? (input as Record) + : {}; + + return ( + fields.status !== undefined || + fields.category_id !== undefined || + fields.season_id !== undefined + ); +} + +/** + * Determine whether a status change should immediately hide public pages. + */ +function shouldInvalidatePublicProductCaches(status: unknown): boolean { + // Scheduled behaves like unpublished on the public read path. + return status === "unpublished" || status === "scheduled"; +} + type CreateProductInput = Parameters[2]; type UpdateProductInput = Parameters[2]; type AttributeInput = { @@ -418,19 +518,27 @@ export const productsRouter = createTRPCRouter({ ); } - // If status changed to "published", trigger publish flow to create passport versions - // This ensures QR codes become resolvable when status is set to published - if ( - input.status === "published" && - result.productIds && - result.productIds.length > 0 - ) { - // Trigger bulk publish (fire-and-forget, errors logged but not thrown) - bulkPublishProducts(brandCtx.db, result.productIds, brandId).catch( - (err) => { - console.error("Bulk publish failed after status change:", err); - }, - ); + if (result.productIds && result.productIds.length > 0) { + // Mark published passports dirty inline instead of materializing snapshots here. + if (hasBulkProductSnapshotChanges(input)) { + await markPassportsDirtyByProductIds( + brandCtx.db, + brandId, + result.productIds, + ); + } + + // Invalidate public caches right away when the status hides the passport. + if (shouldInvalidatePublicProductCaches(input.status)) { + const identifiers = await collectPublicProductVariantIdentifiers( + brandCtx, + brandId, + result.productIds, + ); + + revalidatePassports(identifiers.upids).catch(() => {}); + revalidateBarcodes(brandId, identifiers.barcodes).catch(() => {}); + } } return { @@ -471,35 +579,23 @@ export const productsRouter = createTRPCRouter({ tag_ids: input.tag_ids, }); - // Trigger publish when status is explicitly changed to "published" - // This handles the case when user toggles status from the passports table - // (The form handles publish explicitly after all mutations complete) - if (input.status === "published" && product?.id) { - try { - const publishResult = await publishProduct( - brandCtx.db, - product.id, - brandId, - ); - if (!publishResult.success) { - console.error( - "Publish failed after status change:", - publishResult.error, - ); - } else { - const upids = publishResult.variants - .map((v) => v.passport?.upid) - .filter((u): u is string => Boolean(u)); - if (upids.length > 0) { - revalidatePassports(upids).catch(() => {}); - } - } - } catch (err) { - console.error( - "Publish threw an exception after status change:", - err, - ); - } + if (product?.id && hasSingleProductSnapshotChanges(input)) { + // Defer materialization by marking any published passports dirty. + await markPassportsDirtyByProductIds(brandCtx.db, brandId, [ + product.id, + ]); + } + + if (product?.id && shouldInvalidatePublicProductCaches(input.status)) { + // Hide existing public pages as soon as the product leaves the published state. + const identifiers = await collectPublicProductVariantIdentifiers( + brandCtx, + brandId, + [product.id], + ); + + revalidatePassports(identifiers.upids).catch(() => {}); + revalidateBarcodes(brandId, identifiers.barcodes).catch(() => {}); } // Revalidate DPP cache for this product (fire-and-forget) diff --git a/apps/api/src/trpc/routers/products/publish.ts b/apps/api/src/trpc/routers/products/publish.ts index 835c11b1..4c315823 100644 --- a/apps/api/src/trpc/routers/products/publish.ts +++ b/apps/api/src/trpc/routers/products/publish.ts @@ -2,20 +2,23 @@ * Publish Router. * * Exposes tRPC endpoints for publishing product passports. - * Publishing creates an immutable version record that persists independently - * of the working layer. + * Phase 2 rewires these endpoints to flip product status and mark passports dirty. * * Endpoints: * - publish.variant: Publish a single variant * - publish.product: Publish all variants of a product * - publish.bulk: Publish multiple products at once */ +import { and, eq, inArray } from "drizzle-orm"; import { - bulkPublishProducts, getPublishingState, - publishProduct, - publishVariant, + getOrCreatePassport, + markPassportDirty, + markPassportsDirtyByProductIds, + projectDirtyPassports, + projectSinglePassport, } from "@v1/db/queries/products"; +import { productVariants, products } from "@v1/db/schema"; import { z } from "zod"; import { revalidateBarcodes, @@ -31,6 +34,97 @@ import { type BrandContext = AuthenticatedTRPCContext & { brandId: string }; +/** + * Resolve the owning product for a variant inside the active brand. + */ +async function getVariantPublishTarget( + db: BrandContext["db"], + brandId: string, + variantId: string, +): Promise<{ productId: string } | null> { + // Constrain the lookup to the active brand so publish cannot cross brand boundaries. + const [target] = await db + .select({ productId: productVariants.productId }) + .from(productVariants) + .innerJoin(products, eq(products.id, productVariants.productId)) + .where( + and(eq(productVariants.id, variantId), eq(products.brandId, brandId)), + ) + .limit(1); + + return target ?? null; +} + +/** + * Count variants for a product inside the active brand. + */ +async function countProductVariants( + db: BrandContext["db"], + brandId: string, + productId: string, +): Promise { + // Read all matching variants so publish can keep the existing no-variants guard. + const variants = await db + .select({ id: productVariants.id }) + .from(productVariants) + .innerJoin(products, eq(products.id, productVariants.productId)) + .where(and(eq(products.id, productId), eq(products.brandId, brandId))); + + if (variants.length === 0) { + const [product] = await db + .select({ id: products.id }) + .from(products) + .where(and(eq(products.id, productId), eq(products.brandId, brandId))) + .limit(1); + + return product ? 0 : null; + } + + return variants.length; +} + +/** + * Flip the supplied products into published status. + */ +async function setProductsPublished( + db: BrandContext["db"], + brandId: string, + productIds: string[], +): Promise { + // Skip empty batches so callers can forward filtered product lists directly. + if (productIds.length === 0) { + return []; + } + + const updated = await db + .update(products) + .set({ + status: "published", + updatedAt: new Date().toISOString(), + }) + .where(and(eq(products.brandId, brandId), inArray(products.id, productIds))) + .returning({ id: products.id }); + + return updated.map((row) => row.id); +} + +/** + * Revalidate public caches after an inline passport projection. + */ +async function revalidateProjectedPassports( + brandId: string, + identifiers: { + upids: string[]; + barcodes: string[]; + }, +): Promise { + // Revalidate both public lookup paths without failing the mutation on cache errors. + await Promise.allSettled([ + revalidatePassports(identifiers.upids), + revalidateBarcodes(brandId, identifiers.barcodes), + ]); +} + // ============================================================================= // INPUT SCHEMAS // ============================================================================= @@ -62,11 +156,10 @@ export const publishRouter = createTRPCRouter({ /** * Publish a single variant. * - * Creates or updates the variant's passport with a new immutable version - * containing the current working data as a JSON-LD snapshot. + * Marks the variant dirty, projects it inline, and revalidates the public cache. * * @param variantId - The variant's UUID - * @returns Success status with passport and version info + * @returns Success status with passport info */ variant: brandWriteProcedure .input(publishVariantSchema) @@ -74,21 +167,42 @@ export const publishRouter = createTRPCRouter({ const { db, brandId } = ctx as BrandContext; try { - const result = await publishVariant(db, input.variantId, brandId); + const target = await getVariantPublishTarget(db, brandId, input.variantId); + if (!target) { + throw badRequest("Variant not found"); + } - if (!result.success) { - throw badRequest(result.error ?? "Failed to publish variant"); + const { passport, isNew } = await getOrCreatePassport( + db, + input.variantId, + brandId, + ); + if (!passport) { + throw badRequest("Failed to create or retrieve passport"); } - if (result.passport?.upid) { - revalidatePassports([result.passport.upid]).catch(() => {}); + await setProductsPublished(db, brandId, [target.productId]); + await markPassportDirty(db, passport.id); + const projection = await projectSinglePassport(db, passport.id); + + if (!projection.found || !projection.version || !projection.passport) { + throw badRequest(projection.error ?? "Failed to project passport"); } + await revalidateProjectedPassports(brandId, { + upids: [projection.passport.upid], + barcodes: projection.passport.barcode ? [projection.passport.barcode] : [], + }); + return { success: true, - variantId: result.variantId, - passport: result.passport, - version: result.version, + variantId: input.variantId, + passport: { + id: projection.passport.id, + upid: projection.passport.upid, + isNew, + }, + version: projection.version, }; } catch (error) { throw wrapError(error, "Failed to publish variant"); @@ -98,12 +212,10 @@ export const publishRouter = createTRPCRouter({ /** * Publish all variants of a product. * - * Creates or updates passports for all variants of the specified product. - * Updates the product's status to 'published' and clears the - * has_unpublished_changes flag. + * Marks the product's passports dirty, projects them inline, and revalidates caches. * * @param productId - The product's UUID - * @returns Success status with count of published variants + * @returns Success status with count of targeted variants */ product: brandWriteProcedure .input(publishProductSchema) @@ -111,25 +223,33 @@ export const publishRouter = createTRPCRouter({ const { db, brandId } = ctx as BrandContext; try { - const result = await publishProduct(db, input.productId, brandId); - - if (!result.success) { - throw badRequest(result.error ?? "Failed to publish product"); + const variantCount = await countProductVariants(db, brandId, input.productId); + if (variantCount === null) { + throw badRequest("Product not found"); } - - const upids = result.variants - .map((v) => v.passport?.upid) - .filter((u): u is string => Boolean(u)); - if (upids.length > 0) { - revalidatePassports(upids).catch(() => {}); + if (variantCount === 0) { + throw badRequest("No variants found for product"); } + await setProductsPublished(db, brandId, [input.productId]); + await markPassportsDirtyByProductIds(db, brandId, [input.productId]); + const projection = await projectDirtyPassports(db, brandId, { + productIds: [input.productId], + }); + + await revalidateProjectedPassports(brandId, { + upids: projection.upids, + barcodes: projection.barcodes, + }); + return { success: true, - productId: result.productId, - count: result.totalPublished, - failed: result.totalFailed, - variants: result.variants, + productId: input.productId, + count: variantCount, + failed: 0, + passportsProjected: projection.totalPassportsProjected, + versionsCreated: projection.versionsCreated, + versionsSkippedUnchanged: projection.versionsSkippedUnchanged, }; } catch (error) { throw wrapError(error, "Failed to publish product"); @@ -139,8 +259,7 @@ export const publishRouter = createTRPCRouter({ /** * Bulk publish multiple products. * - * Publishes all variants for each product in the provided list. - * Useful for publishing multiple products from the list view. + * Flips the selected products to published and leaves projection to the background job. * * @param productIds - Array of product UUIDs to publish * @returns Success status with total counts @@ -151,25 +270,24 @@ export const publishRouter = createTRPCRouter({ const { db, brandId } = ctx as BrandContext; try { - const result = await bulkPublishProducts(db, input.productIds, brandId); - - if (!result.success) { - throw badRequest("Failed to bulk publish products"); - } - - const upids = result.products - .flatMap((p) => p.variants.map((v) => v.passport?.upid)) - .filter((u): u is string => Boolean(u)); - if (upids.length > 0) { - revalidatePassports(upids).catch(() => {}); - } + const uniqueProductIds = Array.from(new Set(input.productIds)); + const updatedProductIds = await setProductsPublished( + db, + brandId, + uniqueProductIds, + ); + const dirtyResult = await markPassportsDirtyByProductIds( + db, + brandId, + updatedProductIds, + ); return { - success: result.success, - totalProductsPublished: result.totalProductsPublished, - totalVariantsPublished: result.totalVariantsPublished, - totalFailed: result.totalFailed, - products: result.products, + success: true, + totalProductsPublished: updatedProductIds.length, + totalVariantsPublished: dirtyResult.marked, + totalFailed: uniqueProductIds.length - updatedProductIds.length, + productsMarkedDirty: updatedProductIds.length, }; } catch (error) { throw wrapError(error, "Failed to bulk publish products"); diff --git a/apps/api/src/trpc/routers/products/variants.ts b/apps/api/src/trpc/routers/products/variants.ts index cd1c0528..989214af 100644 --- a/apps/api/src/trpc/routers/products/variants.ts +++ b/apps/api/src/trpc/routers/products/variants.ts @@ -2,6 +2,7 @@ import { and, eq, inArray } from "@v1/db/queries"; import { batchCreatePassportsForVariants, batchOrphanPassportsByVariantIds, + markPassportsDirtyByVariantIds, batchSyncPassportMetadata, clearAllVariantOverrides, createPassportForVariant, @@ -684,6 +685,9 @@ export const productVariantsRouter = createTRPCRouter({ await applyVariantOverrides(db, variant.id, input.overrides); } + // Mark the new passport dirty if the parent product is already published. + await markPassportsDirtyByVariantIds(db, [variant.id]); + // Revalidate product cache revalidateProduct(input.productHandle).catch(() => {}); @@ -797,6 +801,9 @@ export const productVariantsRouter = createTRPCRouter({ await applyVariantOverrides(db, variantId, input.overrides); } + // Mark the affected passport dirty when the parent product is published. + await markPassportsDirtyByVariantIds(db, [variantId]); + // Revalidate product cache revalidateProduct(input.productHandle).catch(() => {}); @@ -1001,6 +1008,14 @@ export const productVariantsRouter = createTRPCRouter({ await applyVariantOverrides(db, variantId, overrides); } + if (createdVariants.length > 0) { + // Mark new passports dirty so published products can be re-materialized later. + await markPassportsDirtyByVariantIds( + db, + createdVariants.map((variant) => variant.id), + ); + } + // Revalidate product cache revalidateProduct(input.productHandle).catch(() => {}); @@ -1112,6 +1127,7 @@ export const productVariantsRouter = createTRPCRouter({ // ─────────────────────────────────────────────────────────────────────── let updated = 0; + const updatedVariantIds: string[] = []; // Track variant overrides to apply after transaction const variantOverridesToApply: Array<{ @@ -1197,6 +1213,7 @@ export const productVariantsRouter = createTRPCRouter({ } updated++; + updatedVariantIds.push(variant.id); } }); @@ -1210,6 +1227,11 @@ export const productVariantsRouter = createTRPCRouter({ await applyVariantOverrides(db, variantId, overrides); } + if (updatedVariantIds.length > 0) { + // Mark updated passports dirty so published products serve fresh data later. + await markPassportsDirtyByVariantIds(db, updatedVariantIds); + } + // Revalidate product cache revalidateProduct(input.productHandle).catch(() => {}); @@ -1549,9 +1571,17 @@ export const productVariantsRouter = createTRPCRouter({ } }); - // NOTE: Publishing is handled explicitly by the form after all mutations complete. - // This keeps publish logic in ONE place and avoids complex conditional publish triggers. - // The form calls publish.product at the end of the save flow if the product is published. + // Dirty marking now happens here so published products can be projected later. + // The form may still call publish.product explicitly, but that path is now idempotent. + const affectedVariantIds = [ + ...createdVariants.map((variant) => variant.id), + ...toUpdate.map((variant) => variant.variantId), + ]; + + if (affectedVariantIds.length > 0) { + // Mark all changed passports dirty now that sync has committed its writes. + await markPassportsDirtyByVariantIds(db, affectedVariantIds); + } // Revalidate product cache revalidateProduct(input.productHandle).catch(() => {}); @@ -1602,6 +1632,9 @@ export const productVariantsRouter = createTRPCRouter({ await clearAllVariantOverrides(db, variantId); + // Clearing overrides changes the published snapshot input for this variant. + await markPassportsDirtyByVariantIds(db, [variantId]); + // Revalidate product cache revalidateProduct(input.productHandle).catch(() => {}); diff --git a/apps/api/supabase/migrations/20260306101056_bouncy_cyclops.sql b/apps/api/supabase/migrations/20260306101056_bouncy_cyclops.sql new file mode 100644 index 00000000..f86c2ddf --- /dev/null +++ b/apps/api/supabase/migrations/20260306101056_bouncy_cyclops.sql @@ -0,0 +1,3 @@ +ALTER TABLE "product_passports" ALTER COLUMN "first_published_at" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "product_passports" ADD COLUMN "dirty" boolean DEFAULT false NOT NULL;--> statement-breakpoint +CREATE INDEX "idx_product_passports_brand_dirty" ON "product_passports" USING btree ("brand_id" uuid_ops,"dirty" bool_ops) WHERE dirty = true; \ No newline at end of file diff --git a/apps/api/supabase/migrations/20260306111521_white_iron_lad.sql b/apps/api/supabase/migrations/20260306111521_white_iron_lad.sql new file mode 100644 index 00000000..6557a6b1 --- /dev/null +++ b/apps/api/supabase/migrations/20260306111521_white_iron_lad.sql @@ -0,0 +1,3 @@ +ALTER TABLE "product_passport_versions" ALTER COLUMN "data_snapshot" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "product_passport_versions" ADD COLUMN "compressed_snapshot" "bytea";--> statement-breakpoint +ALTER TABLE "product_passport_versions" ADD COLUMN "compressed_at" timestamp with time zone; \ No newline at end of file diff --git a/apps/api/supabase/migrations/20260306125055_remarkable_mattie_franklin.sql b/apps/api/supabase/migrations/20260306125055_remarkable_mattie_franklin.sql new file mode 100644 index 00000000..e1e230c9 --- /dev/null +++ b/apps/api/supabase/migrations/20260306125055_remarkable_mattie_franklin.sql @@ -0,0 +1 @@ +ALTER TABLE "product_passport_versions" ADD CONSTRAINT "product_passport_versions_snapshot_presence_check" CHECK (num_nonnulls("product_passport_versions"."data_snapshot", "product_passport_versions"."compressed_snapshot") = 1); \ No newline at end of file diff --git a/apps/api/supabase/migrations/meta/20260306101056_snapshot.json b/apps/api/supabase/migrations/meta/20260306101056_snapshot.json new file mode 100644 index 00000000..483cdc9a --- /dev/null +++ b/apps/api/supabase/migrations/meta/20260306101056_snapshot.json @@ -0,0 +1,11036 @@ +{ + "id": "0e7a69f9-446b-40fe-8f78-3015200a60ae", + "prevId": "09037d5d-e1d1-47af-ae61-dbbaca7bfca1", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "full_name": { + "name": "full_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "avatar_path": { + "name": "avatar_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_users_brand_id": { + "name": "idx_users_brand_id", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "where": "(brand_id IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "users_id_users_id_fk": { + "name": "users_id_users_id_fk", + "tableFrom": "users", + "tableTo": "users", + "schemaTo": "auth", + "columnsFrom": [ + "id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_key": { + "name": "users_email_key", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": { + "users_select_own_profile": { + "name": "users_select_own_profile", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "auth.uid() = id" + }, + "users_select_for_brand_members": { + "name": "users_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "shares_brand_with(id)" + }, + "users_update_own_profile": { + "name": "users_update_own_profile", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "auth.uid() = id", + "withCheck": "\n auth.uid() = id\n AND (\n brand_id IS NULL\n OR EXISTS (\n SELECT 1\n FROM brand_members bm\n WHERE bm.brand_id = users.brand_id\n AND bm.user_id = auth.uid()\n )\n )\n " + }, + "users_insert_own_profile": { + "name": "users_insert_own_profile", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "auth.uid() = id" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brands": { + "name": "brands", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "country_code": { + "name": "country_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo_path": { + "name": "logo_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_brands_active": { + "name": "idx_brands_active", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "(deleted_at IS NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brands_slug": { + "name": "idx_brands_slug", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "(slug IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brands_email": { + "name": "idx_brands_email", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brands_update_by_member": { + "name": "brands_update_by_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(id)" + }, + "brands_delete_by_owner": { + "name": "brands_delete_by_owner", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_owner(id)" + }, + "brands_select_for_invite_recipients": { + "name": "brands_select_for_invite_recipients", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ] + }, + "brands_select_for_members": { + "name": "brands_select_for_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(id)" + }, + "brands_insert_by_authenticated": { + "name": "brands_insert_by_authenticated", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ] + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.platform_admin_audit_logs": { + "name": "platform_admin_audit_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_type": { + "name": "resource_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_platform_admin_audit_logs_actor_user_id": { + "name": "idx_platform_admin_audit_logs_actor_user_id", + "columns": [ + { + "expression": "actor_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_platform_admin_audit_logs_resource": { + "name": "idx_platform_admin_audit_logs_resource", + "columns": [ + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_platform_admin_audit_logs_created_at": { + "name": "idx_platform_admin_audit_logs_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "platform_admin_audit_logs_actor_user_id_users_id_fk": { + "name": "platform_admin_audit_logs_actor_user_id_users_id_fk", + "tableFrom": "platform_admin_audit_logs", + "tableTo": "users", + "columnsFrom": [ + "actor_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "platform_admin_audit_logs_select_by_service_role": { + "name": "platform_admin_audit_logs_select_by_service_role", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "service_role" + ], + "using": "true" + }, + "platform_admin_audit_logs_insert_by_service_role": { + "name": "platform_admin_audit_logs_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "platform_admin_audit_logs_update_by_service_role": { + "name": "platform_admin_audit_logs_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true", + "withCheck": "true" + }, + "platform_admin_audit_logs_delete_by_service_role": { + "name": "platform_admin_audit_logs_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.platform_admin_allowlist": { + "name": "platform_admin_allowlist", + "schema": "", + "columns": { + "email": { + "name": "email", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ux_platform_admin_allowlist_user_id_not_null": { + "name": "ux_platform_admin_allowlist_user_id_not_null", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "(user_id IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "platform_admin_allowlist_user_id_users_id_fk": { + "name": "platform_admin_allowlist_user_id_users_id_fk", + "tableFrom": "platform_admin_allowlist", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "platform_admin_allowlist_select_by_service_role": { + "name": "platform_admin_allowlist_select_by_service_role", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "service_role" + ], + "using": "true" + }, + "platform_admin_allowlist_insert_by_service_role": { + "name": "platform_admin_allowlist_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "platform_admin_allowlist_update_by_service_role": { + "name": "platform_admin_allowlist_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true", + "withCheck": "true" + }, + "platform_admin_allowlist_delete_by_service_role": { + "name": "platform_admin_allowlist_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_collections": { + "name": "brand_collections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filter": { + "name": "filter", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_collections_brand_name_unq": { + "name": "brand_collections_brand_name_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_collections_brand_id_brands_id_fk": { + "name": "brand_collections_brand_id_brands_id_fk", + "tableFrom": "brand_collections", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_collections_select_for_brand_members": { + "name": "brand_collections_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_collections_insert_by_brand_member": { + "name": "brand_collections_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_collections_update_by_brand_member": { + "name": "brand_collections_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_collections_delete_by_brand_member": { + "name": "brand_collections_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_members": { + "name": "brand_members", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ux_brand_members_user_brand": { + "name": "ux_brand_members_user_brand", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_members_brand_id": { + "name": "idx_brand_members_brand_id", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_members_user_id": { + "name": "idx_brand_members_user_id", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_members_user_id_users_id_fk": { + "name": "brand_members_user_id_users_id_fk", + "tableFrom": "brand_members", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "brand_members_brand_id_brands_id_fk": { + "name": "brand_members_brand_id_brands_id_fk", + "tableFrom": "brand_members", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "brand_members_user_id_brand_id_key": { + "name": "brand_members_user_id_brand_id_key", + "nullsNotDistinct": false, + "columns": [ + "user_id", + "brand_id" + ] + } + }, + "policies": { + "brand_members_update_by_owner": { + "name": "brand_members_update_by_owner", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_owner(brand_id)" + }, + "brand_members_select_for_members": { + "name": "brand_members_select_for_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_members_delete_self": { + "name": "brand_members_delete_self", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ] + }, + "brand_members_delete_owner_non_owner": { + "name": "brand_members_delete_owner_non_owner", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ] + }, + "brand_members_insert_first_owner_self": { + "name": "brand_members_insert_first_owner_self", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ] + } + }, + "checkConstraints": { + "brand_members_role_check": { + "name": "brand_members_role_check", + "value": "role = ANY (ARRAY['owner'::text, 'member'::text, 'avelero'::text])" + } + }, + "isRLSEnabled": false + }, + "public.brand_invites": { + "name": "brand_invites", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_brand_invites_brand_id": { + "name": "idx_brand_invites_brand_id", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_invites_expires_at": { + "name": "idx_brand_invites_expires_at", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_invites_token_hash": { + "name": "idx_brand_invites_token_hash", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_invites_valid_lookup": { + "name": "idx_brand_invites_valid_lookup", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_invites_email": { + "name": "idx_brand_invites_email", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_invites_email_expires": { + "name": "idx_brand_invites_email_expires", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ux_brand_invites_token_hash_not_null": { + "name": "ux_brand_invites_token_hash_not_null", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": true, + "where": "(token_hash IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_invites_brand_id_brands_id_fk": { + "name": "brand_invites_brand_id_brands_id_fk", + "tableFrom": "brand_invites", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_invites_update_by_owner": { + "name": "brand_invites_update_by_owner", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_owner(brand_id)" + }, + "brand_invites_insert_by_owner": { + "name": "brand_invites_insert_by_owner", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_owner(brand_id)" + }, + "brand_invites_delete_by_owner": { + "name": "brand_invites_delete_by_owner", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_owner(brand_id)" + }, + "brand_invites_select_for_recipient": { + "name": "brand_invites_select_for_recipient", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ] + }, + "brand_invites_delete_by_recipient": { + "name": "brand_invites_delete_by_recipient", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ] + }, + "brand_invites_select_for_members": { + "name": "brand_invites_select_for_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ] + } + }, + "checkConstraints": { + "brand_invites_role_check": { + "name": "brand_invites_role_check", + "value": "role = ANY (ARRAY['owner'::text, 'member'::text])" + } + }, + "isRLSEnabled": false + }, + "public.brand_tags": { + "name": "brand_tags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hex": { + "name": "hex", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_tags_brand_name_unq": { + "name": "brand_tags_brand_name_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_tags_brand_id_brands_id_fk": { + "name": "brand_tags_brand_id_brands_id_fk", + "tableFrom": "brand_tags", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_tags_select_for_brand_members": { + "name": "brand_tags_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_tags_insert_by_brand_members": { + "name": "brand_tags_insert_by_brand_members", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_tags_update_by_brand_members": { + "name": "brand_tags_update_by_brand_members", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_tags_delete_by_brand_members": { + "name": "brand_tags_delete_by_brand_members", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_theme": { + "name": "brand_theme", + "schema": "", + "columns": { + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "theme_styles": { + "name": "theme_styles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "theme_config": { + "name": "theme_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "stylesheet_path": { + "name": "stylesheet_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "google_fonts_url": { + "name": "google_fonts_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_brand_theme_updated_at": { + "name": "idx_brand_theme_updated_at", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_theme_brand_id_brands_id_fk": { + "name": "brand_theme_brand_id_brands_id_fk", + "tableFrom": "brand_theme", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "brand_theme_brand_id_pkey": { + "name": "brand_theme_brand_id_pkey", + "columns": [ + "brand_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": { + "brand_theme_select_for_brand_members": { + "name": "brand_theme_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_theme_insert_by_brand_member": { + "name": "brand_theme_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_theme_update_by_brand_member": { + "name": "brand_theme_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)", + "withCheck": "is_brand_member(brand_id)" + }, + "brand_theme_delete_by_brand_member": { + "name": "brand_theme_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_custom_domains": { + "name": "brand_custom_domains", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "verification_token": { + "name": "verification_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_verification_attempt": { + "name": "last_verification_attempt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "verification_error": { + "name": "verification_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "verified_at": { + "name": "verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_custom_domains_brand_id_unq": { + "name": "brand_custom_domains_brand_id_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "brand_custom_domains_domain_unq": { + "name": "brand_custom_domains_domain_unq", + "columns": [ + { + "expression": "domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_custom_domains_status": { + "name": "idx_brand_custom_domains_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_custom_domains_brand_id_brands_id_fk": { + "name": "brand_custom_domains_brand_id_brands_id_fk", + "tableFrom": "brand_custom_domains", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_custom_domains_select_for_brand_members": { + "name": "brand_custom_domains_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_custom_domains_insert_by_brand_owner": { + "name": "brand_custom_domains_insert_by_brand_owner", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_owner(brand_id)" + }, + "brand_custom_domains_update_by_brand_owner": { + "name": "brand_custom_domains_update_by_brand_owner", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_owner(brand_id)", + "withCheck": "is_brand_owner(brand_id)" + }, + "brand_custom_domains_delete_by_brand_owner": { + "name": "brand_custom_domains_delete_by_brand_owner", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_owner(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_lifecycle": { + "name": "brand_lifecycle", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "phase": { + "name": "phase", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'demo'" + }, + "phase_changed_at": { + "name": "phase_changed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "trial_started_at": { + "name": "trial_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "trial_ends_at": { + "name": "trial_ends_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "hard_delete_after": { + "name": "hard_delete_after", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_lifecycle_brand_id_unq": { + "name": "brand_lifecycle_brand_id_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_lifecycle_phase": { + "name": "idx_brand_lifecycle_phase", + "columns": [ + { + "expression": "phase", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_lifecycle_trial_ends_at": { + "name": "idx_brand_lifecycle_trial_ends_at", + "columns": [ + { + "expression": "trial_ends_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_lifecycle_hard_delete_after": { + "name": "idx_brand_lifecycle_hard_delete_after", + "columns": [ + { + "expression": "hard_delete_after", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "(hard_delete_after IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_lifecycle_brand_id_brands_id_fk": { + "name": "brand_lifecycle_brand_id_brands_id_fk", + "tableFrom": "brand_lifecycle", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_lifecycle_select_for_brand_members": { + "name": "brand_lifecycle_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_lifecycle_insert_by_service_role": { + "name": "brand_lifecycle_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "brand_lifecycle_update_by_service_role": { + "name": "brand_lifecycle_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true", + "withCheck": "true" + }, + "brand_lifecycle_delete_by_service_role": { + "name": "brand_lifecycle_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": { + "brand_lifecycle_phase_check": { + "name": "brand_lifecycle_phase_check", + "value": "phase = ANY (ARRAY['demo'::text, 'trial'::text, 'expired'::text, 'active'::text, 'past_due'::text, 'suspended'::text, 'cancelled'::text])" + } + }, + "isRLSEnabled": false + }, + "public.brand_plan": { + "name": "brand_plan", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "plan_type": { + "name": "plan_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "plan_selected_at": { + "name": "plan_selected_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "sku_annual_limit": { + "name": "sku_annual_limit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "sku_onboarding_limit": { + "name": "sku_onboarding_limit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "sku_limit_override": { + "name": "sku_limit_override", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "sku_year_start": { + "name": "sku_year_start", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "skus_created_this_year": { + "name": "skus_created_this_year", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "skus_created_onboarding": { + "name": "skus_created_onboarding", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "max_seats": { + "name": "max_seats", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_plan_brand_id_unq": { + "name": "brand_plan_brand_id_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_plan_plan_type": { + "name": "idx_brand_plan_plan_type", + "columns": [ + { + "expression": "plan_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_plan_sku_year_start": { + "name": "idx_brand_plan_sku_year_start", + "columns": [ + { + "expression": "sku_year_start", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_plan_brand_id_brands_id_fk": { + "name": "brand_plan_brand_id_brands_id_fk", + "tableFrom": "brand_plan", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_plan_select_for_brand_members": { + "name": "brand_plan_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_plan_insert_by_service_role": { + "name": "brand_plan_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "brand_plan_update_by_service_role": { + "name": "brand_plan_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true", + "withCheck": "true" + }, + "brand_plan_delete_by_service_role": { + "name": "brand_plan_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": { + "brand_plan_type_check": { + "name": "brand_plan_type_check", + "value": "plan_type IS NULL OR plan_type = ANY (ARRAY['starter'::text, 'growth'::text, 'scale'::text, 'enterprise'::text])" + }, + "brand_plan_sku_annual_limit_check": { + "name": "brand_plan_sku_annual_limit_check", + "value": "sku_annual_limit IS NULL OR sku_annual_limit >= 0" + }, + "brand_plan_sku_onboarding_limit_check": { + "name": "brand_plan_sku_onboarding_limit_check", + "value": "sku_onboarding_limit IS NULL OR sku_onboarding_limit >= 0" + }, + "brand_plan_sku_limit_override_check": { + "name": "brand_plan_sku_limit_override_check", + "value": "sku_limit_override IS NULL OR sku_limit_override >= 0" + }, + "brand_plan_skus_created_this_year_check": { + "name": "brand_plan_skus_created_this_year_check", + "value": "skus_created_this_year >= 0" + }, + "brand_plan_skus_created_onboarding_check": { + "name": "brand_plan_skus_created_onboarding_check", + "value": "skus_created_onboarding >= 0" + }, + "brand_plan_max_seats_check": { + "name": "brand_plan_max_seats_check", + "value": "max_seats IS NULL OR max_seats > 0" + } + }, + "isRLSEnabled": false + }, + "public.brand_billing": { + "name": "brand_billing", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "billing_mode": { + "name": "billing_mode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_subscription_id": { + "name": "stripe_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "plan_currency": { + "name": "plan_currency", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'EUR'" + }, + "custom_monthly_price_cents": { + "name": "custom_monthly_price_cents", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "billing_access_override": { + "name": "billing_access_override", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "billing_override_expires_at": { + "name": "billing_override_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_billing_brand_id_unq": { + "name": "brand_billing_brand_id_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "brand_billing_stripe_customer_id_unq": { + "name": "brand_billing_stripe_customer_id_unq", + "columns": [ + { + "expression": "stripe_customer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "(stripe_customer_id IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "brand_billing_stripe_subscription_id_unq": { + "name": "brand_billing_stripe_subscription_id_unq", + "columns": [ + { + "expression": "stripe_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "(stripe_subscription_id IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_billing_override_expires": { + "name": "idx_brand_billing_override_expires", + "columns": [ + { + "expression": "billing_override_expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_billing_brand_id_brands_id_fk": { + "name": "brand_billing_brand_id_brands_id_fk", + "tableFrom": "brand_billing", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_billing_select_for_brand_members": { + "name": "brand_billing_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_billing_insert_by_service_role": { + "name": "brand_billing_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "brand_billing_update_by_service_role": { + "name": "brand_billing_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true", + "withCheck": "true" + }, + "brand_billing_delete_by_service_role": { + "name": "brand_billing_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": { + "brand_billing_mode_check": { + "name": "brand_billing_mode_check", + "value": "billing_mode IS NULL OR billing_mode = ANY (ARRAY['stripe_checkout'::text, 'stripe_invoice'::text])" + }, + "brand_billing_plan_currency_check": { + "name": "brand_billing_plan_currency_check", + "value": "char_length(plan_currency) = 3" + }, + "brand_billing_custom_monthly_price_check": { + "name": "brand_billing_custom_monthly_price_check", + "value": "custom_monthly_price_cents IS NULL OR custom_monthly_price_cents >= 0" + }, + "brand_billing_access_override_check": { + "name": "brand_billing_access_override_check", + "value": "billing_access_override = ANY (ARRAY['none'::text, 'temporary_allow'::text, 'temporary_block'::text])" + } + }, + "isRLSEnabled": false + }, + "public.brand_billing_events": { + "name": "brand_billing_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_event_id": { + "name": "stripe_event_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_brand_billing_events_brand_created_at": { + "name": "idx_brand_billing_events_brand_created_at", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_billing_events_event_type": { + "name": "idx_brand_billing_events_event_type", + "columns": [ + { + "expression": "event_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "brand_billing_events_stripe_event_id_unq": { + "name": "brand_billing_events_stripe_event_id_unq", + "columns": [ + { + "expression": "stripe_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "(stripe_event_id IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_billing_events_brand_id_brands_id_fk": { + "name": "brand_billing_events_brand_id_brands_id_fk", + "tableFrom": "brand_billing_events", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_billing_events_select_for_brand_members": { + "name": "brand_billing_events_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_billing_events_insert_by_service_role": { + "name": "brand_billing_events_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "brand_billing_events_update_by_service_role": { + "name": "brand_billing_events_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true", + "withCheck": "true" + }, + "brand_billing_events_delete_by_service_role": { + "name": "brand_billing_events_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_attributes": { + "name": "brand_attributes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "taxonomy_attribute_id": { + "name": "taxonomy_attribute_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_attributes_brand_name_unq": { + "name": "brand_attributes_brand_name_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_attributes_brand_id_brands_id_fk": { + "name": "brand_attributes_brand_id_brands_id_fk", + "tableFrom": "brand_attributes", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "brand_attributes_taxonomy_attribute_id_taxonomy_attributes_id_fk": { + "name": "brand_attributes_taxonomy_attribute_id_taxonomy_attributes_id_fk", + "tableFrom": "brand_attributes", + "tableTo": "taxonomy_attributes", + "columnsFrom": [ + "taxonomy_attribute_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_attributes_select_for_brand_members": { + "name": "brand_attributes_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_attributes_insert_by_brand_member": { + "name": "brand_attributes_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_attributes_update_by_brand_member": { + "name": "brand_attributes_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_attributes_delete_by_brand_member": { + "name": "brand_attributes_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_attribute_values": { + "name": "brand_attribute_values", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "attribute_id": { + "name": "attribute_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "taxonomy_value_id": { + "name": "taxonomy_value_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_attribute_values_brand_attr_name_unq": { + "name": "brand_attribute_values_brand_attr_name_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "attribute_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_attribute_values_brand_id_brands_id_fk": { + "name": "brand_attribute_values_brand_id_brands_id_fk", + "tableFrom": "brand_attribute_values", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "brand_attribute_values_attribute_id_brand_attributes_id_fk": { + "name": "brand_attribute_values_attribute_id_brand_attributes_id_fk", + "tableFrom": "brand_attribute_values", + "tableTo": "brand_attributes", + "columnsFrom": [ + "attribute_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "brand_attribute_values_taxonomy_value_id_taxonomy_values_id_fk": { + "name": "brand_attribute_values_taxonomy_value_id_taxonomy_values_id_fk", + "tableFrom": "brand_attribute_values", + "tableTo": "taxonomy_values", + "columnsFrom": [ + "taxonomy_value_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_attribute_values_select_for_brand_members": { + "name": "brand_attribute_values_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_attribute_values_insert_by_brand_member": { + "name": "brand_attribute_values_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_attribute_values_update_by_brand_member": { + "name": "brand_attribute_values_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_attribute_values_delete_by_brand_member": { + "name": "brand_attribute_values_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_certifications": { + "name": "brand_certifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certification_code": { + "name": "certification_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_name": { + "name": "institute_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_email": { + "name": "institute_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_website": { + "name": "institute_website", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_address_line_1": { + "name": "institute_address_line_1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_address_line_2": { + "name": "institute_address_line_2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_city": { + "name": "institute_city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_state": { + "name": "institute_state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_zip": { + "name": "institute_zip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_country_code": { + "name": "institute_country_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "issue_date": { + "name": "issue_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "expiry_date": { + "name": "expiry_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "certification_path": { + "name": "certification_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "brand_certifications_brand_id_brands_id_fk": { + "name": "brand_certifications_brand_id_brands_id_fk", + "tableFrom": "brand_certifications", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_certifications_select_for_brand_members": { + "name": "brand_certifications_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_certifications_insert_by_brand_member": { + "name": "brand_certifications_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_certifications_update_by_brand_member": { + "name": "brand_certifications_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_certifications_delete_by_brand_member": { + "name": "brand_certifications_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_operators": { + "name": "brand_operators", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "legal_name": { + "name": "legal_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "website": { + "name": "website", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_line_1": { + "name": "address_line_1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_line_2": { + "name": "address_line_2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "city": { + "name": "city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "zip": { + "name": "zip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "country_code": { + "name": "country_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "brand_operators_brand_id_brands_id_fk": { + "name": "brand_operators_brand_id_brands_id_fk", + "tableFrom": "brand_operators", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_operators_select_for_brand_members": { + "name": "brand_operators_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_operators_insert_by_brand_member": { + "name": "brand_operators_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_operators_update_by_brand_member": { + "name": "brand_operators_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_operators_delete_by_brand_member": { + "name": "brand_operators_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_manufacturers": { + "name": "brand_manufacturers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "legal_name": { + "name": "legal_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "website": { + "name": "website", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_line_1": { + "name": "address_line_1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_line_2": { + "name": "address_line_2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "city": { + "name": "city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "zip": { + "name": "zip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "country_code": { + "name": "country_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_manufacturers_brand_name_unq": { + "name": "brand_manufacturers_brand_name_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_manufacturers_brand_id_brands_id_fk": { + "name": "brand_manufacturers_brand_id_brands_id_fk", + "tableFrom": "brand_manufacturers", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_manufacturers_select_for_brand_members": { + "name": "brand_manufacturers_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_manufacturers_insert_by_brand_member": { + "name": "brand_manufacturers_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_manufacturers_update_by_brand_member": { + "name": "brand_manufacturers_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_manufacturers_delete_by_brand_member": { + "name": "brand_manufacturers_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_materials": { + "name": "brand_materials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certification_id": { + "name": "certification_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "recyclable": { + "name": "recyclable", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "country_of_origin": { + "name": "country_of_origin", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "brand_materials_brand_id_brands_id_fk": { + "name": "brand_materials_brand_id_brands_id_fk", + "tableFrom": "brand_materials", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "brand_materials_certification_id_brand_certifications_id_fk": { + "name": "brand_materials_certification_id_brand_certifications_id_fk", + "tableFrom": "brand_materials", + "tableTo": "brand_certifications", + "columnsFrom": [ + "certification_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_materials_select_for_brand_members": { + "name": "brand_materials_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_materials_insert_by_brand_member": { + "name": "brand_materials_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_materials_update_by_brand_member": { + "name": "brand_materials_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_materials_delete_by_brand_member": { + "name": "brand_materials_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_seasons": { + "name": "brand_seasons", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "start_date": { + "name": "start_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "end_date": { + "name": "end_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "ongoing": { + "name": "ongoing", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_seasons_brand_name_unq": { + "name": "brand_seasons_brand_name_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_seasons_brand_id_brands_id_fk": { + "name": "brand_seasons_brand_id_brands_id_fk", + "tableFrom": "brand_seasons", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_seasons_select_for_brand_members": { + "name": "brand_seasons_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_seasons_insert_by_brand_members": { + "name": "brand_seasons_insert_by_brand_members", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_seasons_update_by_brand_members": { + "name": "brand_seasons_update_by_brand_members", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_seasons_delete_by_brand_members": { + "name": "brand_seasons_delete_by_brand_members", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.products": { + "name": "products", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "product_handle": { + "name": "product_handle", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "manufacturer_id": { + "name": "manufacturer_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "image_path": { + "name": "image_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "category_id": { + "name": "category_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unpublished'" + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "source_integration_id": { + "name": "source_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "products_brand_id_product_handle_unq": { + "name": "products_brand_id_product_handle_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "product_handle", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_brand_id": { + "name": "idx_products_brand_id", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_brand_status": { + "name": "idx_products_brand_status", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_brand_created": { + "name": "idx_products_brand_created", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_brand_product_handle": { + "name": "idx_products_brand_product_handle", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "product_handle", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_brand_category": { + "name": "idx_products_brand_category", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "category_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_brand_season": { + "name": "idx_products_brand_season", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_brand_name": { + "name": "idx_products_brand_name", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_name": { + "name": "idx_products_name", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "products_brand_id_brands_id_fk": { + "name": "products_brand_id_brands_id_fk", + "tableFrom": "products", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "products_manufacturer_id_brand_manufacturers_id_fk": { + "name": "products_manufacturer_id_brand_manufacturers_id_fk", + "tableFrom": "products", + "tableTo": "brand_manufacturers", + "columnsFrom": [ + "manufacturer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "products_category_id_taxonomy_categories_id_fk": { + "name": "products_category_id_taxonomy_categories_id_fk", + "tableFrom": "products", + "tableTo": "taxonomy_categories", + "columnsFrom": [ + "category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "products_season_id_brand_seasons_id_fk": { + "name": "products_season_id_brand_seasons_id_fk", + "tableFrom": "products", + "tableTo": "brand_seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "products_source_integration_id_brand_integrations_id_fk": { + "name": "products_source_integration_id_brand_integrations_id_fk", + "tableFrom": "products", + "tableTo": "brand_integrations", + "columnsFrom": [ + "source_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "products_select_for_brand_members": { + "name": "products_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "products_insert_by_brand_members": { + "name": "products_insert_by_brand_members", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "products_update_by_brand_members": { + "name": "products_update_by_brand_members", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "products_delete_by_brand_members": { + "name": "products_delete_by_brand_members", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_variants": { + "name": "product_variants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "barcode": { + "name": "barcode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sku": { + "name": "sku", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "upid": { + "name": "upid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "image_path": { + "name": "image_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_integration": { + "name": "source_integration", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_external_id": { + "name": "source_external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_ghost": { + "name": "is_ghost", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_product_variants_product_id": { + "name": "idx_product_variants_product_id", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_variants_product_created": { + "name": "idx_product_variants_product_created", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_variants_upid": { + "name": "idx_product_variants_upid", + "columns": [ + { + "expression": "upid", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "where": "(upid IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_unique_upid_global": { + "name": "idx_unique_upid_global", + "columns": [ + { + "expression": "upid", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "upid IS NOT NULL AND upid != ''", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_unique_barcode_per_brand": { + "name": "idx_unique_barcode_per_brand", + "columns": [ + { + "expression": "barcode", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "get_product_brand_id(product_id)", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "barcode IS NOT NULL AND barcode != ''", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_variants_product_id_products_id_fk": { + "name": "product_variants_product_id_products_id_fk", + "tableFrom": "product_variants", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "product_variants_select_for_brand_members": { + "name": "product_variants_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_variants_insert_by_brand_member": { + "name": "product_variants_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_variants_update_by_brand_member": { + "name": "product_variants_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_variants_delete_by_brand_member": { + "name": "product_variants_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_materials": { + "name": "product_materials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "brand_material_id": { + "name": "brand_material_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "percentage": { + "name": "percentage", + "type": "numeric(6, 2)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "product_materials_product_material_unq": { + "name": "product_materials_product_material_unq", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "brand_material_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_materials_product_id": { + "name": "idx_product_materials_product_id", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_materials_product_created": { + "name": "idx_product_materials_product_created", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_materials_product_id_products_id_fk": { + "name": "product_materials_product_id_products_id_fk", + "tableFrom": "product_materials", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "product_materials_brand_material_id_brand_materials_id_fk": { + "name": "product_materials_brand_material_id_brand_materials_id_fk", + "tableFrom": "product_materials", + "tableTo": "brand_materials", + "columnsFrom": [ + "brand_material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "product_materials_select_for_brand_members": { + "name": "product_materials_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_materials_insert_by_brand_member": { + "name": "product_materials_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_materials_update_by_brand_member": { + "name": "product_materials_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_materials_delete_by_brand_member": { + "name": "product_materials_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_journey_steps": { + "name": "product_journey_steps", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sort_index": { + "name": "sort_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "step_type": { + "name": "step_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "operator_id": { + "name": "operator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "product_journey_steps_product_sort_operator_unq": { + "name": "product_journey_steps_product_sort_operator_unq", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_index", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "operator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_journey_steps_product_id": { + "name": "idx_product_journey_steps_product_id", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_journey_steps_product_sort": { + "name": "idx_product_journey_steps_product_sort", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "sort_index", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_journey_steps_product_id_products_id_fk": { + "name": "product_journey_steps_product_id_products_id_fk", + "tableFrom": "product_journey_steps", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "product_journey_steps_operator_id_brand_operators_id_fk": { + "name": "product_journey_steps_operator_id_brand_operators_id_fk", + "tableFrom": "product_journey_steps", + "tableTo": "brand_operators", + "columnsFrom": [ + "operator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "product_journey_steps_select_for_brand_members": { + "name": "product_journey_steps_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_journey_steps_insert_by_brand_member": { + "name": "product_journey_steps_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_journey_steps_update_by_brand_member": { + "name": "product_journey_steps_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_journey_steps_delete_by_brand_member": { + "name": "product_journey_steps_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_environment": { + "name": "product_environment", + "schema": "", + "columns": { + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "numeric(12, 4)", + "primaryKey": false, + "notNull": false + }, + "unit": { + "name": "unit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metric": { + "name": "metric", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "product_environment_product_id_products_id_fk": { + "name": "product_environment_product_id_products_id_fk", + "tableFrom": "product_environment", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "product_environment_pkey": { + "name": "product_environment_pkey", + "columns": [ + "product_id", + "metric" + ] + } + }, + "uniqueConstraints": {}, + "policies": { + "product_environment_select_for_brand_members": { + "name": "product_environment_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_environment_insert_by_brand_member": { + "name": "product_environment_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_environment_update_by_brand_member": { + "name": "product_environment_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_environment_delete_by_brand_member": { + "name": "product_environment_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_tags": { + "name": "product_tags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tag_id": { + "name": "tag_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tags_on_product_tag_product_unq": { + "name": "tags_on_product_tag_product_unq", + "columns": [ + { + "expression": "tag_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tags_on_product_tag_id_idx": { + "name": "tags_on_product_tag_id_idx", + "columns": [ + { + "expression": "tag_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tags_on_product_product_id_idx": { + "name": "tags_on_product_product_id_idx", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_tags_tag_id_brand_tags_id_fk": { + "name": "product_tags_tag_id_brand_tags_id_fk", + "tableFrom": "product_tags", + "tableTo": "brand_tags", + "columnsFrom": [ + "tag_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "product_tags_product_id_products_id_fk": { + "name": "product_tags_product_id_products_id_fk", + "tableFrom": "product_tags", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "tags_on_product_select_for_brand_members": { + "name": "tags_on_product_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "tags_on_product_insert_by_brand_members": { + "name": "tags_on_product_insert_by_brand_members", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "tags_on_product_delete_by_brand_members": { + "name": "tags_on_product_delete_by_brand_members", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_variant_attributes": { + "name": "product_variant_attributes", + "schema": "", + "columns": { + "variant_id": { + "name": "variant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "attribute_value_id": { + "name": "attribute_value_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_product_variant_attributes_variant": { + "name": "idx_product_variant_attributes_variant", + "columns": [ + { + "expression": "variant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_variant_attributes_variant_id_product_variants_id_fk": { + "name": "product_variant_attributes_variant_id_product_variants_id_fk", + "tableFrom": "product_variant_attributes", + "tableTo": "product_variants", + "columnsFrom": [ + "variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "product_variant_attributes_attribute_value_id_brand_attribute_values_id_fk": { + "name": "product_variant_attributes_attribute_value_id_brand_attribute_values_id_fk", + "tableFrom": "product_variant_attributes", + "tableTo": "brand_attribute_values", + "columnsFrom": [ + "attribute_value_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "product_variant_attributes_pkey": { + "name": "product_variant_attributes_pkey", + "columns": [ + "variant_id", + "attribute_value_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": { + "product_variant_attributes_select_for_brand_members": { + "name": "product_variant_attributes_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "product_variant_attributes_insert_by_brand_member": { + "name": "product_variant_attributes_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "product_variant_attributes_update_by_brand_member": { + "name": "product_variant_attributes_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "product_variant_attributes_delete_by_brand_member": { + "name": "product_variant_attributes_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_weight": { + "name": "product_weight", + "schema": "", + "columns": { + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "weight": { + "name": "weight", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "weight_unit": { + "name": "weight_unit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "product_weight_product_id_products_id_fk": { + "name": "product_weight_product_id_products_id_fk", + "tableFrom": "product_weight", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "product_weight_select_for_brand_members": { + "name": "product_weight_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_weight_insert_by_brand_member": { + "name": "product_weight_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_weight_update_by_brand_member": { + "name": "product_weight_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_weight_delete_by_brand_member": { + "name": "product_weight_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_commercial": { + "name": "product_commercial", + "schema": "", + "columns": { + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "webshop_url": { + "name": "webshop_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sales_status": { + "name": "sales_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "product_commercial_product_id_products_id_fk": { + "name": "product_commercial_product_id_products_id_fk", + "tableFrom": "product_commercial", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "product_commercial_select_for_brand_members": { + "name": "product_commercial_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_commercial_insert_by_brand_member": { + "name": "product_commercial_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_commercial_update_by_brand_member": { + "name": "product_commercial_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_commercial_delete_by_brand_member": { + "name": "product_commercial_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.variant_commercial": { + "name": "variant_commercial", + "schema": "", + "columns": { + "variant_id": { + "name": "variant_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "webshop_url": { + "name": "webshop_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sales_status": { + "name": "sales_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_integration": { + "name": "source_integration", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_external_id": { + "name": "source_external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "variant_commercial_variant_id_product_variants_id_fk": { + "name": "variant_commercial_variant_id_product_variants_id_fk", + "tableFrom": "variant_commercial", + "tableTo": "product_variants", + "columnsFrom": [ + "variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "variant_commercial_select_for_brand_members": { + "name": "variant_commercial_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_commercial_insert_by_brand_member": { + "name": "variant_commercial_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_commercial_update_by_brand_member": { + "name": "variant_commercial_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_commercial_delete_by_brand_member": { + "name": "variant_commercial_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.variant_environment": { + "name": "variant_environment", + "schema": "", + "columns": { + "variant_id": { + "name": "variant_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "carbon_kg_co2e": { + "name": "carbon_kg_co2e", + "type": "numeric(12, 4)", + "primaryKey": false, + "notNull": false + }, + "water_liters": { + "name": "water_liters", + "type": "numeric(12, 2)", + "primaryKey": false, + "notNull": false + }, + "source_integration": { + "name": "source_integration", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_external_id": { + "name": "source_external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "variant_environment_variant_id_product_variants_id_fk": { + "name": "variant_environment_variant_id_product_variants_id_fk", + "tableFrom": "variant_environment", + "tableTo": "product_variants", + "columnsFrom": [ + "variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "variant_environment_select_for_brand_members": { + "name": "variant_environment_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_environment_insert_by_brand_member": { + "name": "variant_environment_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_environment_update_by_brand_member": { + "name": "variant_environment_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_environment_delete_by_brand_member": { + "name": "variant_environment_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.variant_materials": { + "name": "variant_materials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "variant_id": { + "name": "variant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "brand_material_id": { + "name": "brand_material_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "percentage": { + "name": "percentage", + "type": "numeric(6, 2)", + "primaryKey": false, + "notNull": false + }, + "source_integration": { + "name": "source_integration", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_external_id": { + "name": "source_external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "variant_materials_variant_material_unq": { + "name": "variant_materials_variant_material_unq", + "columns": [ + { + "expression": "variant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "brand_material_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_variant_materials_variant_id": { + "name": "idx_variant_materials_variant_id", + "columns": [ + { + "expression": "variant_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "variant_materials_variant_id_product_variants_id_fk": { + "name": "variant_materials_variant_id_product_variants_id_fk", + "tableFrom": "variant_materials", + "tableTo": "product_variants", + "columnsFrom": [ + "variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "variant_materials_brand_material_id_brand_materials_id_fk": { + "name": "variant_materials_brand_material_id_brand_materials_id_fk", + "tableFrom": "variant_materials", + "tableTo": "brand_materials", + "columnsFrom": [ + "brand_material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "variant_materials_select_for_brand_members": { + "name": "variant_materials_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_materials_insert_by_brand_member": { + "name": "variant_materials_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_materials_update_by_brand_member": { + "name": "variant_materials_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )", + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_materials_delete_by_brand_member": { + "name": "variant_materials_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.variant_weight": { + "name": "variant_weight", + "schema": "", + "columns": { + "variant_id": { + "name": "variant_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "weight": { + "name": "weight", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "weight_unit": { + "name": "weight_unit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_integration": { + "name": "source_integration", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_external_id": { + "name": "source_external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "variant_weight_variant_id_product_variants_id_fk": { + "name": "variant_weight_variant_id_product_variants_id_fk", + "tableFrom": "variant_weight", + "tableTo": "product_variants", + "columnsFrom": [ + "variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "variant_weight_select_for_brand_members": { + "name": "variant_weight_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_weight_insert_by_brand_member": { + "name": "variant_weight_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_weight_update_by_brand_member": { + "name": "variant_weight_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_weight_delete_by_brand_member": { + "name": "variant_weight_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.variant_journey_steps": { + "name": "variant_journey_steps", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "variant_id": { + "name": "variant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sort_index": { + "name": "sort_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "step_type": { + "name": "step_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "operator_id": { + "name": "operator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_integration": { + "name": "source_integration", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_external_id": { + "name": "source_external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "variant_journey_steps_variant_sort_operator_unq": { + "name": "variant_journey_steps_variant_sort_operator_unq", + "columns": [ + { + "expression": "variant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_index", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "operator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_variant_journey_steps_variant_id": { + "name": "idx_variant_journey_steps_variant_id", + "columns": [ + { + "expression": "variant_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_variant_journey_steps_variant_sort": { + "name": "idx_variant_journey_steps_variant_sort", + "columns": [ + { + "expression": "variant_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "sort_index", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_variant_journey_steps_operator_id": { + "name": "idx_variant_journey_steps_operator_id", + "columns": [ + { + "expression": "operator_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "variant_journey_steps_variant_id_product_variants_id_fk": { + "name": "variant_journey_steps_variant_id_product_variants_id_fk", + "tableFrom": "variant_journey_steps", + "tableTo": "product_variants", + "columnsFrom": [ + "variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "variant_journey_steps_operator_id_brand_operators_id_fk": { + "name": "variant_journey_steps_operator_id_brand_operators_id_fk", + "tableFrom": "variant_journey_steps", + "tableTo": "brand_operators", + "columnsFrom": [ + "operator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "variant_journey_steps_select_for_brand_members": { + "name": "variant_journey_steps_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_journey_steps_insert_by_brand_member": { + "name": "variant_journey_steps_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_journey_steps_update_by_brand_member": { + "name": "variant_journey_steps_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )", + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_journey_steps_delete_by_brand_member": { + "name": "variant_journey_steps_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_passports": { + "name": "product_passports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "upid": { + "name": "upid", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "working_variant_id": { + "name": "working_variant_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "current_version_id": { + "name": "current_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "dirty": { + "name": "dirty", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "orphaned_at": { + "name": "orphaned_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "sku": { + "name": "sku", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "barcode": { + "name": "barcode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "first_published_at": { + "name": "first_published_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_product_passports_brand_id": { + "name": "idx_product_passports_brand_id", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_passports_brand_dirty": { + "name": "idx_product_passports_brand_dirty", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "dirty", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "bool_ops" + } + ], + "isUnique": false, + "where": "dirty = true", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_passports_working_variant_id": { + "name": "idx_product_passports_working_variant_id", + "columns": [ + { + "expression": "working_variant_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "where": "working_variant_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_passports_status": { + "name": "idx_product_passports_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_passports_orphaned_at": { + "name": "idx_product_passports_orphaned_at", + "columns": [ + { + "expression": "orphaned_at", + "isExpression": false, + "asc": false, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "where": "status = 'orphaned'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_passports_brand_id_brands_id_fk": { + "name": "product_passports_brand_id_brands_id_fk", + "tableFrom": "product_passports", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "product_passports_working_variant_id_product_variants_id_fk": { + "name": "product_passports_working_variant_id_product_variants_id_fk", + "tableFrom": "product_passports", + "tableTo": "product_variants", + "columnsFrom": [ + "working_variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "product_passports_upid_unique": { + "name": "product_passports_upid_unique", + "nullsNotDistinct": false, + "columns": [ + "upid" + ] + } + }, + "policies": { + "product_passports_select_for_brand_members": { + "name": "product_passports_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "product_passports_insert_by_brand_members": { + "name": "product_passports_insert_by_brand_members", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "product_passports_update_by_brand_members": { + "name": "product_passports_update_by_brand_members", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "product_passports_delete_by_brand_members": { + "name": "product_passports_delete_by_brand_members", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "product_passports_select_public": { + "name": "product_passports_select_public", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "anon" + ], + "using": "\n current_version_id IS NOT NULL\n AND (\n working_variant_id IS NULL\n OR\n EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = working_variant_id\n AND p.status = 'published'\n )\n )\n " + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_passport_versions": { + "name": "product_passport_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "passport_id": { + "name": "passport_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "version_number": { + "name": "version_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "data_snapshot": { + "name": "data_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "schema_version": { + "name": "schema_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "published_at": { + "name": "published_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_product_passport_versions_passport_version": { + "name": "idx_product_passport_versions_passport_version", + "columns": [ + { + "expression": "passport_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_passport_versions_passport_id": { + "name": "idx_product_passport_versions_passport_id", + "columns": [ + { + "expression": "passport_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_passport_versions_passport_published": { + "name": "idx_product_passport_versions_passport_published", + "columns": [ + { + "expression": "passport_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "published_at", + "isExpression": false, + "asc": false, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_passport_versions_passport_id_product_passports_id_fk": { + "name": "product_passport_versions_passport_id_product_passports_id_fk", + "tableFrom": "product_passport_versions", + "tableTo": "product_passports", + "columnsFrom": [ + "passport_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "product_passport_versions_select_for_brand_members": { + "name": "product_passport_versions_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_passports\n WHERE product_passports.id = passport_id\n AND is_brand_member(product_passports.brand_id)\n )" + }, + "product_passport_versions_insert_by_brand_members": { + "name": "product_passport_versions_insert_by_brand_members", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM product_passports\n WHERE product_passports.id = passport_id\n AND is_brand_member(product_passports.brand_id)\n )" + }, + "product_passport_versions_select_public": { + "name": "product_passport_versions_select_public", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "anon" + ], + "using": "EXISTS (\n SELECT 1 FROM product_passports\n WHERE product_passports.id = passport_id\n AND product_passports.current_version_id IS NOT NULL\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.file_assets": { + "name": "file_assets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bytes": { + "name": "bytes", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "file_assets_bucket_path_unq": { + "name": "file_assets_bucket_path_unq", + "columns": [ + { + "expression": "bucket", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "file_assets_brand_id_brands_id_fk": { + "name": "file_assets_brand_id_brands_id_fk", + "tableFrom": "file_assets", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "file_assets_select_for_brand_members": { + "name": "file_assets_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "(brand_id IS NULL) OR is_brand_member(brand_id)" + }, + "file_assets_insert_by_brand_member_or_system": { + "name": "file_assets_insert_by_brand_member_or_system", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "(brand_id IS NULL) OR is_brand_member(brand_id)" + }, + "file_assets_update_by_brand_member_or_system": { + "name": "file_assets_update_by_brand_member_or_system", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "(brand_id IS NULL) OR is_brand_member(brand_id)" + }, + "file_assets_delete_by_brand_member_or_system": { + "name": "file_assets_delete_by_brand_member_or_system", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "(brand_id IS NULL) OR is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.import_jobs": { + "name": "import_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "filename": { + "name": "filename", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'CREATE'" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "commit_started_at": { + "name": "commit_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'PENDING'" + }, + "requires_value_approval": { + "name": "requires_value_approval", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "has_exportable_failures": { + "name": "has_exportable_failures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "summary": { + "name": "summary", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "correction_file_path": { + "name": "correction_file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "correction_download_url": { + "name": "correction_download_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "correction_expires_at": { + "name": "correction_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "user_email": { + "name": "user_email", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "import_jobs_brand_id_brands_id_fk": { + "name": "import_jobs_brand_id_brands_id_fk", + "tableFrom": "import_jobs", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "import_jobs_select_for_brand_members": { + "name": "import_jobs_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "import_jobs_insert_by_brand_member": { + "name": "import_jobs_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "import_jobs_update_by_brand_member": { + "name": "import_jobs_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "import_jobs_delete_by_brand_member": { + "name": "import_jobs_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.import_rows": { + "name": "import_rows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "job_id": { + "name": "job_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "row_number": { + "name": "row_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "raw": { + "name": "raw", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "normalized": { + "name": "normalized", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'PENDING'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "import_rows_job_row_unq": { + "name": "import_rows_job_row_unq", + "columns": [ + { + "expression": "job_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "row_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "import_rows_job_id_import_jobs_id_fk": { + "name": "import_rows_job_id_import_jobs_id_fk", + "tableFrom": "import_rows", + "tableTo": "import_jobs", + "columnsFrom": [ + "job_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "import_rows_select_for_brand_members": { + "name": "import_rows_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM import_jobs \n WHERE import_jobs.id = job_id \n AND is_brand_member(import_jobs.brand_id)\n )" + }, + "import_rows_insert_by_brand_member": { + "name": "import_rows_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM import_jobs \n WHERE import_jobs.id = job_id \n AND is_brand_member(import_jobs.brand_id)\n )" + }, + "import_rows_update_by_brand_member": { + "name": "import_rows_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM import_jobs \n WHERE import_jobs.id = job_id \n AND is_brand_member(import_jobs.brand_id)\n )" + }, + "import_rows_delete_by_brand_member": { + "name": "import_rows_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM import_jobs \n WHERE import_jobs.id = job_id \n AND is_brand_member(import_jobs.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.export_jobs": { + "name": "export_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_email": { + "name": "user_email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'PENDING'" + }, + "selection_mode": { + "name": "selection_mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "include_ids": { + "name": "include_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "exclude_ids": { + "name": "exclude_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "filter_state": { + "name": "filter_state", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "search_query": { + "name": "search_query", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "total_products": { + "name": "total_products", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "products_processed": { + "name": "products_processed", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "download_url": { + "name": "download_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "export_jobs_brand_id_brands_id_fk": { + "name": "export_jobs_brand_id_brands_id_fk", + "tableFrom": "export_jobs", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "export_jobs_user_id_users_id_fk": { + "name": "export_jobs_user_id_users_id_fk", + "tableFrom": "export_jobs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "export_jobs_select_for_brand_members": { + "name": "export_jobs_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "export_jobs_insert_by_brand_member": { + "name": "export_jobs_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "export_jobs_update_by_brand_member": { + "name": "export_jobs_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "export_jobs_delete_by_brand_member": { + "name": "export_jobs_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.qr_export_jobs": { + "name": "qr_export_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_email": { + "name": "user_email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'PENDING'" + }, + "selection_mode": { + "name": "selection_mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "include_ids": { + "name": "include_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "exclude_ids": { + "name": "exclude_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "filter_state": { + "name": "filter_state", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "search_query": { + "name": "search_query", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "custom_domain": { + "name": "custom_domain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "total_products": { + "name": "total_products", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "total_variants": { + "name": "total_variants", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "eligible_variants": { + "name": "eligible_variants", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "variants_processed": { + "name": "variants_processed", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "download_url": { + "name": "download_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "qr_export_jobs_brand_id_brands_id_fk": { + "name": "qr_export_jobs_brand_id_brands_id_fk", + "tableFrom": "qr_export_jobs", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "qr_export_jobs_user_id_users_id_fk": { + "name": "qr_export_jobs_user_id_users_id_fk", + "tableFrom": "qr_export_jobs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "qr_export_jobs_select_for_brand_members": { + "name": "qr_export_jobs_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "qr_export_jobs_insert_by_brand_member": { + "name": "qr_export_jobs_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "qr_export_jobs_update_by_brand_member": { + "name": "qr_export_jobs_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)", + "withCheck": "is_brand_member(brand_id)" + }, + "qr_export_jobs_delete_by_brand_member": { + "name": "qr_export_jobs_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_notifications": { + "name": "user_notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resource_type": { + "name": "resource_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resource_id": { + "name": "resource_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "action_url": { + "name": "action_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action_data": { + "name": "action_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "seen_at": { + "name": "seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "dismissed_at": { + "name": "dismissed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_user_notifications_user_unread": { + "name": "idx_user_notifications_user_unread", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "(seen_at IS NULL AND dismissed_at IS NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_user_notifications_expires": { + "name": "idx_user_notifications_expires", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "(expires_at IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_user_notifications_resource": { + "name": "idx_user_notifications_resource", + "columns": [ + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "(resource_type IS NOT NULL AND resource_id IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_notifications_user_id_users_id_fk": { + "name": "user_notifications_user_id_users_id_fk", + "tableFrom": "user_notifications", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "user_notifications_brand_id_brands_id_fk": { + "name": "user_notifications_brand_id_brands_id_fk", + "tableFrom": "user_notifications", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "user_notifications_select_own": { + "name": "user_notifications_select_own", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "auth.uid() = user_id" + }, + "user_notifications_insert_service": { + "name": "user_notifications_insert_service", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "user_notifications_update_own": { + "name": "user_notifications_update_own", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "auth.uid() = user_id" + }, + "user_notifications_delete_own": { + "name": "user_notifications_delete_own", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "auth.uid() = user_id" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.value_mappings": { + "name": "value_mappings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_column": { + "name": "source_column", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "raw_value": { + "name": "raw_value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target": { + "name": "target", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "value_mappings_brand_col_raw_unq": { + "name": "value_mappings_brand_col_raw_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "source_column", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + }, + { + "expression": "raw_value", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "value_mappings_brand_id_brands_id_fk": { + "name": "value_mappings_brand_id_brands_id_fk", + "tableFrom": "value_mappings", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "value_mappings_delete_by_brand_member": { + "name": "value_mappings_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "value_mappings_insert_by_brand_member": { + "name": "value_mappings_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "value_mappings_select_for_brand_members": { + "name": "value_mappings_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "value_mappings_update_by_brand_member": { + "name": "value_mappings_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_integrations": { + "name": "brand_integrations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "credentials": { + "name": "credentials", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credentials_iv": { + "name": "credentials_iv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shop_domain": { + "name": "shop_domain", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sync_interval": { + "name": "sync_interval", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 86400 + }, + "last_sync_at": { + "name": "last_sync_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_primary": { + "name": "is_primary", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "match_identifier": { + "name": "match_identifier", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'barcode'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_integrations_brand_integration_unq": { + "name": "brand_integrations_brand_integration_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_integrations_brand_id": { + "name": "idx_brand_integrations_brand_id", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_integrations_status": { + "name": "idx_brand_integrations_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "brand_integrations_primary_unq": { + "name": "brand_integrations_primary_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"brand_integrations\".\"is_primary\" = TRUE", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_integrations_brand_id_brands_id_fk": { + "name": "brand_integrations_brand_id_brands_id_fk", + "tableFrom": "brand_integrations", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "brand_integrations_integration_id_integrations_id_fk": { + "name": "brand_integrations_integration_id_integrations_id_fk", + "tableFrom": "brand_integrations", + "tableTo": "integrations", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_integrations_select_for_brand_members": { + "name": "brand_integrations_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_integrations_insert_by_brand_member": { + "name": "brand_integrations_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_integrations_update_by_brand_member": { + "name": "brand_integrations_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_integrations_delete_by_brand_member": { + "name": "brand_integrations_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_field_configs": { + "name": "integration_field_configs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "field_key": { + "name": "field_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ownership_enabled": { + "name": "ownership_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "source_option_key": { + "name": "source_option_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_field_configs_integration_field_unq": { + "name": "integration_field_configs_integration_field_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "field_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_field_configs_integration": { + "name": "idx_integration_field_configs_integration", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_field_configs_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_field_configs_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_field_configs", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_field_configs_select_for_brand_members": { + "name": "integration_field_configs_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_field_configs_insert_by_brand_member": { + "name": "integration_field_configs_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_field_configs_update_by_brand_member": { + "name": "integration_field_configs_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_field_configs_delete_by_brand_member": { + "name": "integration_field_configs_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integrations": { + "name": "integrations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "auth_type": { + "name": "auth_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integrations_slug_unq": { + "name": "integrations_slug_unq", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integrations_select_for_authenticated": { + "name": "integrations_select_for_authenticated", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "true" + }, + "integrations_insert_by_service_role": { + "name": "integrations_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "integrations_update_by_service_role": { + "name": "integrations_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true" + }, + "integrations_delete_by_service_role": { + "name": "integrations_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_states": { + "name": "oauth_states", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "integration_slug": { + "name": "integration_slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "shop_domain": { + "name": "shop_domain", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_oauth_states_state": { + "name": "idx_oauth_states_state", + "columns": [ + { + "expression": "state", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_oauth_states_expires": { + "name": "idx_oauth_states_expires", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_states_brand_id_brands_id_fk": { + "name": "oauth_states_brand_id_brands_id_fk", + "tableFrom": "oauth_states", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "oauth_states_select_by_service_role": { + "name": "oauth_states_select_by_service_role", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "service_role" + ], + "using": "true" + }, + "oauth_states_insert_by_service_role": { + "name": "oauth_states_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "oauth_states_update_by_service_role": { + "name": "oauth_states_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true" + }, + "oauth_states_delete_by_service_role": { + "name": "oauth_states_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pending_installations": { + "name": "pending_installations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "shop_domain": { + "name": "shop_domain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credentials": { + "name": "credentials", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credentials_iv": { + "name": "credentials_iv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "pending_installations_shop_domain_unq": { + "name": "pending_installations_shop_domain_unq", + "columns": [ + { + "expression": "shop_domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_pending_installations_expires": { + "name": "idx_pending_installations_expires", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "pending_installations_select_by_service_role": { + "name": "pending_installations_select_by_service_role", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "service_role" + ], + "using": "true" + }, + "pending_installations_insert_by_service_role": { + "name": "pending_installations_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "pending_installations_update_by_service_role": { + "name": "pending_installations_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true" + }, + "pending_installations_delete_by_service_role": { + "name": "pending_installations_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_sync_jobs": { + "name": "integration_sync_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "trigger_type": { + "name": "trigger_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'scheduled'" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "variants_processed": { + "name": "variants_processed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "variants_created": { + "name": "variants_created", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "variants_updated": { + "name": "variants_updated", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "variants_failed": { + "name": "variants_failed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "variants_skipped": { + "name": "variants_skipped", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "products_total": { + "name": "products_total", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "products_processed": { + "name": "products_processed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "products_created": { + "name": "products_created", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "products_updated": { + "name": "products_updated", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "products_failed": { + "name": "products_failed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "products_skipped": { + "name": "products_skipped", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "entities_created": { + "name": "entities_created", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "error_summary": { + "name": "error_summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_log": { + "name": "error_log", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_integration_sync_jobs_integration": { + "name": "idx_integration_sync_jobs_integration", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_sync_jobs_status": { + "name": "idx_integration_sync_jobs_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_sync_jobs_created": { + "name": "idx_integration_sync_jobs_created", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_sync_jobs_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_sync_jobs_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_sync_jobs", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_sync_jobs_select_for_brand_members": { + "name": "integration_sync_jobs_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_sync_jobs_insert_by_brand_member": { + "name": "integration_sync_jobs_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_sync_jobs_update_by_brand_member": { + "name": "integration_sync_jobs_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_sync_jobs_delete_by_brand_member": { + "name": "integration_sync_jobs_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.promotion_operations": { + "name": "promotion_operations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "new_primary_integration_id": { + "name": "new_primary_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "old_primary_integration_id": { + "name": "old_primary_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'preparing'" + }, + "phase": { + "name": "phase", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "variants_processed": { + "name": "variants_processed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_variants": { + "name": "total_variants", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "products_created": { + "name": "products_created", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "products_archived": { + "name": "products_archived", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "variants_moved": { + "name": "variants_moved", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "variants_orphaned": { + "name": "variants_orphaned", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "attributes_created": { + "name": "attributes_created", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_promotion_operations_brand_id": { + "name": "idx_promotion_operations_brand_id", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_promotion_operations_status": { + "name": "idx_promotion_operations_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "promotion_operations_brand_id_brands_id_fk": { + "name": "promotion_operations_brand_id_brands_id_fk", + "tableFrom": "promotion_operations", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "promotion_operations_new_primary_integration_id_brand_integrations_id_fk": { + "name": "promotion_operations_new_primary_integration_id_brand_integrations_id_fk", + "tableFrom": "promotion_operations", + "tableTo": "brand_integrations", + "columnsFrom": [ + "new_primary_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "promotion_operations_old_primary_integration_id_brand_integrations_id_fk": { + "name": "promotion_operations_old_primary_integration_id_brand_integrations_id_fk", + "tableFrom": "promotion_operations", + "tableTo": "brand_integrations", + "columnsFrom": [ + "old_primary_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "promotion_operations_select_for_brand_members": { + "name": "promotion_operations_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "promotion_operations_insert_by_brand_member": { + "name": "promotion_operations_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "promotion_operations_update_by_brand_member": { + "name": "promotion_operations_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "promotion_operations_delete_by_brand_member": { + "name": "promotion_operations_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_certification_links": { + "name": "integration_certification_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "certification_id": { + "name": "certification_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_name": { + "name": "external_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_certification_links_integration_external_unq": { + "name": "integration_certification_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_certification_links_integration_cert_unq": { + "name": "integration_certification_links_integration_cert_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "certification_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_certification_links_cert": { + "name": "idx_integration_certification_links_cert", + "columns": [ + { + "expression": "certification_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_certification_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_certification_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_certification_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_certification_links_certification_id_brand_certifications_id_fk": { + "name": "integration_certification_links_certification_id_brand_certifications_id_fk", + "tableFrom": "integration_certification_links", + "tableTo": "brand_certifications", + "columnsFrom": [ + "certification_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_certification_links_select_for_brand_members": { + "name": "integration_certification_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_certification_links_insert_by_brand_member": { + "name": "integration_certification_links_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_certification_links_update_by_brand_member": { + "name": "integration_certification_links_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_certification_links_delete_by_brand_member": { + "name": "integration_certification_links_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_manufacturer_links": { + "name": "integration_manufacturer_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "manufacturer_id": { + "name": "manufacturer_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_name": { + "name": "external_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_manufacturer_links_integration_external_unq": { + "name": "integration_manufacturer_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_manufacturer_links_integration_mfr_unq": { + "name": "integration_manufacturer_links_integration_mfr_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "manufacturer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_manufacturer_links_mfr": { + "name": "idx_integration_manufacturer_links_mfr", + "columns": [ + { + "expression": "manufacturer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_manufacturer_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_manufacturer_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_manufacturer_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_manufacturer_links_manufacturer_id_brand_manufacturers_id_fk": { + "name": "integration_manufacturer_links_manufacturer_id_brand_manufacturers_id_fk", + "tableFrom": "integration_manufacturer_links", + "tableTo": "brand_manufacturers", + "columnsFrom": [ + "manufacturer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_manufacturer_links_select_for_brand_members": { + "name": "integration_manufacturer_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_manufacturer_links_insert_by_brand_member": { + "name": "integration_manufacturer_links_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_manufacturer_links_update_by_brand_member": { + "name": "integration_manufacturer_links_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_manufacturer_links_delete_by_brand_member": { + "name": "integration_manufacturer_links_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_material_links": { + "name": "integration_material_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "material_id": { + "name": "material_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_name": { + "name": "external_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_material_links_integration_external_unq": { + "name": "integration_material_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_material_links_integration_material_unq": { + "name": "integration_material_links_integration_material_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "material_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_material_links_material": { + "name": "idx_integration_material_links_material", + "columns": [ + { + "expression": "material_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_material_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_material_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_material_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_material_links_material_id_brand_materials_id_fk": { + "name": "integration_material_links_material_id_brand_materials_id_fk", + "tableFrom": "integration_material_links", + "tableTo": "brand_materials", + "columnsFrom": [ + "material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_material_links_select_for_brand_members": { + "name": "integration_material_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_material_links_insert_by_brand_member": { + "name": "integration_material_links_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_material_links_update_by_brand_member": { + "name": "integration_material_links_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_material_links_delete_by_brand_member": { + "name": "integration_material_links_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_operator_links": { + "name": "integration_operator_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "operator_id": { + "name": "operator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_name": { + "name": "external_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_operator_links_integration_external_unq": { + "name": "integration_operator_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_operator_links_integration_operator_unq": { + "name": "integration_operator_links_integration_operator_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "operator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_operator_links_operator": { + "name": "idx_integration_operator_links_operator", + "columns": [ + { + "expression": "operator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_operator_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_operator_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_operator_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_operator_links_operator_id_brand_operators_id_fk": { + "name": "integration_operator_links_operator_id_brand_operators_id_fk", + "tableFrom": "integration_operator_links", + "tableTo": "brand_operators", + "columnsFrom": [ + "operator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_operator_links_select_for_brand_members": { + "name": "integration_operator_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_operator_links_insert_by_brand_member": { + "name": "integration_operator_links_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_operator_links_update_by_brand_member": { + "name": "integration_operator_links_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_operator_links_delete_by_brand_member": { + "name": "integration_operator_links_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_season_links": { + "name": "integration_season_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_name": { + "name": "external_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_season_links_integration_external_unq": { + "name": "integration_season_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_season_links_integration_season_unq": { + "name": "integration_season_links_integration_season_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_season_links_season": { + "name": "idx_integration_season_links_season", + "columns": [ + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_season_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_season_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_season_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_season_links_season_id_brand_seasons_id_fk": { + "name": "integration_season_links_season_id_brand_seasons_id_fk", + "tableFrom": "integration_season_links", + "tableTo": "brand_seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_season_links_select_for_brand_members": { + "name": "integration_season_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_season_links_insert_by_brand_member": { + "name": "integration_season_links_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_season_links_update_by_brand_member": { + "name": "integration_season_links_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_season_links_delete_by_brand_member": { + "name": "integration_season_links_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_tag_links": { + "name": "integration_tag_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tag_id": { + "name": "tag_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_name": { + "name": "external_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_tag_links_integration_external_unq": { + "name": "integration_tag_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_tag_links_integration_tag_unq": { + "name": "integration_tag_links_integration_tag_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "tag_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_tag_links_tag": { + "name": "idx_integration_tag_links_tag", + "columns": [ + { + "expression": "tag_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_tag_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_tag_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_tag_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_tag_links_tag_id_brand_tags_id_fk": { + "name": "integration_tag_links_tag_id_brand_tags_id_fk", + "tableFrom": "integration_tag_links", + "tableTo": "brand_tags", + "columnsFrom": [ + "tag_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_tag_links_select_for_brand_members": { + "name": "integration_tag_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_tag_links_insert_by_brand_member": { + "name": "integration_tag_links_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_tag_links_update_by_brand_member": { + "name": "integration_tag_links_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_tag_links_delete_by_brand_member": { + "name": "integration_tag_links_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_product_links": { + "name": "integration_product_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_name": { + "name": "external_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_synced_hash": { + "name": "last_synced_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_canonical": { + "name": "is_canonical", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_product_links_integration_external_unq": { + "name": "integration_product_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_product_links_canonical_unq": { + "name": "integration_product_links_canonical_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "is_canonical = true", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_product_links_product": { + "name": "idx_integration_product_links_product", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_product_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_product_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_product_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_product_links_product_id_products_id_fk": { + "name": "integration_product_links_product_id_products_id_fk", + "tableFrom": "integration_product_links", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_product_links_select_for_brand_members": { + "name": "integration_product_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_product_links_insert_by_brand_member": { + "name": "integration_product_links_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_product_links_update_by_brand_member": { + "name": "integration_product_links_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_product_links_delete_by_brand_member": { + "name": "integration_product_links_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_variant_links": { + "name": "integration_variant_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "variant_id": { + "name": "variant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_product_id": { + "name": "external_product_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "external_sku": { + "name": "external_sku", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "external_barcode": { + "name": "external_barcode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_synced_hash": { + "name": "last_synced_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_variant_links_integration_external_unq": { + "name": "integration_variant_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_variant_links_variant": { + "name": "idx_integration_variant_links_variant", + "columns": [ + { + "expression": "variant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_variant_links_integration": { + "name": "idx_integration_variant_links_integration", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_variant_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_variant_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_variant_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_variant_links_variant_id_product_variants_id_fk": { + "name": "integration_variant_links_variant_id_product_variants_id_fk", + "tableFrom": "integration_variant_links", + "tableTo": "product_variants", + "columnsFrom": [ + "variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_variant_links_select_for_brand_members": { + "name": "integration_variant_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_variant_links_insert_by_service": { + "name": "integration_variant_links_insert_by_service", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "integration_variant_links_update_by_service": { + "name": "integration_variant_links_update_by_service", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true" + }, + "integration_variant_links_delete_by_service": { + "name": "integration_variant_links_delete_by_service", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.stripe_webhook_events": { + "name": "stripe_webhook_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "stripe_event_id": { + "name": "stripe_event_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "stripe_webhook_events_stripe_event_id_unq": { + "name": "stripe_webhook_events_stripe_event_id_unq", + "columns": [ + { + "expression": "stripe_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_stripe_webhook_events_processed_at": { + "name": "idx_stripe_webhook_events_processed_at", + "columns": [ + { + "expression": "processed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "stripe_webhook_events_select_by_service_role": { + "name": "stripe_webhook_events_select_by_service_role", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "service_role" + ], + "using": "true" + }, + "stripe_webhook_events_insert_by_service_role": { + "name": "stripe_webhook_events_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "stripe_webhook_events_update_by_service_role": { + "name": "stripe_webhook_events_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true", + "withCheck": "true" + }, + "stripe_webhook_events_delete_by_service_role": { + "name": "stripe_webhook_events_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.taxonomy_categories": { + "name": "taxonomy_categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "public_id": { + "name": "public_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_id": { + "name": "parent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "categories_parent_name_unq": { + "name": "categories_parent_name_unq", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "categories_parent_id_idx": { + "name": "categories_parent_id_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "taxonomy_categories_parent_id_taxonomy_categories_id_fk": { + "name": "taxonomy_categories_parent_id_taxonomy_categories_id_fk", + "tableFrom": "taxonomy_categories", + "tableTo": "taxonomy_categories", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "taxonomy_categories_public_id_unique": { + "name": "taxonomy_categories_public_id_unique", + "nullsNotDistinct": false, + "columns": [ + "public_id" + ] + } + }, + "policies": { + "categories_select_for_authenticated": { + "name": "categories_select_for_authenticated", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "true" + }, + "categories_modify_system_only": { + "name": "categories_modify_system_only", + "as": "RESTRICTIVE", + "for": "ALL", + "to": [ + "authenticated", + "service_role" + ], + "using": "false", + "withCheck": "false" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.taxonomy_attributes": { + "name": "taxonomy_attributes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "friendly_id": { + "name": "friendly_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "public_id": { + "name": "public_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "taxonomy_attributes_friendly_id_idx": { + "name": "taxonomy_attributes_friendly_id_idx", + "columns": [ + { + "expression": "friendly_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "taxonomy_attributes_friendly_id_unique": { + "name": "taxonomy_attributes_friendly_id_unique", + "nullsNotDistinct": false, + "columns": [ + "friendly_id" + ] + }, + "taxonomy_attributes_public_id_unique": { + "name": "taxonomy_attributes_public_id_unique", + "nullsNotDistinct": false, + "columns": [ + "public_id" + ] + } + }, + "policies": { + "taxonomy_attributes_select_for_authenticated": { + "name": "taxonomy_attributes_select_for_authenticated", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.taxonomy_values": { + "name": "taxonomy_values", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "attribute_id": { + "name": "attribute_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "friendly_id": { + "name": "friendly_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "public_id": { + "name": "public_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "public_attribute_id": { + "name": "public_attribute_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "taxonomy_values_attribute_id_idx": { + "name": "taxonomy_values_attribute_id_idx", + "columns": [ + { + "expression": "attribute_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "taxonomy_values_friendly_id_idx": { + "name": "taxonomy_values_friendly_id_idx", + "columns": [ + { + "expression": "friendly_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "taxonomy_values_attribute_id_taxonomy_attributes_id_fk": { + "name": "taxonomy_values_attribute_id_taxonomy_attributes_id_fk", + "tableFrom": "taxonomy_values", + "tableTo": "taxonomy_attributes", + "columnsFrom": [ + "attribute_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "taxonomy_values_friendly_id_unique": { + "name": "taxonomy_values_friendly_id_unique", + "nullsNotDistinct": false, + "columns": [ + "friendly_id" + ] + }, + "taxonomy_values_public_id_unique": { + "name": "taxonomy_values_public_id_unique", + "nullsNotDistinct": false, + "columns": [ + "public_id" + ] + } + }, + "policies": { + "taxonomy_values_select_for_authenticated": { + "name": "taxonomy_values_select_for_authenticated", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.taxonomy_external_mappings": { + "name": "taxonomy_external_mappings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_system": { + "name": "source_system", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_taxonomy": { + "name": "source_taxonomy", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_taxonomy": { + "name": "target_taxonomy", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "taxonomy_external_mappings_slug_unq": { + "name": "taxonomy_external_mappings_slug_unq", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "taxonomy_external_mappings_source_idx": { + "name": "taxonomy_external_mappings_source_idx", + "columns": [ + { + "expression": "source_system", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_taxonomy", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "taxonomy_external_mappings_select_for_authenticated": { + "name": "taxonomy_external_mappings_select_for_authenticated", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.user_role": { + "name": "user_role", + "schema": "public", + "values": [ + "owner", + "member" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/api/supabase/migrations/meta/20260306111521_snapshot.json b/apps/api/supabase/migrations/meta/20260306111521_snapshot.json new file mode 100644 index 00000000..16be220c --- /dev/null +++ b/apps/api/supabase/migrations/meta/20260306111521_snapshot.json @@ -0,0 +1,11048 @@ +{ + "id": "e146b2a1-772b-475f-b7cc-5a1d2b2adb9f", + "prevId": "0e7a69f9-446b-40fe-8f78-3015200a60ae", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "full_name": { + "name": "full_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "avatar_path": { + "name": "avatar_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_users_brand_id": { + "name": "idx_users_brand_id", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "where": "(brand_id IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "users_id_users_id_fk": { + "name": "users_id_users_id_fk", + "tableFrom": "users", + "tableTo": "users", + "schemaTo": "auth", + "columnsFrom": [ + "id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_key": { + "name": "users_email_key", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": { + "users_select_own_profile": { + "name": "users_select_own_profile", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "auth.uid() = id" + }, + "users_select_for_brand_members": { + "name": "users_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "shares_brand_with(id)" + }, + "users_update_own_profile": { + "name": "users_update_own_profile", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "auth.uid() = id", + "withCheck": "\n auth.uid() = id\n AND (\n brand_id IS NULL\n OR EXISTS (\n SELECT 1\n FROM brand_members bm\n WHERE bm.brand_id = users.brand_id\n AND bm.user_id = auth.uid()\n )\n )\n " + }, + "users_insert_own_profile": { + "name": "users_insert_own_profile", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "auth.uid() = id" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brands": { + "name": "brands", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "country_code": { + "name": "country_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo_path": { + "name": "logo_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_brands_active": { + "name": "idx_brands_active", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "(deleted_at IS NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brands_slug": { + "name": "idx_brands_slug", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "(slug IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brands_email": { + "name": "idx_brands_email", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brands_update_by_member": { + "name": "brands_update_by_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(id)" + }, + "brands_delete_by_owner": { + "name": "brands_delete_by_owner", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_owner(id)" + }, + "brands_select_for_invite_recipients": { + "name": "brands_select_for_invite_recipients", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ] + }, + "brands_select_for_members": { + "name": "brands_select_for_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(id)" + }, + "brands_insert_by_authenticated": { + "name": "brands_insert_by_authenticated", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ] + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.platform_admin_audit_logs": { + "name": "platform_admin_audit_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_type": { + "name": "resource_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_platform_admin_audit_logs_actor_user_id": { + "name": "idx_platform_admin_audit_logs_actor_user_id", + "columns": [ + { + "expression": "actor_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_platform_admin_audit_logs_resource": { + "name": "idx_platform_admin_audit_logs_resource", + "columns": [ + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_platform_admin_audit_logs_created_at": { + "name": "idx_platform_admin_audit_logs_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "platform_admin_audit_logs_actor_user_id_users_id_fk": { + "name": "platform_admin_audit_logs_actor_user_id_users_id_fk", + "tableFrom": "platform_admin_audit_logs", + "tableTo": "users", + "columnsFrom": [ + "actor_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "platform_admin_audit_logs_select_by_service_role": { + "name": "platform_admin_audit_logs_select_by_service_role", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "service_role" + ], + "using": "true" + }, + "platform_admin_audit_logs_insert_by_service_role": { + "name": "platform_admin_audit_logs_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "platform_admin_audit_logs_update_by_service_role": { + "name": "platform_admin_audit_logs_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true", + "withCheck": "true" + }, + "platform_admin_audit_logs_delete_by_service_role": { + "name": "platform_admin_audit_logs_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.platform_admin_allowlist": { + "name": "platform_admin_allowlist", + "schema": "", + "columns": { + "email": { + "name": "email", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ux_platform_admin_allowlist_user_id_not_null": { + "name": "ux_platform_admin_allowlist_user_id_not_null", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "(user_id IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "platform_admin_allowlist_user_id_users_id_fk": { + "name": "platform_admin_allowlist_user_id_users_id_fk", + "tableFrom": "platform_admin_allowlist", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "platform_admin_allowlist_select_by_service_role": { + "name": "platform_admin_allowlist_select_by_service_role", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "service_role" + ], + "using": "true" + }, + "platform_admin_allowlist_insert_by_service_role": { + "name": "platform_admin_allowlist_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "platform_admin_allowlist_update_by_service_role": { + "name": "platform_admin_allowlist_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true", + "withCheck": "true" + }, + "platform_admin_allowlist_delete_by_service_role": { + "name": "platform_admin_allowlist_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_collections": { + "name": "brand_collections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filter": { + "name": "filter", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_collections_brand_name_unq": { + "name": "brand_collections_brand_name_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_collections_brand_id_brands_id_fk": { + "name": "brand_collections_brand_id_brands_id_fk", + "tableFrom": "brand_collections", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_collections_select_for_brand_members": { + "name": "brand_collections_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_collections_insert_by_brand_member": { + "name": "brand_collections_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_collections_update_by_brand_member": { + "name": "brand_collections_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_collections_delete_by_brand_member": { + "name": "brand_collections_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_members": { + "name": "brand_members", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ux_brand_members_user_brand": { + "name": "ux_brand_members_user_brand", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_members_brand_id": { + "name": "idx_brand_members_brand_id", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_members_user_id": { + "name": "idx_brand_members_user_id", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_members_user_id_users_id_fk": { + "name": "brand_members_user_id_users_id_fk", + "tableFrom": "brand_members", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "brand_members_brand_id_brands_id_fk": { + "name": "brand_members_brand_id_brands_id_fk", + "tableFrom": "brand_members", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "brand_members_user_id_brand_id_key": { + "name": "brand_members_user_id_brand_id_key", + "nullsNotDistinct": false, + "columns": [ + "user_id", + "brand_id" + ] + } + }, + "policies": { + "brand_members_update_by_owner": { + "name": "brand_members_update_by_owner", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_owner(brand_id)" + }, + "brand_members_select_for_members": { + "name": "brand_members_select_for_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_members_delete_self": { + "name": "brand_members_delete_self", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ] + }, + "brand_members_delete_owner_non_owner": { + "name": "brand_members_delete_owner_non_owner", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ] + }, + "brand_members_insert_first_owner_self": { + "name": "brand_members_insert_first_owner_self", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ] + } + }, + "checkConstraints": { + "brand_members_role_check": { + "name": "brand_members_role_check", + "value": "role = ANY (ARRAY['owner'::text, 'member'::text, 'avelero'::text])" + } + }, + "isRLSEnabled": false + }, + "public.brand_invites": { + "name": "brand_invites", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_brand_invites_brand_id": { + "name": "idx_brand_invites_brand_id", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_invites_expires_at": { + "name": "idx_brand_invites_expires_at", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_invites_token_hash": { + "name": "idx_brand_invites_token_hash", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_invites_valid_lookup": { + "name": "idx_brand_invites_valid_lookup", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_invites_email": { + "name": "idx_brand_invites_email", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_invites_email_expires": { + "name": "idx_brand_invites_email_expires", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ux_brand_invites_token_hash_not_null": { + "name": "ux_brand_invites_token_hash_not_null", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": true, + "where": "(token_hash IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_invites_brand_id_brands_id_fk": { + "name": "brand_invites_brand_id_brands_id_fk", + "tableFrom": "brand_invites", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_invites_update_by_owner": { + "name": "brand_invites_update_by_owner", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_owner(brand_id)" + }, + "brand_invites_insert_by_owner": { + "name": "brand_invites_insert_by_owner", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_owner(brand_id)" + }, + "brand_invites_delete_by_owner": { + "name": "brand_invites_delete_by_owner", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_owner(brand_id)" + }, + "brand_invites_select_for_recipient": { + "name": "brand_invites_select_for_recipient", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ] + }, + "brand_invites_delete_by_recipient": { + "name": "brand_invites_delete_by_recipient", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ] + }, + "brand_invites_select_for_members": { + "name": "brand_invites_select_for_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ] + } + }, + "checkConstraints": { + "brand_invites_role_check": { + "name": "brand_invites_role_check", + "value": "role = ANY (ARRAY['owner'::text, 'member'::text])" + } + }, + "isRLSEnabled": false + }, + "public.brand_tags": { + "name": "brand_tags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hex": { + "name": "hex", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_tags_brand_name_unq": { + "name": "brand_tags_brand_name_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_tags_brand_id_brands_id_fk": { + "name": "brand_tags_brand_id_brands_id_fk", + "tableFrom": "brand_tags", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_tags_select_for_brand_members": { + "name": "brand_tags_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_tags_insert_by_brand_members": { + "name": "brand_tags_insert_by_brand_members", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_tags_update_by_brand_members": { + "name": "brand_tags_update_by_brand_members", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_tags_delete_by_brand_members": { + "name": "brand_tags_delete_by_brand_members", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_theme": { + "name": "brand_theme", + "schema": "", + "columns": { + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "theme_styles": { + "name": "theme_styles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "theme_config": { + "name": "theme_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "stylesheet_path": { + "name": "stylesheet_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "google_fonts_url": { + "name": "google_fonts_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_brand_theme_updated_at": { + "name": "idx_brand_theme_updated_at", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_theme_brand_id_brands_id_fk": { + "name": "brand_theme_brand_id_brands_id_fk", + "tableFrom": "brand_theme", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "brand_theme_brand_id_pkey": { + "name": "brand_theme_brand_id_pkey", + "columns": [ + "brand_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": { + "brand_theme_select_for_brand_members": { + "name": "brand_theme_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_theme_insert_by_brand_member": { + "name": "brand_theme_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_theme_update_by_brand_member": { + "name": "brand_theme_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)", + "withCheck": "is_brand_member(brand_id)" + }, + "brand_theme_delete_by_brand_member": { + "name": "brand_theme_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_custom_domains": { + "name": "brand_custom_domains", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "verification_token": { + "name": "verification_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_verification_attempt": { + "name": "last_verification_attempt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "verification_error": { + "name": "verification_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "verified_at": { + "name": "verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_custom_domains_brand_id_unq": { + "name": "brand_custom_domains_brand_id_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "brand_custom_domains_domain_unq": { + "name": "brand_custom_domains_domain_unq", + "columns": [ + { + "expression": "domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_custom_domains_status": { + "name": "idx_brand_custom_domains_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_custom_domains_brand_id_brands_id_fk": { + "name": "brand_custom_domains_brand_id_brands_id_fk", + "tableFrom": "brand_custom_domains", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_custom_domains_select_for_brand_members": { + "name": "brand_custom_domains_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_custom_domains_insert_by_brand_owner": { + "name": "brand_custom_domains_insert_by_brand_owner", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_owner(brand_id)" + }, + "brand_custom_domains_update_by_brand_owner": { + "name": "brand_custom_domains_update_by_brand_owner", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_owner(brand_id)", + "withCheck": "is_brand_owner(brand_id)" + }, + "brand_custom_domains_delete_by_brand_owner": { + "name": "brand_custom_domains_delete_by_brand_owner", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_owner(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_lifecycle": { + "name": "brand_lifecycle", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "phase": { + "name": "phase", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'demo'" + }, + "phase_changed_at": { + "name": "phase_changed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "trial_started_at": { + "name": "trial_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "trial_ends_at": { + "name": "trial_ends_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "hard_delete_after": { + "name": "hard_delete_after", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_lifecycle_brand_id_unq": { + "name": "brand_lifecycle_brand_id_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_lifecycle_phase": { + "name": "idx_brand_lifecycle_phase", + "columns": [ + { + "expression": "phase", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_lifecycle_trial_ends_at": { + "name": "idx_brand_lifecycle_trial_ends_at", + "columns": [ + { + "expression": "trial_ends_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_lifecycle_hard_delete_after": { + "name": "idx_brand_lifecycle_hard_delete_after", + "columns": [ + { + "expression": "hard_delete_after", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "(hard_delete_after IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_lifecycle_brand_id_brands_id_fk": { + "name": "brand_lifecycle_brand_id_brands_id_fk", + "tableFrom": "brand_lifecycle", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_lifecycle_select_for_brand_members": { + "name": "brand_lifecycle_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_lifecycle_insert_by_service_role": { + "name": "brand_lifecycle_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "brand_lifecycle_update_by_service_role": { + "name": "brand_lifecycle_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true", + "withCheck": "true" + }, + "brand_lifecycle_delete_by_service_role": { + "name": "brand_lifecycle_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": { + "brand_lifecycle_phase_check": { + "name": "brand_lifecycle_phase_check", + "value": "phase = ANY (ARRAY['demo'::text, 'trial'::text, 'expired'::text, 'active'::text, 'past_due'::text, 'suspended'::text, 'cancelled'::text])" + } + }, + "isRLSEnabled": false + }, + "public.brand_plan": { + "name": "brand_plan", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "plan_type": { + "name": "plan_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "plan_selected_at": { + "name": "plan_selected_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "sku_annual_limit": { + "name": "sku_annual_limit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "sku_onboarding_limit": { + "name": "sku_onboarding_limit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "sku_limit_override": { + "name": "sku_limit_override", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "sku_year_start": { + "name": "sku_year_start", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "skus_created_this_year": { + "name": "skus_created_this_year", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "skus_created_onboarding": { + "name": "skus_created_onboarding", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "max_seats": { + "name": "max_seats", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_plan_brand_id_unq": { + "name": "brand_plan_brand_id_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_plan_plan_type": { + "name": "idx_brand_plan_plan_type", + "columns": [ + { + "expression": "plan_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_plan_sku_year_start": { + "name": "idx_brand_plan_sku_year_start", + "columns": [ + { + "expression": "sku_year_start", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_plan_brand_id_brands_id_fk": { + "name": "brand_plan_brand_id_brands_id_fk", + "tableFrom": "brand_plan", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_plan_select_for_brand_members": { + "name": "brand_plan_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_plan_insert_by_service_role": { + "name": "brand_plan_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "brand_plan_update_by_service_role": { + "name": "brand_plan_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true", + "withCheck": "true" + }, + "brand_plan_delete_by_service_role": { + "name": "brand_plan_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": { + "brand_plan_type_check": { + "name": "brand_plan_type_check", + "value": "plan_type IS NULL OR plan_type = ANY (ARRAY['starter'::text, 'growth'::text, 'scale'::text, 'enterprise'::text])" + }, + "brand_plan_sku_annual_limit_check": { + "name": "brand_plan_sku_annual_limit_check", + "value": "sku_annual_limit IS NULL OR sku_annual_limit >= 0" + }, + "brand_plan_sku_onboarding_limit_check": { + "name": "brand_plan_sku_onboarding_limit_check", + "value": "sku_onboarding_limit IS NULL OR sku_onboarding_limit >= 0" + }, + "brand_plan_sku_limit_override_check": { + "name": "brand_plan_sku_limit_override_check", + "value": "sku_limit_override IS NULL OR sku_limit_override >= 0" + }, + "brand_plan_skus_created_this_year_check": { + "name": "brand_plan_skus_created_this_year_check", + "value": "skus_created_this_year >= 0" + }, + "brand_plan_skus_created_onboarding_check": { + "name": "brand_plan_skus_created_onboarding_check", + "value": "skus_created_onboarding >= 0" + }, + "brand_plan_max_seats_check": { + "name": "brand_plan_max_seats_check", + "value": "max_seats IS NULL OR max_seats > 0" + } + }, + "isRLSEnabled": false + }, + "public.brand_billing": { + "name": "brand_billing", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "billing_mode": { + "name": "billing_mode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_subscription_id": { + "name": "stripe_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "plan_currency": { + "name": "plan_currency", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'EUR'" + }, + "custom_monthly_price_cents": { + "name": "custom_monthly_price_cents", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "billing_access_override": { + "name": "billing_access_override", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "billing_override_expires_at": { + "name": "billing_override_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_billing_brand_id_unq": { + "name": "brand_billing_brand_id_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "brand_billing_stripe_customer_id_unq": { + "name": "brand_billing_stripe_customer_id_unq", + "columns": [ + { + "expression": "stripe_customer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "(stripe_customer_id IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "brand_billing_stripe_subscription_id_unq": { + "name": "brand_billing_stripe_subscription_id_unq", + "columns": [ + { + "expression": "stripe_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "(stripe_subscription_id IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_billing_override_expires": { + "name": "idx_brand_billing_override_expires", + "columns": [ + { + "expression": "billing_override_expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_billing_brand_id_brands_id_fk": { + "name": "brand_billing_brand_id_brands_id_fk", + "tableFrom": "brand_billing", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_billing_select_for_brand_members": { + "name": "brand_billing_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_billing_insert_by_service_role": { + "name": "brand_billing_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "brand_billing_update_by_service_role": { + "name": "brand_billing_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true", + "withCheck": "true" + }, + "brand_billing_delete_by_service_role": { + "name": "brand_billing_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": { + "brand_billing_mode_check": { + "name": "brand_billing_mode_check", + "value": "billing_mode IS NULL OR billing_mode = ANY (ARRAY['stripe_checkout'::text, 'stripe_invoice'::text])" + }, + "brand_billing_plan_currency_check": { + "name": "brand_billing_plan_currency_check", + "value": "char_length(plan_currency) = 3" + }, + "brand_billing_custom_monthly_price_check": { + "name": "brand_billing_custom_monthly_price_check", + "value": "custom_monthly_price_cents IS NULL OR custom_monthly_price_cents >= 0" + }, + "brand_billing_access_override_check": { + "name": "brand_billing_access_override_check", + "value": "billing_access_override = ANY (ARRAY['none'::text, 'temporary_allow'::text, 'temporary_block'::text])" + } + }, + "isRLSEnabled": false + }, + "public.brand_billing_events": { + "name": "brand_billing_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_event_id": { + "name": "stripe_event_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_brand_billing_events_brand_created_at": { + "name": "idx_brand_billing_events_brand_created_at", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_billing_events_event_type": { + "name": "idx_brand_billing_events_event_type", + "columns": [ + { + "expression": "event_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "brand_billing_events_stripe_event_id_unq": { + "name": "brand_billing_events_stripe_event_id_unq", + "columns": [ + { + "expression": "stripe_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "(stripe_event_id IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_billing_events_brand_id_brands_id_fk": { + "name": "brand_billing_events_brand_id_brands_id_fk", + "tableFrom": "brand_billing_events", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_billing_events_select_for_brand_members": { + "name": "brand_billing_events_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_billing_events_insert_by_service_role": { + "name": "brand_billing_events_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "brand_billing_events_update_by_service_role": { + "name": "brand_billing_events_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true", + "withCheck": "true" + }, + "brand_billing_events_delete_by_service_role": { + "name": "brand_billing_events_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_attributes": { + "name": "brand_attributes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "taxonomy_attribute_id": { + "name": "taxonomy_attribute_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_attributes_brand_name_unq": { + "name": "brand_attributes_brand_name_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_attributes_brand_id_brands_id_fk": { + "name": "brand_attributes_brand_id_brands_id_fk", + "tableFrom": "brand_attributes", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "brand_attributes_taxonomy_attribute_id_taxonomy_attributes_id_fk": { + "name": "brand_attributes_taxonomy_attribute_id_taxonomy_attributes_id_fk", + "tableFrom": "brand_attributes", + "tableTo": "taxonomy_attributes", + "columnsFrom": [ + "taxonomy_attribute_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_attributes_select_for_brand_members": { + "name": "brand_attributes_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_attributes_insert_by_brand_member": { + "name": "brand_attributes_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_attributes_update_by_brand_member": { + "name": "brand_attributes_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_attributes_delete_by_brand_member": { + "name": "brand_attributes_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_attribute_values": { + "name": "brand_attribute_values", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "attribute_id": { + "name": "attribute_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "taxonomy_value_id": { + "name": "taxonomy_value_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_attribute_values_brand_attr_name_unq": { + "name": "brand_attribute_values_brand_attr_name_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "attribute_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_attribute_values_brand_id_brands_id_fk": { + "name": "brand_attribute_values_brand_id_brands_id_fk", + "tableFrom": "brand_attribute_values", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "brand_attribute_values_attribute_id_brand_attributes_id_fk": { + "name": "brand_attribute_values_attribute_id_brand_attributes_id_fk", + "tableFrom": "brand_attribute_values", + "tableTo": "brand_attributes", + "columnsFrom": [ + "attribute_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "brand_attribute_values_taxonomy_value_id_taxonomy_values_id_fk": { + "name": "brand_attribute_values_taxonomy_value_id_taxonomy_values_id_fk", + "tableFrom": "brand_attribute_values", + "tableTo": "taxonomy_values", + "columnsFrom": [ + "taxonomy_value_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_attribute_values_select_for_brand_members": { + "name": "brand_attribute_values_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_attribute_values_insert_by_brand_member": { + "name": "brand_attribute_values_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_attribute_values_update_by_brand_member": { + "name": "brand_attribute_values_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_attribute_values_delete_by_brand_member": { + "name": "brand_attribute_values_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_certifications": { + "name": "brand_certifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certification_code": { + "name": "certification_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_name": { + "name": "institute_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_email": { + "name": "institute_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_website": { + "name": "institute_website", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_address_line_1": { + "name": "institute_address_line_1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_address_line_2": { + "name": "institute_address_line_2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_city": { + "name": "institute_city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_state": { + "name": "institute_state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_zip": { + "name": "institute_zip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_country_code": { + "name": "institute_country_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "issue_date": { + "name": "issue_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "expiry_date": { + "name": "expiry_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "certification_path": { + "name": "certification_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "brand_certifications_brand_id_brands_id_fk": { + "name": "brand_certifications_brand_id_brands_id_fk", + "tableFrom": "brand_certifications", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_certifications_select_for_brand_members": { + "name": "brand_certifications_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_certifications_insert_by_brand_member": { + "name": "brand_certifications_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_certifications_update_by_brand_member": { + "name": "brand_certifications_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_certifications_delete_by_brand_member": { + "name": "brand_certifications_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_operators": { + "name": "brand_operators", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "legal_name": { + "name": "legal_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "website": { + "name": "website", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_line_1": { + "name": "address_line_1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_line_2": { + "name": "address_line_2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "city": { + "name": "city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "zip": { + "name": "zip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "country_code": { + "name": "country_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "brand_operators_brand_id_brands_id_fk": { + "name": "brand_operators_brand_id_brands_id_fk", + "tableFrom": "brand_operators", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_operators_select_for_brand_members": { + "name": "brand_operators_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_operators_insert_by_brand_member": { + "name": "brand_operators_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_operators_update_by_brand_member": { + "name": "brand_operators_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_operators_delete_by_brand_member": { + "name": "brand_operators_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_manufacturers": { + "name": "brand_manufacturers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "legal_name": { + "name": "legal_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "website": { + "name": "website", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_line_1": { + "name": "address_line_1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_line_2": { + "name": "address_line_2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "city": { + "name": "city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "zip": { + "name": "zip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "country_code": { + "name": "country_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_manufacturers_brand_name_unq": { + "name": "brand_manufacturers_brand_name_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_manufacturers_brand_id_brands_id_fk": { + "name": "brand_manufacturers_brand_id_brands_id_fk", + "tableFrom": "brand_manufacturers", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_manufacturers_select_for_brand_members": { + "name": "brand_manufacturers_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_manufacturers_insert_by_brand_member": { + "name": "brand_manufacturers_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_manufacturers_update_by_brand_member": { + "name": "brand_manufacturers_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_manufacturers_delete_by_brand_member": { + "name": "brand_manufacturers_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_materials": { + "name": "brand_materials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certification_id": { + "name": "certification_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "recyclable": { + "name": "recyclable", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "country_of_origin": { + "name": "country_of_origin", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "brand_materials_brand_id_brands_id_fk": { + "name": "brand_materials_brand_id_brands_id_fk", + "tableFrom": "brand_materials", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "brand_materials_certification_id_brand_certifications_id_fk": { + "name": "brand_materials_certification_id_brand_certifications_id_fk", + "tableFrom": "brand_materials", + "tableTo": "brand_certifications", + "columnsFrom": [ + "certification_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_materials_select_for_brand_members": { + "name": "brand_materials_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_materials_insert_by_brand_member": { + "name": "brand_materials_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_materials_update_by_brand_member": { + "name": "brand_materials_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_materials_delete_by_brand_member": { + "name": "brand_materials_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_seasons": { + "name": "brand_seasons", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "start_date": { + "name": "start_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "end_date": { + "name": "end_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "ongoing": { + "name": "ongoing", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_seasons_brand_name_unq": { + "name": "brand_seasons_brand_name_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_seasons_brand_id_brands_id_fk": { + "name": "brand_seasons_brand_id_brands_id_fk", + "tableFrom": "brand_seasons", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_seasons_select_for_brand_members": { + "name": "brand_seasons_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_seasons_insert_by_brand_members": { + "name": "brand_seasons_insert_by_brand_members", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_seasons_update_by_brand_members": { + "name": "brand_seasons_update_by_brand_members", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_seasons_delete_by_brand_members": { + "name": "brand_seasons_delete_by_brand_members", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.products": { + "name": "products", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "product_handle": { + "name": "product_handle", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "manufacturer_id": { + "name": "manufacturer_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "image_path": { + "name": "image_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "category_id": { + "name": "category_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unpublished'" + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "source_integration_id": { + "name": "source_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "products_brand_id_product_handle_unq": { + "name": "products_brand_id_product_handle_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "product_handle", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_brand_id": { + "name": "idx_products_brand_id", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_brand_status": { + "name": "idx_products_brand_status", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_brand_created": { + "name": "idx_products_brand_created", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_brand_product_handle": { + "name": "idx_products_brand_product_handle", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "product_handle", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_brand_category": { + "name": "idx_products_brand_category", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "category_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_brand_season": { + "name": "idx_products_brand_season", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_brand_name": { + "name": "idx_products_brand_name", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_name": { + "name": "idx_products_name", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "products_brand_id_brands_id_fk": { + "name": "products_brand_id_brands_id_fk", + "tableFrom": "products", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "products_manufacturer_id_brand_manufacturers_id_fk": { + "name": "products_manufacturer_id_brand_manufacturers_id_fk", + "tableFrom": "products", + "tableTo": "brand_manufacturers", + "columnsFrom": [ + "manufacturer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "products_category_id_taxonomy_categories_id_fk": { + "name": "products_category_id_taxonomy_categories_id_fk", + "tableFrom": "products", + "tableTo": "taxonomy_categories", + "columnsFrom": [ + "category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "products_season_id_brand_seasons_id_fk": { + "name": "products_season_id_brand_seasons_id_fk", + "tableFrom": "products", + "tableTo": "brand_seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "products_source_integration_id_brand_integrations_id_fk": { + "name": "products_source_integration_id_brand_integrations_id_fk", + "tableFrom": "products", + "tableTo": "brand_integrations", + "columnsFrom": [ + "source_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "products_select_for_brand_members": { + "name": "products_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "products_insert_by_brand_members": { + "name": "products_insert_by_brand_members", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "products_update_by_brand_members": { + "name": "products_update_by_brand_members", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "products_delete_by_brand_members": { + "name": "products_delete_by_brand_members", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_variants": { + "name": "product_variants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "barcode": { + "name": "barcode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sku": { + "name": "sku", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "upid": { + "name": "upid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "image_path": { + "name": "image_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_integration": { + "name": "source_integration", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_external_id": { + "name": "source_external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_ghost": { + "name": "is_ghost", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_product_variants_product_id": { + "name": "idx_product_variants_product_id", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_variants_product_created": { + "name": "idx_product_variants_product_created", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_variants_upid": { + "name": "idx_product_variants_upid", + "columns": [ + { + "expression": "upid", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "where": "(upid IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_unique_upid_global": { + "name": "idx_unique_upid_global", + "columns": [ + { + "expression": "upid", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "upid IS NOT NULL AND upid != ''", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_unique_barcode_per_brand": { + "name": "idx_unique_barcode_per_brand", + "columns": [ + { + "expression": "barcode", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "get_product_brand_id(product_id)", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "barcode IS NOT NULL AND barcode != ''", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_variants_product_id_products_id_fk": { + "name": "product_variants_product_id_products_id_fk", + "tableFrom": "product_variants", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "product_variants_select_for_brand_members": { + "name": "product_variants_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_variants_insert_by_brand_member": { + "name": "product_variants_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_variants_update_by_brand_member": { + "name": "product_variants_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_variants_delete_by_brand_member": { + "name": "product_variants_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_materials": { + "name": "product_materials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "brand_material_id": { + "name": "brand_material_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "percentage": { + "name": "percentage", + "type": "numeric(6, 2)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "product_materials_product_material_unq": { + "name": "product_materials_product_material_unq", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "brand_material_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_materials_product_id": { + "name": "idx_product_materials_product_id", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_materials_product_created": { + "name": "idx_product_materials_product_created", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_materials_product_id_products_id_fk": { + "name": "product_materials_product_id_products_id_fk", + "tableFrom": "product_materials", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "product_materials_brand_material_id_brand_materials_id_fk": { + "name": "product_materials_brand_material_id_brand_materials_id_fk", + "tableFrom": "product_materials", + "tableTo": "brand_materials", + "columnsFrom": [ + "brand_material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "product_materials_select_for_brand_members": { + "name": "product_materials_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_materials_insert_by_brand_member": { + "name": "product_materials_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_materials_update_by_brand_member": { + "name": "product_materials_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_materials_delete_by_brand_member": { + "name": "product_materials_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_journey_steps": { + "name": "product_journey_steps", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sort_index": { + "name": "sort_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "step_type": { + "name": "step_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "operator_id": { + "name": "operator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "product_journey_steps_product_sort_operator_unq": { + "name": "product_journey_steps_product_sort_operator_unq", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_index", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "operator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_journey_steps_product_id": { + "name": "idx_product_journey_steps_product_id", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_journey_steps_product_sort": { + "name": "idx_product_journey_steps_product_sort", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "sort_index", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_journey_steps_product_id_products_id_fk": { + "name": "product_journey_steps_product_id_products_id_fk", + "tableFrom": "product_journey_steps", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "product_journey_steps_operator_id_brand_operators_id_fk": { + "name": "product_journey_steps_operator_id_brand_operators_id_fk", + "tableFrom": "product_journey_steps", + "tableTo": "brand_operators", + "columnsFrom": [ + "operator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "product_journey_steps_select_for_brand_members": { + "name": "product_journey_steps_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_journey_steps_insert_by_brand_member": { + "name": "product_journey_steps_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_journey_steps_update_by_brand_member": { + "name": "product_journey_steps_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_journey_steps_delete_by_brand_member": { + "name": "product_journey_steps_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_environment": { + "name": "product_environment", + "schema": "", + "columns": { + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "numeric(12, 4)", + "primaryKey": false, + "notNull": false + }, + "unit": { + "name": "unit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metric": { + "name": "metric", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "product_environment_product_id_products_id_fk": { + "name": "product_environment_product_id_products_id_fk", + "tableFrom": "product_environment", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "product_environment_pkey": { + "name": "product_environment_pkey", + "columns": [ + "product_id", + "metric" + ] + } + }, + "uniqueConstraints": {}, + "policies": { + "product_environment_select_for_brand_members": { + "name": "product_environment_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_environment_insert_by_brand_member": { + "name": "product_environment_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_environment_update_by_brand_member": { + "name": "product_environment_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_environment_delete_by_brand_member": { + "name": "product_environment_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_tags": { + "name": "product_tags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tag_id": { + "name": "tag_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tags_on_product_tag_product_unq": { + "name": "tags_on_product_tag_product_unq", + "columns": [ + { + "expression": "tag_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tags_on_product_tag_id_idx": { + "name": "tags_on_product_tag_id_idx", + "columns": [ + { + "expression": "tag_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tags_on_product_product_id_idx": { + "name": "tags_on_product_product_id_idx", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_tags_tag_id_brand_tags_id_fk": { + "name": "product_tags_tag_id_brand_tags_id_fk", + "tableFrom": "product_tags", + "tableTo": "brand_tags", + "columnsFrom": [ + "tag_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "product_tags_product_id_products_id_fk": { + "name": "product_tags_product_id_products_id_fk", + "tableFrom": "product_tags", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "tags_on_product_select_for_brand_members": { + "name": "tags_on_product_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "tags_on_product_insert_by_brand_members": { + "name": "tags_on_product_insert_by_brand_members", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "tags_on_product_delete_by_brand_members": { + "name": "tags_on_product_delete_by_brand_members", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_variant_attributes": { + "name": "product_variant_attributes", + "schema": "", + "columns": { + "variant_id": { + "name": "variant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "attribute_value_id": { + "name": "attribute_value_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_product_variant_attributes_variant": { + "name": "idx_product_variant_attributes_variant", + "columns": [ + { + "expression": "variant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_variant_attributes_variant_id_product_variants_id_fk": { + "name": "product_variant_attributes_variant_id_product_variants_id_fk", + "tableFrom": "product_variant_attributes", + "tableTo": "product_variants", + "columnsFrom": [ + "variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "product_variant_attributes_attribute_value_id_brand_attribute_values_id_fk": { + "name": "product_variant_attributes_attribute_value_id_brand_attribute_values_id_fk", + "tableFrom": "product_variant_attributes", + "tableTo": "brand_attribute_values", + "columnsFrom": [ + "attribute_value_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "product_variant_attributes_pkey": { + "name": "product_variant_attributes_pkey", + "columns": [ + "variant_id", + "attribute_value_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": { + "product_variant_attributes_select_for_brand_members": { + "name": "product_variant_attributes_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "product_variant_attributes_insert_by_brand_member": { + "name": "product_variant_attributes_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "product_variant_attributes_update_by_brand_member": { + "name": "product_variant_attributes_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "product_variant_attributes_delete_by_brand_member": { + "name": "product_variant_attributes_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_weight": { + "name": "product_weight", + "schema": "", + "columns": { + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "weight": { + "name": "weight", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "weight_unit": { + "name": "weight_unit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "product_weight_product_id_products_id_fk": { + "name": "product_weight_product_id_products_id_fk", + "tableFrom": "product_weight", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "product_weight_select_for_brand_members": { + "name": "product_weight_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_weight_insert_by_brand_member": { + "name": "product_weight_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_weight_update_by_brand_member": { + "name": "product_weight_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_weight_delete_by_brand_member": { + "name": "product_weight_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_commercial": { + "name": "product_commercial", + "schema": "", + "columns": { + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "webshop_url": { + "name": "webshop_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sales_status": { + "name": "sales_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "product_commercial_product_id_products_id_fk": { + "name": "product_commercial_product_id_products_id_fk", + "tableFrom": "product_commercial", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "product_commercial_select_for_brand_members": { + "name": "product_commercial_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_commercial_insert_by_brand_member": { + "name": "product_commercial_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_commercial_update_by_brand_member": { + "name": "product_commercial_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_commercial_delete_by_brand_member": { + "name": "product_commercial_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.variant_commercial": { + "name": "variant_commercial", + "schema": "", + "columns": { + "variant_id": { + "name": "variant_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "webshop_url": { + "name": "webshop_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sales_status": { + "name": "sales_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_integration": { + "name": "source_integration", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_external_id": { + "name": "source_external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "variant_commercial_variant_id_product_variants_id_fk": { + "name": "variant_commercial_variant_id_product_variants_id_fk", + "tableFrom": "variant_commercial", + "tableTo": "product_variants", + "columnsFrom": [ + "variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "variant_commercial_select_for_brand_members": { + "name": "variant_commercial_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_commercial_insert_by_brand_member": { + "name": "variant_commercial_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_commercial_update_by_brand_member": { + "name": "variant_commercial_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_commercial_delete_by_brand_member": { + "name": "variant_commercial_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.variant_environment": { + "name": "variant_environment", + "schema": "", + "columns": { + "variant_id": { + "name": "variant_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "carbon_kg_co2e": { + "name": "carbon_kg_co2e", + "type": "numeric(12, 4)", + "primaryKey": false, + "notNull": false + }, + "water_liters": { + "name": "water_liters", + "type": "numeric(12, 2)", + "primaryKey": false, + "notNull": false + }, + "source_integration": { + "name": "source_integration", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_external_id": { + "name": "source_external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "variant_environment_variant_id_product_variants_id_fk": { + "name": "variant_environment_variant_id_product_variants_id_fk", + "tableFrom": "variant_environment", + "tableTo": "product_variants", + "columnsFrom": [ + "variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "variant_environment_select_for_brand_members": { + "name": "variant_environment_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_environment_insert_by_brand_member": { + "name": "variant_environment_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_environment_update_by_brand_member": { + "name": "variant_environment_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_environment_delete_by_brand_member": { + "name": "variant_environment_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.variant_materials": { + "name": "variant_materials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "variant_id": { + "name": "variant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "brand_material_id": { + "name": "brand_material_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "percentage": { + "name": "percentage", + "type": "numeric(6, 2)", + "primaryKey": false, + "notNull": false + }, + "source_integration": { + "name": "source_integration", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_external_id": { + "name": "source_external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "variant_materials_variant_material_unq": { + "name": "variant_materials_variant_material_unq", + "columns": [ + { + "expression": "variant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "brand_material_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_variant_materials_variant_id": { + "name": "idx_variant_materials_variant_id", + "columns": [ + { + "expression": "variant_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "variant_materials_variant_id_product_variants_id_fk": { + "name": "variant_materials_variant_id_product_variants_id_fk", + "tableFrom": "variant_materials", + "tableTo": "product_variants", + "columnsFrom": [ + "variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "variant_materials_brand_material_id_brand_materials_id_fk": { + "name": "variant_materials_brand_material_id_brand_materials_id_fk", + "tableFrom": "variant_materials", + "tableTo": "brand_materials", + "columnsFrom": [ + "brand_material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "variant_materials_select_for_brand_members": { + "name": "variant_materials_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_materials_insert_by_brand_member": { + "name": "variant_materials_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_materials_update_by_brand_member": { + "name": "variant_materials_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )", + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_materials_delete_by_brand_member": { + "name": "variant_materials_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.variant_weight": { + "name": "variant_weight", + "schema": "", + "columns": { + "variant_id": { + "name": "variant_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "weight": { + "name": "weight", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "weight_unit": { + "name": "weight_unit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_integration": { + "name": "source_integration", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_external_id": { + "name": "source_external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "variant_weight_variant_id_product_variants_id_fk": { + "name": "variant_weight_variant_id_product_variants_id_fk", + "tableFrom": "variant_weight", + "tableTo": "product_variants", + "columnsFrom": [ + "variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "variant_weight_select_for_brand_members": { + "name": "variant_weight_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_weight_insert_by_brand_member": { + "name": "variant_weight_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_weight_update_by_brand_member": { + "name": "variant_weight_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_weight_delete_by_brand_member": { + "name": "variant_weight_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.variant_journey_steps": { + "name": "variant_journey_steps", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "variant_id": { + "name": "variant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sort_index": { + "name": "sort_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "step_type": { + "name": "step_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "operator_id": { + "name": "operator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_integration": { + "name": "source_integration", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_external_id": { + "name": "source_external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "variant_journey_steps_variant_sort_operator_unq": { + "name": "variant_journey_steps_variant_sort_operator_unq", + "columns": [ + { + "expression": "variant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_index", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "operator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_variant_journey_steps_variant_id": { + "name": "idx_variant_journey_steps_variant_id", + "columns": [ + { + "expression": "variant_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_variant_journey_steps_variant_sort": { + "name": "idx_variant_journey_steps_variant_sort", + "columns": [ + { + "expression": "variant_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "sort_index", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_variant_journey_steps_operator_id": { + "name": "idx_variant_journey_steps_operator_id", + "columns": [ + { + "expression": "operator_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "variant_journey_steps_variant_id_product_variants_id_fk": { + "name": "variant_journey_steps_variant_id_product_variants_id_fk", + "tableFrom": "variant_journey_steps", + "tableTo": "product_variants", + "columnsFrom": [ + "variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "variant_journey_steps_operator_id_brand_operators_id_fk": { + "name": "variant_journey_steps_operator_id_brand_operators_id_fk", + "tableFrom": "variant_journey_steps", + "tableTo": "brand_operators", + "columnsFrom": [ + "operator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "variant_journey_steps_select_for_brand_members": { + "name": "variant_journey_steps_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_journey_steps_insert_by_brand_member": { + "name": "variant_journey_steps_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_journey_steps_update_by_brand_member": { + "name": "variant_journey_steps_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )", + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_journey_steps_delete_by_brand_member": { + "name": "variant_journey_steps_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_passports": { + "name": "product_passports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "upid": { + "name": "upid", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "working_variant_id": { + "name": "working_variant_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "current_version_id": { + "name": "current_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "dirty": { + "name": "dirty", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "orphaned_at": { + "name": "orphaned_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "sku": { + "name": "sku", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "barcode": { + "name": "barcode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "first_published_at": { + "name": "first_published_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_product_passports_brand_id": { + "name": "idx_product_passports_brand_id", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_passports_brand_dirty": { + "name": "idx_product_passports_brand_dirty", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "dirty", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "bool_ops" + } + ], + "isUnique": false, + "where": "dirty = true", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_passports_working_variant_id": { + "name": "idx_product_passports_working_variant_id", + "columns": [ + { + "expression": "working_variant_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "where": "working_variant_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_passports_status": { + "name": "idx_product_passports_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_passports_orphaned_at": { + "name": "idx_product_passports_orphaned_at", + "columns": [ + { + "expression": "orphaned_at", + "isExpression": false, + "asc": false, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "where": "status = 'orphaned'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_passports_brand_id_brands_id_fk": { + "name": "product_passports_brand_id_brands_id_fk", + "tableFrom": "product_passports", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "product_passports_working_variant_id_product_variants_id_fk": { + "name": "product_passports_working_variant_id_product_variants_id_fk", + "tableFrom": "product_passports", + "tableTo": "product_variants", + "columnsFrom": [ + "working_variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "product_passports_upid_unique": { + "name": "product_passports_upid_unique", + "nullsNotDistinct": false, + "columns": [ + "upid" + ] + } + }, + "policies": { + "product_passports_select_for_brand_members": { + "name": "product_passports_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "product_passports_insert_by_brand_members": { + "name": "product_passports_insert_by_brand_members", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "product_passports_update_by_brand_members": { + "name": "product_passports_update_by_brand_members", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "product_passports_delete_by_brand_members": { + "name": "product_passports_delete_by_brand_members", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "product_passports_select_public": { + "name": "product_passports_select_public", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "anon" + ], + "using": "\n current_version_id IS NOT NULL\n AND (\n working_variant_id IS NULL\n OR\n EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = working_variant_id\n AND p.status = 'published'\n )\n )\n " + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_passport_versions": { + "name": "product_passport_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "passport_id": { + "name": "passport_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "version_number": { + "name": "version_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "data_snapshot": { + "name": "data_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "compressed_snapshot": { + "name": "compressed_snapshot", + "type": "bytea", + "primaryKey": false, + "notNull": false + }, + "compressed_at": { + "name": "compressed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "schema_version": { + "name": "schema_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "published_at": { + "name": "published_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_product_passport_versions_passport_version": { + "name": "idx_product_passport_versions_passport_version", + "columns": [ + { + "expression": "passport_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_passport_versions_passport_id": { + "name": "idx_product_passport_versions_passport_id", + "columns": [ + { + "expression": "passport_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_passport_versions_passport_published": { + "name": "idx_product_passport_versions_passport_published", + "columns": [ + { + "expression": "passport_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "published_at", + "isExpression": false, + "asc": false, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_passport_versions_passport_id_product_passports_id_fk": { + "name": "product_passport_versions_passport_id_product_passports_id_fk", + "tableFrom": "product_passport_versions", + "tableTo": "product_passports", + "columnsFrom": [ + "passport_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "product_passport_versions_select_for_brand_members": { + "name": "product_passport_versions_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_passports\n WHERE product_passports.id = passport_id\n AND is_brand_member(product_passports.brand_id)\n )" + }, + "product_passport_versions_insert_by_brand_members": { + "name": "product_passport_versions_insert_by_brand_members", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM product_passports\n WHERE product_passports.id = passport_id\n AND is_brand_member(product_passports.brand_id)\n )" + }, + "product_passport_versions_select_public": { + "name": "product_passport_versions_select_public", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "anon" + ], + "using": "EXISTS (\n SELECT 1 FROM product_passports\n WHERE product_passports.id = passport_id\n AND product_passports.current_version_id IS NOT NULL\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.file_assets": { + "name": "file_assets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bytes": { + "name": "bytes", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "file_assets_bucket_path_unq": { + "name": "file_assets_bucket_path_unq", + "columns": [ + { + "expression": "bucket", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "file_assets_brand_id_brands_id_fk": { + "name": "file_assets_brand_id_brands_id_fk", + "tableFrom": "file_assets", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "file_assets_select_for_brand_members": { + "name": "file_assets_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "(brand_id IS NULL) OR is_brand_member(brand_id)" + }, + "file_assets_insert_by_brand_member_or_system": { + "name": "file_assets_insert_by_brand_member_or_system", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "(brand_id IS NULL) OR is_brand_member(brand_id)" + }, + "file_assets_update_by_brand_member_or_system": { + "name": "file_assets_update_by_brand_member_or_system", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "(brand_id IS NULL) OR is_brand_member(brand_id)" + }, + "file_assets_delete_by_brand_member_or_system": { + "name": "file_assets_delete_by_brand_member_or_system", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "(brand_id IS NULL) OR is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.import_jobs": { + "name": "import_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "filename": { + "name": "filename", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'CREATE'" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "commit_started_at": { + "name": "commit_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'PENDING'" + }, + "requires_value_approval": { + "name": "requires_value_approval", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "has_exportable_failures": { + "name": "has_exportable_failures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "summary": { + "name": "summary", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "correction_file_path": { + "name": "correction_file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "correction_download_url": { + "name": "correction_download_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "correction_expires_at": { + "name": "correction_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "user_email": { + "name": "user_email", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "import_jobs_brand_id_brands_id_fk": { + "name": "import_jobs_brand_id_brands_id_fk", + "tableFrom": "import_jobs", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "import_jobs_select_for_brand_members": { + "name": "import_jobs_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "import_jobs_insert_by_brand_member": { + "name": "import_jobs_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "import_jobs_update_by_brand_member": { + "name": "import_jobs_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "import_jobs_delete_by_brand_member": { + "name": "import_jobs_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.import_rows": { + "name": "import_rows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "job_id": { + "name": "job_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "row_number": { + "name": "row_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "raw": { + "name": "raw", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "normalized": { + "name": "normalized", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'PENDING'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "import_rows_job_row_unq": { + "name": "import_rows_job_row_unq", + "columns": [ + { + "expression": "job_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "row_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "import_rows_job_id_import_jobs_id_fk": { + "name": "import_rows_job_id_import_jobs_id_fk", + "tableFrom": "import_rows", + "tableTo": "import_jobs", + "columnsFrom": [ + "job_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "import_rows_select_for_brand_members": { + "name": "import_rows_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM import_jobs \n WHERE import_jobs.id = job_id \n AND is_brand_member(import_jobs.brand_id)\n )" + }, + "import_rows_insert_by_brand_member": { + "name": "import_rows_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM import_jobs \n WHERE import_jobs.id = job_id \n AND is_brand_member(import_jobs.brand_id)\n )" + }, + "import_rows_update_by_brand_member": { + "name": "import_rows_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM import_jobs \n WHERE import_jobs.id = job_id \n AND is_brand_member(import_jobs.brand_id)\n )" + }, + "import_rows_delete_by_brand_member": { + "name": "import_rows_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM import_jobs \n WHERE import_jobs.id = job_id \n AND is_brand_member(import_jobs.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.export_jobs": { + "name": "export_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_email": { + "name": "user_email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'PENDING'" + }, + "selection_mode": { + "name": "selection_mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "include_ids": { + "name": "include_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "exclude_ids": { + "name": "exclude_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "filter_state": { + "name": "filter_state", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "search_query": { + "name": "search_query", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "total_products": { + "name": "total_products", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "products_processed": { + "name": "products_processed", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "download_url": { + "name": "download_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "export_jobs_brand_id_brands_id_fk": { + "name": "export_jobs_brand_id_brands_id_fk", + "tableFrom": "export_jobs", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "export_jobs_user_id_users_id_fk": { + "name": "export_jobs_user_id_users_id_fk", + "tableFrom": "export_jobs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "export_jobs_select_for_brand_members": { + "name": "export_jobs_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "export_jobs_insert_by_brand_member": { + "name": "export_jobs_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "export_jobs_update_by_brand_member": { + "name": "export_jobs_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "export_jobs_delete_by_brand_member": { + "name": "export_jobs_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.qr_export_jobs": { + "name": "qr_export_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_email": { + "name": "user_email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'PENDING'" + }, + "selection_mode": { + "name": "selection_mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "include_ids": { + "name": "include_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "exclude_ids": { + "name": "exclude_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "filter_state": { + "name": "filter_state", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "search_query": { + "name": "search_query", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "custom_domain": { + "name": "custom_domain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "total_products": { + "name": "total_products", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "total_variants": { + "name": "total_variants", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "eligible_variants": { + "name": "eligible_variants", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "variants_processed": { + "name": "variants_processed", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "download_url": { + "name": "download_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "qr_export_jobs_brand_id_brands_id_fk": { + "name": "qr_export_jobs_brand_id_brands_id_fk", + "tableFrom": "qr_export_jobs", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "qr_export_jobs_user_id_users_id_fk": { + "name": "qr_export_jobs_user_id_users_id_fk", + "tableFrom": "qr_export_jobs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "qr_export_jobs_select_for_brand_members": { + "name": "qr_export_jobs_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "qr_export_jobs_insert_by_brand_member": { + "name": "qr_export_jobs_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "qr_export_jobs_update_by_brand_member": { + "name": "qr_export_jobs_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)", + "withCheck": "is_brand_member(brand_id)" + }, + "qr_export_jobs_delete_by_brand_member": { + "name": "qr_export_jobs_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_notifications": { + "name": "user_notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resource_type": { + "name": "resource_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resource_id": { + "name": "resource_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "action_url": { + "name": "action_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action_data": { + "name": "action_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "seen_at": { + "name": "seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "dismissed_at": { + "name": "dismissed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_user_notifications_user_unread": { + "name": "idx_user_notifications_user_unread", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "(seen_at IS NULL AND dismissed_at IS NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_user_notifications_expires": { + "name": "idx_user_notifications_expires", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "(expires_at IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_user_notifications_resource": { + "name": "idx_user_notifications_resource", + "columns": [ + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "(resource_type IS NOT NULL AND resource_id IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_notifications_user_id_users_id_fk": { + "name": "user_notifications_user_id_users_id_fk", + "tableFrom": "user_notifications", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "user_notifications_brand_id_brands_id_fk": { + "name": "user_notifications_brand_id_brands_id_fk", + "tableFrom": "user_notifications", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "user_notifications_select_own": { + "name": "user_notifications_select_own", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "auth.uid() = user_id" + }, + "user_notifications_insert_service": { + "name": "user_notifications_insert_service", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "user_notifications_update_own": { + "name": "user_notifications_update_own", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "auth.uid() = user_id" + }, + "user_notifications_delete_own": { + "name": "user_notifications_delete_own", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "auth.uid() = user_id" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.value_mappings": { + "name": "value_mappings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_column": { + "name": "source_column", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "raw_value": { + "name": "raw_value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target": { + "name": "target", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "value_mappings_brand_col_raw_unq": { + "name": "value_mappings_brand_col_raw_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "source_column", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + }, + { + "expression": "raw_value", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "value_mappings_brand_id_brands_id_fk": { + "name": "value_mappings_brand_id_brands_id_fk", + "tableFrom": "value_mappings", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "value_mappings_delete_by_brand_member": { + "name": "value_mappings_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "value_mappings_insert_by_brand_member": { + "name": "value_mappings_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "value_mappings_select_for_brand_members": { + "name": "value_mappings_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "value_mappings_update_by_brand_member": { + "name": "value_mappings_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_integrations": { + "name": "brand_integrations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "credentials": { + "name": "credentials", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credentials_iv": { + "name": "credentials_iv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shop_domain": { + "name": "shop_domain", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sync_interval": { + "name": "sync_interval", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 86400 + }, + "last_sync_at": { + "name": "last_sync_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_primary": { + "name": "is_primary", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "match_identifier": { + "name": "match_identifier", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'barcode'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_integrations_brand_integration_unq": { + "name": "brand_integrations_brand_integration_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_integrations_brand_id": { + "name": "idx_brand_integrations_brand_id", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_integrations_status": { + "name": "idx_brand_integrations_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "brand_integrations_primary_unq": { + "name": "brand_integrations_primary_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"brand_integrations\".\"is_primary\" = TRUE", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_integrations_brand_id_brands_id_fk": { + "name": "brand_integrations_brand_id_brands_id_fk", + "tableFrom": "brand_integrations", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "brand_integrations_integration_id_integrations_id_fk": { + "name": "brand_integrations_integration_id_integrations_id_fk", + "tableFrom": "brand_integrations", + "tableTo": "integrations", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_integrations_select_for_brand_members": { + "name": "brand_integrations_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_integrations_insert_by_brand_member": { + "name": "brand_integrations_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_integrations_update_by_brand_member": { + "name": "brand_integrations_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_integrations_delete_by_brand_member": { + "name": "brand_integrations_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_field_configs": { + "name": "integration_field_configs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "field_key": { + "name": "field_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ownership_enabled": { + "name": "ownership_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "source_option_key": { + "name": "source_option_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_field_configs_integration_field_unq": { + "name": "integration_field_configs_integration_field_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "field_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_field_configs_integration": { + "name": "idx_integration_field_configs_integration", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_field_configs_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_field_configs_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_field_configs", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_field_configs_select_for_brand_members": { + "name": "integration_field_configs_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_field_configs_insert_by_brand_member": { + "name": "integration_field_configs_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_field_configs_update_by_brand_member": { + "name": "integration_field_configs_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_field_configs_delete_by_brand_member": { + "name": "integration_field_configs_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integrations": { + "name": "integrations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "auth_type": { + "name": "auth_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integrations_slug_unq": { + "name": "integrations_slug_unq", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integrations_select_for_authenticated": { + "name": "integrations_select_for_authenticated", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "true" + }, + "integrations_insert_by_service_role": { + "name": "integrations_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "integrations_update_by_service_role": { + "name": "integrations_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true" + }, + "integrations_delete_by_service_role": { + "name": "integrations_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_states": { + "name": "oauth_states", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "integration_slug": { + "name": "integration_slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "shop_domain": { + "name": "shop_domain", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_oauth_states_state": { + "name": "idx_oauth_states_state", + "columns": [ + { + "expression": "state", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_oauth_states_expires": { + "name": "idx_oauth_states_expires", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_states_brand_id_brands_id_fk": { + "name": "oauth_states_brand_id_brands_id_fk", + "tableFrom": "oauth_states", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "oauth_states_select_by_service_role": { + "name": "oauth_states_select_by_service_role", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "service_role" + ], + "using": "true" + }, + "oauth_states_insert_by_service_role": { + "name": "oauth_states_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "oauth_states_update_by_service_role": { + "name": "oauth_states_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true" + }, + "oauth_states_delete_by_service_role": { + "name": "oauth_states_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pending_installations": { + "name": "pending_installations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "shop_domain": { + "name": "shop_domain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credentials": { + "name": "credentials", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credentials_iv": { + "name": "credentials_iv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "pending_installations_shop_domain_unq": { + "name": "pending_installations_shop_domain_unq", + "columns": [ + { + "expression": "shop_domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_pending_installations_expires": { + "name": "idx_pending_installations_expires", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "pending_installations_select_by_service_role": { + "name": "pending_installations_select_by_service_role", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "service_role" + ], + "using": "true" + }, + "pending_installations_insert_by_service_role": { + "name": "pending_installations_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "pending_installations_update_by_service_role": { + "name": "pending_installations_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true" + }, + "pending_installations_delete_by_service_role": { + "name": "pending_installations_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_sync_jobs": { + "name": "integration_sync_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "trigger_type": { + "name": "trigger_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'scheduled'" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "variants_processed": { + "name": "variants_processed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "variants_created": { + "name": "variants_created", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "variants_updated": { + "name": "variants_updated", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "variants_failed": { + "name": "variants_failed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "variants_skipped": { + "name": "variants_skipped", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "products_total": { + "name": "products_total", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "products_processed": { + "name": "products_processed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "products_created": { + "name": "products_created", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "products_updated": { + "name": "products_updated", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "products_failed": { + "name": "products_failed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "products_skipped": { + "name": "products_skipped", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "entities_created": { + "name": "entities_created", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "error_summary": { + "name": "error_summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_log": { + "name": "error_log", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_integration_sync_jobs_integration": { + "name": "idx_integration_sync_jobs_integration", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_sync_jobs_status": { + "name": "idx_integration_sync_jobs_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_sync_jobs_created": { + "name": "idx_integration_sync_jobs_created", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_sync_jobs_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_sync_jobs_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_sync_jobs", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_sync_jobs_select_for_brand_members": { + "name": "integration_sync_jobs_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_sync_jobs_insert_by_brand_member": { + "name": "integration_sync_jobs_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_sync_jobs_update_by_brand_member": { + "name": "integration_sync_jobs_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_sync_jobs_delete_by_brand_member": { + "name": "integration_sync_jobs_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.promotion_operations": { + "name": "promotion_operations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "new_primary_integration_id": { + "name": "new_primary_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "old_primary_integration_id": { + "name": "old_primary_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'preparing'" + }, + "phase": { + "name": "phase", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "variants_processed": { + "name": "variants_processed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_variants": { + "name": "total_variants", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "products_created": { + "name": "products_created", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "products_archived": { + "name": "products_archived", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "variants_moved": { + "name": "variants_moved", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "variants_orphaned": { + "name": "variants_orphaned", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "attributes_created": { + "name": "attributes_created", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_promotion_operations_brand_id": { + "name": "idx_promotion_operations_brand_id", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_promotion_operations_status": { + "name": "idx_promotion_operations_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "promotion_operations_brand_id_brands_id_fk": { + "name": "promotion_operations_brand_id_brands_id_fk", + "tableFrom": "promotion_operations", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "promotion_operations_new_primary_integration_id_brand_integrations_id_fk": { + "name": "promotion_operations_new_primary_integration_id_brand_integrations_id_fk", + "tableFrom": "promotion_operations", + "tableTo": "brand_integrations", + "columnsFrom": [ + "new_primary_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "promotion_operations_old_primary_integration_id_brand_integrations_id_fk": { + "name": "promotion_operations_old_primary_integration_id_brand_integrations_id_fk", + "tableFrom": "promotion_operations", + "tableTo": "brand_integrations", + "columnsFrom": [ + "old_primary_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "promotion_operations_select_for_brand_members": { + "name": "promotion_operations_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "promotion_operations_insert_by_brand_member": { + "name": "promotion_operations_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "promotion_operations_update_by_brand_member": { + "name": "promotion_operations_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "promotion_operations_delete_by_brand_member": { + "name": "promotion_operations_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_certification_links": { + "name": "integration_certification_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "certification_id": { + "name": "certification_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_name": { + "name": "external_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_certification_links_integration_external_unq": { + "name": "integration_certification_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_certification_links_integration_cert_unq": { + "name": "integration_certification_links_integration_cert_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "certification_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_certification_links_cert": { + "name": "idx_integration_certification_links_cert", + "columns": [ + { + "expression": "certification_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_certification_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_certification_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_certification_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_certification_links_certification_id_brand_certifications_id_fk": { + "name": "integration_certification_links_certification_id_brand_certifications_id_fk", + "tableFrom": "integration_certification_links", + "tableTo": "brand_certifications", + "columnsFrom": [ + "certification_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_certification_links_select_for_brand_members": { + "name": "integration_certification_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_certification_links_insert_by_brand_member": { + "name": "integration_certification_links_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_certification_links_update_by_brand_member": { + "name": "integration_certification_links_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_certification_links_delete_by_brand_member": { + "name": "integration_certification_links_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_manufacturer_links": { + "name": "integration_manufacturer_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "manufacturer_id": { + "name": "manufacturer_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_name": { + "name": "external_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_manufacturer_links_integration_external_unq": { + "name": "integration_manufacturer_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_manufacturer_links_integration_mfr_unq": { + "name": "integration_manufacturer_links_integration_mfr_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "manufacturer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_manufacturer_links_mfr": { + "name": "idx_integration_manufacturer_links_mfr", + "columns": [ + { + "expression": "manufacturer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_manufacturer_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_manufacturer_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_manufacturer_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_manufacturer_links_manufacturer_id_brand_manufacturers_id_fk": { + "name": "integration_manufacturer_links_manufacturer_id_brand_manufacturers_id_fk", + "tableFrom": "integration_manufacturer_links", + "tableTo": "brand_manufacturers", + "columnsFrom": [ + "manufacturer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_manufacturer_links_select_for_brand_members": { + "name": "integration_manufacturer_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_manufacturer_links_insert_by_brand_member": { + "name": "integration_manufacturer_links_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_manufacturer_links_update_by_brand_member": { + "name": "integration_manufacturer_links_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_manufacturer_links_delete_by_brand_member": { + "name": "integration_manufacturer_links_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_material_links": { + "name": "integration_material_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "material_id": { + "name": "material_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_name": { + "name": "external_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_material_links_integration_external_unq": { + "name": "integration_material_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_material_links_integration_material_unq": { + "name": "integration_material_links_integration_material_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "material_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_material_links_material": { + "name": "idx_integration_material_links_material", + "columns": [ + { + "expression": "material_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_material_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_material_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_material_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_material_links_material_id_brand_materials_id_fk": { + "name": "integration_material_links_material_id_brand_materials_id_fk", + "tableFrom": "integration_material_links", + "tableTo": "brand_materials", + "columnsFrom": [ + "material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_material_links_select_for_brand_members": { + "name": "integration_material_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_material_links_insert_by_brand_member": { + "name": "integration_material_links_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_material_links_update_by_brand_member": { + "name": "integration_material_links_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_material_links_delete_by_brand_member": { + "name": "integration_material_links_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_operator_links": { + "name": "integration_operator_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "operator_id": { + "name": "operator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_name": { + "name": "external_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_operator_links_integration_external_unq": { + "name": "integration_operator_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_operator_links_integration_operator_unq": { + "name": "integration_operator_links_integration_operator_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "operator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_operator_links_operator": { + "name": "idx_integration_operator_links_operator", + "columns": [ + { + "expression": "operator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_operator_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_operator_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_operator_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_operator_links_operator_id_brand_operators_id_fk": { + "name": "integration_operator_links_operator_id_brand_operators_id_fk", + "tableFrom": "integration_operator_links", + "tableTo": "brand_operators", + "columnsFrom": [ + "operator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_operator_links_select_for_brand_members": { + "name": "integration_operator_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_operator_links_insert_by_brand_member": { + "name": "integration_operator_links_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_operator_links_update_by_brand_member": { + "name": "integration_operator_links_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_operator_links_delete_by_brand_member": { + "name": "integration_operator_links_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_season_links": { + "name": "integration_season_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_name": { + "name": "external_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_season_links_integration_external_unq": { + "name": "integration_season_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_season_links_integration_season_unq": { + "name": "integration_season_links_integration_season_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_season_links_season": { + "name": "idx_integration_season_links_season", + "columns": [ + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_season_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_season_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_season_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_season_links_season_id_brand_seasons_id_fk": { + "name": "integration_season_links_season_id_brand_seasons_id_fk", + "tableFrom": "integration_season_links", + "tableTo": "brand_seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_season_links_select_for_brand_members": { + "name": "integration_season_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_season_links_insert_by_brand_member": { + "name": "integration_season_links_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_season_links_update_by_brand_member": { + "name": "integration_season_links_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_season_links_delete_by_brand_member": { + "name": "integration_season_links_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_tag_links": { + "name": "integration_tag_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tag_id": { + "name": "tag_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_name": { + "name": "external_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_tag_links_integration_external_unq": { + "name": "integration_tag_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_tag_links_integration_tag_unq": { + "name": "integration_tag_links_integration_tag_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "tag_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_tag_links_tag": { + "name": "idx_integration_tag_links_tag", + "columns": [ + { + "expression": "tag_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_tag_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_tag_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_tag_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_tag_links_tag_id_brand_tags_id_fk": { + "name": "integration_tag_links_tag_id_brand_tags_id_fk", + "tableFrom": "integration_tag_links", + "tableTo": "brand_tags", + "columnsFrom": [ + "tag_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_tag_links_select_for_brand_members": { + "name": "integration_tag_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_tag_links_insert_by_brand_member": { + "name": "integration_tag_links_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_tag_links_update_by_brand_member": { + "name": "integration_tag_links_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_tag_links_delete_by_brand_member": { + "name": "integration_tag_links_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_product_links": { + "name": "integration_product_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_name": { + "name": "external_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_synced_hash": { + "name": "last_synced_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_canonical": { + "name": "is_canonical", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_product_links_integration_external_unq": { + "name": "integration_product_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_product_links_canonical_unq": { + "name": "integration_product_links_canonical_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "is_canonical = true", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_product_links_product": { + "name": "idx_integration_product_links_product", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_product_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_product_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_product_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_product_links_product_id_products_id_fk": { + "name": "integration_product_links_product_id_products_id_fk", + "tableFrom": "integration_product_links", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_product_links_select_for_brand_members": { + "name": "integration_product_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_product_links_insert_by_brand_member": { + "name": "integration_product_links_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_product_links_update_by_brand_member": { + "name": "integration_product_links_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_product_links_delete_by_brand_member": { + "name": "integration_product_links_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_variant_links": { + "name": "integration_variant_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "variant_id": { + "name": "variant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_product_id": { + "name": "external_product_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "external_sku": { + "name": "external_sku", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "external_barcode": { + "name": "external_barcode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_synced_hash": { + "name": "last_synced_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_variant_links_integration_external_unq": { + "name": "integration_variant_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_variant_links_variant": { + "name": "idx_integration_variant_links_variant", + "columns": [ + { + "expression": "variant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_variant_links_integration": { + "name": "idx_integration_variant_links_integration", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_variant_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_variant_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_variant_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_variant_links_variant_id_product_variants_id_fk": { + "name": "integration_variant_links_variant_id_product_variants_id_fk", + "tableFrom": "integration_variant_links", + "tableTo": "product_variants", + "columnsFrom": [ + "variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_variant_links_select_for_brand_members": { + "name": "integration_variant_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_variant_links_insert_by_service": { + "name": "integration_variant_links_insert_by_service", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "integration_variant_links_update_by_service": { + "name": "integration_variant_links_update_by_service", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true" + }, + "integration_variant_links_delete_by_service": { + "name": "integration_variant_links_delete_by_service", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.stripe_webhook_events": { + "name": "stripe_webhook_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "stripe_event_id": { + "name": "stripe_event_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "stripe_webhook_events_stripe_event_id_unq": { + "name": "stripe_webhook_events_stripe_event_id_unq", + "columns": [ + { + "expression": "stripe_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_stripe_webhook_events_processed_at": { + "name": "idx_stripe_webhook_events_processed_at", + "columns": [ + { + "expression": "processed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "stripe_webhook_events_select_by_service_role": { + "name": "stripe_webhook_events_select_by_service_role", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "service_role" + ], + "using": "true" + }, + "stripe_webhook_events_insert_by_service_role": { + "name": "stripe_webhook_events_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "stripe_webhook_events_update_by_service_role": { + "name": "stripe_webhook_events_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true", + "withCheck": "true" + }, + "stripe_webhook_events_delete_by_service_role": { + "name": "stripe_webhook_events_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.taxonomy_categories": { + "name": "taxonomy_categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "public_id": { + "name": "public_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_id": { + "name": "parent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "categories_parent_name_unq": { + "name": "categories_parent_name_unq", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "categories_parent_id_idx": { + "name": "categories_parent_id_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "taxonomy_categories_parent_id_taxonomy_categories_id_fk": { + "name": "taxonomy_categories_parent_id_taxonomy_categories_id_fk", + "tableFrom": "taxonomy_categories", + "tableTo": "taxonomy_categories", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "taxonomy_categories_public_id_unique": { + "name": "taxonomy_categories_public_id_unique", + "nullsNotDistinct": false, + "columns": [ + "public_id" + ] + } + }, + "policies": { + "categories_select_for_authenticated": { + "name": "categories_select_for_authenticated", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "true" + }, + "categories_modify_system_only": { + "name": "categories_modify_system_only", + "as": "RESTRICTIVE", + "for": "ALL", + "to": [ + "authenticated", + "service_role" + ], + "using": "false", + "withCheck": "false" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.taxonomy_attributes": { + "name": "taxonomy_attributes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "friendly_id": { + "name": "friendly_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "public_id": { + "name": "public_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "taxonomy_attributes_friendly_id_idx": { + "name": "taxonomy_attributes_friendly_id_idx", + "columns": [ + { + "expression": "friendly_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "taxonomy_attributes_friendly_id_unique": { + "name": "taxonomy_attributes_friendly_id_unique", + "nullsNotDistinct": false, + "columns": [ + "friendly_id" + ] + }, + "taxonomy_attributes_public_id_unique": { + "name": "taxonomy_attributes_public_id_unique", + "nullsNotDistinct": false, + "columns": [ + "public_id" + ] + } + }, + "policies": { + "taxonomy_attributes_select_for_authenticated": { + "name": "taxonomy_attributes_select_for_authenticated", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.taxonomy_values": { + "name": "taxonomy_values", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "attribute_id": { + "name": "attribute_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "friendly_id": { + "name": "friendly_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "public_id": { + "name": "public_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "public_attribute_id": { + "name": "public_attribute_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "taxonomy_values_attribute_id_idx": { + "name": "taxonomy_values_attribute_id_idx", + "columns": [ + { + "expression": "attribute_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "taxonomy_values_friendly_id_idx": { + "name": "taxonomy_values_friendly_id_idx", + "columns": [ + { + "expression": "friendly_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "taxonomy_values_attribute_id_taxonomy_attributes_id_fk": { + "name": "taxonomy_values_attribute_id_taxonomy_attributes_id_fk", + "tableFrom": "taxonomy_values", + "tableTo": "taxonomy_attributes", + "columnsFrom": [ + "attribute_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "taxonomy_values_friendly_id_unique": { + "name": "taxonomy_values_friendly_id_unique", + "nullsNotDistinct": false, + "columns": [ + "friendly_id" + ] + }, + "taxonomy_values_public_id_unique": { + "name": "taxonomy_values_public_id_unique", + "nullsNotDistinct": false, + "columns": [ + "public_id" + ] + } + }, + "policies": { + "taxonomy_values_select_for_authenticated": { + "name": "taxonomy_values_select_for_authenticated", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.taxonomy_external_mappings": { + "name": "taxonomy_external_mappings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_system": { + "name": "source_system", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_taxonomy": { + "name": "source_taxonomy", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_taxonomy": { + "name": "target_taxonomy", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "taxonomy_external_mappings_slug_unq": { + "name": "taxonomy_external_mappings_slug_unq", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "taxonomy_external_mappings_source_idx": { + "name": "taxonomy_external_mappings_source_idx", + "columns": [ + { + "expression": "source_system", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_taxonomy", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "taxonomy_external_mappings_select_for_authenticated": { + "name": "taxonomy_external_mappings_select_for_authenticated", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.user_role": { + "name": "user_role", + "schema": "public", + "values": [ + "owner", + "member" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/api/supabase/migrations/meta/20260306125055_snapshot.json b/apps/api/supabase/migrations/meta/20260306125055_snapshot.json new file mode 100644 index 00000000..5f3531c9 --- /dev/null +++ b/apps/api/supabase/migrations/meta/20260306125055_snapshot.json @@ -0,0 +1,11053 @@ +{ + "id": "52f117a8-2518-4299-985d-6e77cbe62673", + "prevId": "e146b2a1-772b-475f-b7cc-5a1d2b2adb9f", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "full_name": { + "name": "full_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "avatar_path": { + "name": "avatar_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_users_brand_id": { + "name": "idx_users_brand_id", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "where": "(brand_id IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "users_id_users_id_fk": { + "name": "users_id_users_id_fk", + "tableFrom": "users", + "tableTo": "users", + "schemaTo": "auth", + "columnsFrom": [ + "id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_key": { + "name": "users_email_key", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": { + "users_select_own_profile": { + "name": "users_select_own_profile", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "auth.uid() = id" + }, + "users_select_for_brand_members": { + "name": "users_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "shares_brand_with(id)" + }, + "users_update_own_profile": { + "name": "users_update_own_profile", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "auth.uid() = id", + "withCheck": "\n auth.uid() = id\n AND (\n brand_id IS NULL\n OR EXISTS (\n SELECT 1\n FROM brand_members bm\n WHERE bm.brand_id = users.brand_id\n AND bm.user_id = auth.uid()\n )\n )\n " + }, + "users_insert_own_profile": { + "name": "users_insert_own_profile", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "auth.uid() = id" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brands": { + "name": "brands", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "country_code": { + "name": "country_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo_path": { + "name": "logo_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_brands_active": { + "name": "idx_brands_active", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "(deleted_at IS NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brands_slug": { + "name": "idx_brands_slug", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "(slug IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brands_email": { + "name": "idx_brands_email", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brands_update_by_member": { + "name": "brands_update_by_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(id)" + }, + "brands_delete_by_owner": { + "name": "brands_delete_by_owner", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_owner(id)" + }, + "brands_select_for_invite_recipients": { + "name": "brands_select_for_invite_recipients", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ] + }, + "brands_select_for_members": { + "name": "brands_select_for_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(id)" + }, + "brands_insert_by_authenticated": { + "name": "brands_insert_by_authenticated", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ] + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.platform_admin_audit_logs": { + "name": "platform_admin_audit_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_type": { + "name": "resource_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_platform_admin_audit_logs_actor_user_id": { + "name": "idx_platform_admin_audit_logs_actor_user_id", + "columns": [ + { + "expression": "actor_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_platform_admin_audit_logs_resource": { + "name": "idx_platform_admin_audit_logs_resource", + "columns": [ + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_platform_admin_audit_logs_created_at": { + "name": "idx_platform_admin_audit_logs_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "platform_admin_audit_logs_actor_user_id_users_id_fk": { + "name": "platform_admin_audit_logs_actor_user_id_users_id_fk", + "tableFrom": "platform_admin_audit_logs", + "tableTo": "users", + "columnsFrom": [ + "actor_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "platform_admin_audit_logs_select_by_service_role": { + "name": "platform_admin_audit_logs_select_by_service_role", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "service_role" + ], + "using": "true" + }, + "platform_admin_audit_logs_insert_by_service_role": { + "name": "platform_admin_audit_logs_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "platform_admin_audit_logs_update_by_service_role": { + "name": "platform_admin_audit_logs_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true", + "withCheck": "true" + }, + "platform_admin_audit_logs_delete_by_service_role": { + "name": "platform_admin_audit_logs_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.platform_admin_allowlist": { + "name": "platform_admin_allowlist", + "schema": "", + "columns": { + "email": { + "name": "email", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ux_platform_admin_allowlist_user_id_not_null": { + "name": "ux_platform_admin_allowlist_user_id_not_null", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "(user_id IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "platform_admin_allowlist_user_id_users_id_fk": { + "name": "platform_admin_allowlist_user_id_users_id_fk", + "tableFrom": "platform_admin_allowlist", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "platform_admin_allowlist_select_by_service_role": { + "name": "platform_admin_allowlist_select_by_service_role", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "service_role" + ], + "using": "true" + }, + "platform_admin_allowlist_insert_by_service_role": { + "name": "platform_admin_allowlist_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "platform_admin_allowlist_update_by_service_role": { + "name": "platform_admin_allowlist_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true", + "withCheck": "true" + }, + "platform_admin_allowlist_delete_by_service_role": { + "name": "platform_admin_allowlist_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_collections": { + "name": "brand_collections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filter": { + "name": "filter", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_collections_brand_name_unq": { + "name": "brand_collections_brand_name_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_collections_brand_id_brands_id_fk": { + "name": "brand_collections_brand_id_brands_id_fk", + "tableFrom": "brand_collections", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_collections_select_for_brand_members": { + "name": "brand_collections_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_collections_insert_by_brand_member": { + "name": "brand_collections_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_collections_update_by_brand_member": { + "name": "brand_collections_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_collections_delete_by_brand_member": { + "name": "brand_collections_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_members": { + "name": "brand_members", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ux_brand_members_user_brand": { + "name": "ux_brand_members_user_brand", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_members_brand_id": { + "name": "idx_brand_members_brand_id", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_members_user_id": { + "name": "idx_brand_members_user_id", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_members_user_id_users_id_fk": { + "name": "brand_members_user_id_users_id_fk", + "tableFrom": "brand_members", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "brand_members_brand_id_brands_id_fk": { + "name": "brand_members_brand_id_brands_id_fk", + "tableFrom": "brand_members", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "brand_members_user_id_brand_id_key": { + "name": "brand_members_user_id_brand_id_key", + "nullsNotDistinct": false, + "columns": [ + "user_id", + "brand_id" + ] + } + }, + "policies": { + "brand_members_update_by_owner": { + "name": "brand_members_update_by_owner", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_owner(brand_id)" + }, + "brand_members_select_for_members": { + "name": "brand_members_select_for_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_members_delete_self": { + "name": "brand_members_delete_self", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ] + }, + "brand_members_delete_owner_non_owner": { + "name": "brand_members_delete_owner_non_owner", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ] + }, + "brand_members_insert_first_owner_self": { + "name": "brand_members_insert_first_owner_self", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ] + } + }, + "checkConstraints": { + "brand_members_role_check": { + "name": "brand_members_role_check", + "value": "role = ANY (ARRAY['owner'::text, 'member'::text, 'avelero'::text])" + } + }, + "isRLSEnabled": false + }, + "public.brand_invites": { + "name": "brand_invites", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_brand_invites_brand_id": { + "name": "idx_brand_invites_brand_id", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_invites_expires_at": { + "name": "idx_brand_invites_expires_at", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_invites_token_hash": { + "name": "idx_brand_invites_token_hash", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_invites_valid_lookup": { + "name": "idx_brand_invites_valid_lookup", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_invites_email": { + "name": "idx_brand_invites_email", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_invites_email_expires": { + "name": "idx_brand_invites_email_expires", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ux_brand_invites_token_hash_not_null": { + "name": "ux_brand_invites_token_hash_not_null", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": true, + "where": "(token_hash IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_invites_brand_id_brands_id_fk": { + "name": "brand_invites_brand_id_brands_id_fk", + "tableFrom": "brand_invites", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_invites_update_by_owner": { + "name": "brand_invites_update_by_owner", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_owner(brand_id)" + }, + "brand_invites_insert_by_owner": { + "name": "brand_invites_insert_by_owner", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_owner(brand_id)" + }, + "brand_invites_delete_by_owner": { + "name": "brand_invites_delete_by_owner", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_owner(brand_id)" + }, + "brand_invites_select_for_recipient": { + "name": "brand_invites_select_for_recipient", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ] + }, + "brand_invites_delete_by_recipient": { + "name": "brand_invites_delete_by_recipient", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ] + }, + "brand_invites_select_for_members": { + "name": "brand_invites_select_for_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ] + } + }, + "checkConstraints": { + "brand_invites_role_check": { + "name": "brand_invites_role_check", + "value": "role = ANY (ARRAY['owner'::text, 'member'::text])" + } + }, + "isRLSEnabled": false + }, + "public.brand_tags": { + "name": "brand_tags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hex": { + "name": "hex", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_tags_brand_name_unq": { + "name": "brand_tags_brand_name_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_tags_brand_id_brands_id_fk": { + "name": "brand_tags_brand_id_brands_id_fk", + "tableFrom": "brand_tags", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_tags_select_for_brand_members": { + "name": "brand_tags_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_tags_insert_by_brand_members": { + "name": "brand_tags_insert_by_brand_members", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_tags_update_by_brand_members": { + "name": "brand_tags_update_by_brand_members", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_tags_delete_by_brand_members": { + "name": "brand_tags_delete_by_brand_members", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_theme": { + "name": "brand_theme", + "schema": "", + "columns": { + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "theme_styles": { + "name": "theme_styles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "theme_config": { + "name": "theme_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "stylesheet_path": { + "name": "stylesheet_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "google_fonts_url": { + "name": "google_fonts_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_brand_theme_updated_at": { + "name": "idx_brand_theme_updated_at", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_theme_brand_id_brands_id_fk": { + "name": "brand_theme_brand_id_brands_id_fk", + "tableFrom": "brand_theme", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "brand_theme_brand_id_pkey": { + "name": "brand_theme_brand_id_pkey", + "columns": [ + "brand_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": { + "brand_theme_select_for_brand_members": { + "name": "brand_theme_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_theme_insert_by_brand_member": { + "name": "brand_theme_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_theme_update_by_brand_member": { + "name": "brand_theme_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)", + "withCheck": "is_brand_member(brand_id)" + }, + "brand_theme_delete_by_brand_member": { + "name": "brand_theme_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_custom_domains": { + "name": "brand_custom_domains", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "verification_token": { + "name": "verification_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_verification_attempt": { + "name": "last_verification_attempt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "verification_error": { + "name": "verification_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "verified_at": { + "name": "verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_custom_domains_brand_id_unq": { + "name": "brand_custom_domains_brand_id_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "brand_custom_domains_domain_unq": { + "name": "brand_custom_domains_domain_unq", + "columns": [ + { + "expression": "domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_custom_domains_status": { + "name": "idx_brand_custom_domains_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_custom_domains_brand_id_brands_id_fk": { + "name": "brand_custom_domains_brand_id_brands_id_fk", + "tableFrom": "brand_custom_domains", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_custom_domains_select_for_brand_members": { + "name": "brand_custom_domains_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_custom_domains_insert_by_brand_owner": { + "name": "brand_custom_domains_insert_by_brand_owner", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_owner(brand_id)" + }, + "brand_custom_domains_update_by_brand_owner": { + "name": "brand_custom_domains_update_by_brand_owner", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_owner(brand_id)", + "withCheck": "is_brand_owner(brand_id)" + }, + "brand_custom_domains_delete_by_brand_owner": { + "name": "brand_custom_domains_delete_by_brand_owner", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_owner(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_lifecycle": { + "name": "brand_lifecycle", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "phase": { + "name": "phase", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'demo'" + }, + "phase_changed_at": { + "name": "phase_changed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "trial_started_at": { + "name": "trial_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "trial_ends_at": { + "name": "trial_ends_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "hard_delete_after": { + "name": "hard_delete_after", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_lifecycle_brand_id_unq": { + "name": "brand_lifecycle_brand_id_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_lifecycle_phase": { + "name": "idx_brand_lifecycle_phase", + "columns": [ + { + "expression": "phase", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_lifecycle_trial_ends_at": { + "name": "idx_brand_lifecycle_trial_ends_at", + "columns": [ + { + "expression": "trial_ends_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_lifecycle_hard_delete_after": { + "name": "idx_brand_lifecycle_hard_delete_after", + "columns": [ + { + "expression": "hard_delete_after", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "(hard_delete_after IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_lifecycle_brand_id_brands_id_fk": { + "name": "brand_lifecycle_brand_id_brands_id_fk", + "tableFrom": "brand_lifecycle", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_lifecycle_select_for_brand_members": { + "name": "brand_lifecycle_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_lifecycle_insert_by_service_role": { + "name": "brand_lifecycle_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "brand_lifecycle_update_by_service_role": { + "name": "brand_lifecycle_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true", + "withCheck": "true" + }, + "brand_lifecycle_delete_by_service_role": { + "name": "brand_lifecycle_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": { + "brand_lifecycle_phase_check": { + "name": "brand_lifecycle_phase_check", + "value": "phase = ANY (ARRAY['demo'::text, 'trial'::text, 'expired'::text, 'active'::text, 'past_due'::text, 'suspended'::text, 'cancelled'::text])" + } + }, + "isRLSEnabled": false + }, + "public.brand_plan": { + "name": "brand_plan", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "plan_type": { + "name": "plan_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "plan_selected_at": { + "name": "plan_selected_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "sku_annual_limit": { + "name": "sku_annual_limit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "sku_onboarding_limit": { + "name": "sku_onboarding_limit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "sku_limit_override": { + "name": "sku_limit_override", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "sku_year_start": { + "name": "sku_year_start", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "skus_created_this_year": { + "name": "skus_created_this_year", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "skus_created_onboarding": { + "name": "skus_created_onboarding", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "max_seats": { + "name": "max_seats", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_plan_brand_id_unq": { + "name": "brand_plan_brand_id_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_plan_plan_type": { + "name": "idx_brand_plan_plan_type", + "columns": [ + { + "expression": "plan_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_plan_sku_year_start": { + "name": "idx_brand_plan_sku_year_start", + "columns": [ + { + "expression": "sku_year_start", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_plan_brand_id_brands_id_fk": { + "name": "brand_plan_brand_id_brands_id_fk", + "tableFrom": "brand_plan", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_plan_select_for_brand_members": { + "name": "brand_plan_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_plan_insert_by_service_role": { + "name": "brand_plan_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "brand_plan_update_by_service_role": { + "name": "brand_plan_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true", + "withCheck": "true" + }, + "brand_plan_delete_by_service_role": { + "name": "brand_plan_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": { + "brand_plan_type_check": { + "name": "brand_plan_type_check", + "value": "plan_type IS NULL OR plan_type = ANY (ARRAY['starter'::text, 'growth'::text, 'scale'::text, 'enterprise'::text])" + }, + "brand_plan_sku_annual_limit_check": { + "name": "brand_plan_sku_annual_limit_check", + "value": "sku_annual_limit IS NULL OR sku_annual_limit >= 0" + }, + "brand_plan_sku_onboarding_limit_check": { + "name": "brand_plan_sku_onboarding_limit_check", + "value": "sku_onboarding_limit IS NULL OR sku_onboarding_limit >= 0" + }, + "brand_plan_sku_limit_override_check": { + "name": "brand_plan_sku_limit_override_check", + "value": "sku_limit_override IS NULL OR sku_limit_override >= 0" + }, + "brand_plan_skus_created_this_year_check": { + "name": "brand_plan_skus_created_this_year_check", + "value": "skus_created_this_year >= 0" + }, + "brand_plan_skus_created_onboarding_check": { + "name": "brand_plan_skus_created_onboarding_check", + "value": "skus_created_onboarding >= 0" + }, + "brand_plan_max_seats_check": { + "name": "brand_plan_max_seats_check", + "value": "max_seats IS NULL OR max_seats > 0" + } + }, + "isRLSEnabled": false + }, + "public.brand_billing": { + "name": "brand_billing", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "billing_mode": { + "name": "billing_mode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_subscription_id": { + "name": "stripe_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "plan_currency": { + "name": "plan_currency", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'EUR'" + }, + "custom_monthly_price_cents": { + "name": "custom_monthly_price_cents", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "billing_access_override": { + "name": "billing_access_override", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "billing_override_expires_at": { + "name": "billing_override_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_billing_brand_id_unq": { + "name": "brand_billing_brand_id_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "brand_billing_stripe_customer_id_unq": { + "name": "brand_billing_stripe_customer_id_unq", + "columns": [ + { + "expression": "stripe_customer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "(stripe_customer_id IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "brand_billing_stripe_subscription_id_unq": { + "name": "brand_billing_stripe_subscription_id_unq", + "columns": [ + { + "expression": "stripe_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "(stripe_subscription_id IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_billing_override_expires": { + "name": "idx_brand_billing_override_expires", + "columns": [ + { + "expression": "billing_override_expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_billing_brand_id_brands_id_fk": { + "name": "brand_billing_brand_id_brands_id_fk", + "tableFrom": "brand_billing", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_billing_select_for_brand_members": { + "name": "brand_billing_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_billing_insert_by_service_role": { + "name": "brand_billing_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "brand_billing_update_by_service_role": { + "name": "brand_billing_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true", + "withCheck": "true" + }, + "brand_billing_delete_by_service_role": { + "name": "brand_billing_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": { + "brand_billing_mode_check": { + "name": "brand_billing_mode_check", + "value": "billing_mode IS NULL OR billing_mode = ANY (ARRAY['stripe_checkout'::text, 'stripe_invoice'::text])" + }, + "brand_billing_plan_currency_check": { + "name": "brand_billing_plan_currency_check", + "value": "char_length(plan_currency) = 3" + }, + "brand_billing_custom_monthly_price_check": { + "name": "brand_billing_custom_monthly_price_check", + "value": "custom_monthly_price_cents IS NULL OR custom_monthly_price_cents >= 0" + }, + "brand_billing_access_override_check": { + "name": "brand_billing_access_override_check", + "value": "billing_access_override = ANY (ARRAY['none'::text, 'temporary_allow'::text, 'temporary_block'::text])" + } + }, + "isRLSEnabled": false + }, + "public.brand_billing_events": { + "name": "brand_billing_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_event_id": { + "name": "stripe_event_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_brand_billing_events_brand_created_at": { + "name": "idx_brand_billing_events_brand_created_at", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_billing_events_event_type": { + "name": "idx_brand_billing_events_event_type", + "columns": [ + { + "expression": "event_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "brand_billing_events_stripe_event_id_unq": { + "name": "brand_billing_events_stripe_event_id_unq", + "columns": [ + { + "expression": "stripe_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "(stripe_event_id IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_billing_events_brand_id_brands_id_fk": { + "name": "brand_billing_events_brand_id_brands_id_fk", + "tableFrom": "brand_billing_events", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_billing_events_select_for_brand_members": { + "name": "brand_billing_events_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_billing_events_insert_by_service_role": { + "name": "brand_billing_events_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "brand_billing_events_update_by_service_role": { + "name": "brand_billing_events_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true", + "withCheck": "true" + }, + "brand_billing_events_delete_by_service_role": { + "name": "brand_billing_events_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_attributes": { + "name": "brand_attributes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "taxonomy_attribute_id": { + "name": "taxonomy_attribute_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_attributes_brand_name_unq": { + "name": "brand_attributes_brand_name_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_attributes_brand_id_brands_id_fk": { + "name": "brand_attributes_brand_id_brands_id_fk", + "tableFrom": "brand_attributes", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "brand_attributes_taxonomy_attribute_id_taxonomy_attributes_id_fk": { + "name": "brand_attributes_taxonomy_attribute_id_taxonomy_attributes_id_fk", + "tableFrom": "brand_attributes", + "tableTo": "taxonomy_attributes", + "columnsFrom": [ + "taxonomy_attribute_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_attributes_select_for_brand_members": { + "name": "brand_attributes_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_attributes_insert_by_brand_member": { + "name": "brand_attributes_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_attributes_update_by_brand_member": { + "name": "brand_attributes_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_attributes_delete_by_brand_member": { + "name": "brand_attributes_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_attribute_values": { + "name": "brand_attribute_values", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "attribute_id": { + "name": "attribute_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "taxonomy_value_id": { + "name": "taxonomy_value_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_attribute_values_brand_attr_name_unq": { + "name": "brand_attribute_values_brand_attr_name_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "attribute_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_attribute_values_brand_id_brands_id_fk": { + "name": "brand_attribute_values_brand_id_brands_id_fk", + "tableFrom": "brand_attribute_values", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "brand_attribute_values_attribute_id_brand_attributes_id_fk": { + "name": "brand_attribute_values_attribute_id_brand_attributes_id_fk", + "tableFrom": "brand_attribute_values", + "tableTo": "brand_attributes", + "columnsFrom": [ + "attribute_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "brand_attribute_values_taxonomy_value_id_taxonomy_values_id_fk": { + "name": "brand_attribute_values_taxonomy_value_id_taxonomy_values_id_fk", + "tableFrom": "brand_attribute_values", + "tableTo": "taxonomy_values", + "columnsFrom": [ + "taxonomy_value_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_attribute_values_select_for_brand_members": { + "name": "brand_attribute_values_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_attribute_values_insert_by_brand_member": { + "name": "brand_attribute_values_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_attribute_values_update_by_brand_member": { + "name": "brand_attribute_values_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_attribute_values_delete_by_brand_member": { + "name": "brand_attribute_values_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_certifications": { + "name": "brand_certifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certification_code": { + "name": "certification_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_name": { + "name": "institute_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_email": { + "name": "institute_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_website": { + "name": "institute_website", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_address_line_1": { + "name": "institute_address_line_1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_address_line_2": { + "name": "institute_address_line_2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_city": { + "name": "institute_city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_state": { + "name": "institute_state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_zip": { + "name": "institute_zip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "institute_country_code": { + "name": "institute_country_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "issue_date": { + "name": "issue_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "expiry_date": { + "name": "expiry_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "certification_path": { + "name": "certification_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "brand_certifications_brand_id_brands_id_fk": { + "name": "brand_certifications_brand_id_brands_id_fk", + "tableFrom": "brand_certifications", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_certifications_select_for_brand_members": { + "name": "brand_certifications_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_certifications_insert_by_brand_member": { + "name": "brand_certifications_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_certifications_update_by_brand_member": { + "name": "brand_certifications_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_certifications_delete_by_brand_member": { + "name": "brand_certifications_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_operators": { + "name": "brand_operators", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "legal_name": { + "name": "legal_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "website": { + "name": "website", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_line_1": { + "name": "address_line_1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_line_2": { + "name": "address_line_2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "city": { + "name": "city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "zip": { + "name": "zip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "country_code": { + "name": "country_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "brand_operators_brand_id_brands_id_fk": { + "name": "brand_operators_brand_id_brands_id_fk", + "tableFrom": "brand_operators", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_operators_select_for_brand_members": { + "name": "brand_operators_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_operators_insert_by_brand_member": { + "name": "brand_operators_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_operators_update_by_brand_member": { + "name": "brand_operators_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_operators_delete_by_brand_member": { + "name": "brand_operators_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_manufacturers": { + "name": "brand_manufacturers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "legal_name": { + "name": "legal_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "website": { + "name": "website", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_line_1": { + "name": "address_line_1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_line_2": { + "name": "address_line_2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "city": { + "name": "city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "zip": { + "name": "zip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "country_code": { + "name": "country_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_manufacturers_brand_name_unq": { + "name": "brand_manufacturers_brand_name_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_manufacturers_brand_id_brands_id_fk": { + "name": "brand_manufacturers_brand_id_brands_id_fk", + "tableFrom": "brand_manufacturers", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_manufacturers_select_for_brand_members": { + "name": "brand_manufacturers_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_manufacturers_insert_by_brand_member": { + "name": "brand_manufacturers_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_manufacturers_update_by_brand_member": { + "name": "brand_manufacturers_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_manufacturers_delete_by_brand_member": { + "name": "brand_manufacturers_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_materials": { + "name": "brand_materials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certification_id": { + "name": "certification_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "recyclable": { + "name": "recyclable", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "country_of_origin": { + "name": "country_of_origin", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "brand_materials_brand_id_brands_id_fk": { + "name": "brand_materials_brand_id_brands_id_fk", + "tableFrom": "brand_materials", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "brand_materials_certification_id_brand_certifications_id_fk": { + "name": "brand_materials_certification_id_brand_certifications_id_fk", + "tableFrom": "brand_materials", + "tableTo": "brand_certifications", + "columnsFrom": [ + "certification_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_materials_select_for_brand_members": { + "name": "brand_materials_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_materials_insert_by_brand_member": { + "name": "brand_materials_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_materials_update_by_brand_member": { + "name": "brand_materials_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_materials_delete_by_brand_member": { + "name": "brand_materials_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_seasons": { + "name": "brand_seasons", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "start_date": { + "name": "start_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "end_date": { + "name": "end_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "ongoing": { + "name": "ongoing", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_seasons_brand_name_unq": { + "name": "brand_seasons_brand_name_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_seasons_brand_id_brands_id_fk": { + "name": "brand_seasons_brand_id_brands_id_fk", + "tableFrom": "brand_seasons", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_seasons_select_for_brand_members": { + "name": "brand_seasons_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_seasons_insert_by_brand_members": { + "name": "brand_seasons_insert_by_brand_members", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_seasons_update_by_brand_members": { + "name": "brand_seasons_update_by_brand_members", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_seasons_delete_by_brand_members": { + "name": "brand_seasons_delete_by_brand_members", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.products": { + "name": "products", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "product_handle": { + "name": "product_handle", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "manufacturer_id": { + "name": "manufacturer_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "image_path": { + "name": "image_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "category_id": { + "name": "category_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unpublished'" + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "source_integration_id": { + "name": "source_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "products_brand_id_product_handle_unq": { + "name": "products_brand_id_product_handle_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "product_handle", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_brand_id": { + "name": "idx_products_brand_id", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_brand_status": { + "name": "idx_products_brand_status", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_brand_created": { + "name": "idx_products_brand_created", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_brand_product_handle": { + "name": "idx_products_brand_product_handle", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "product_handle", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_brand_category": { + "name": "idx_products_brand_category", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "category_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_brand_season": { + "name": "idx_products_brand_season", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_brand_name": { + "name": "idx_products_brand_name", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_products_name": { + "name": "idx_products_name", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "products_brand_id_brands_id_fk": { + "name": "products_brand_id_brands_id_fk", + "tableFrom": "products", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "products_manufacturer_id_brand_manufacturers_id_fk": { + "name": "products_manufacturer_id_brand_manufacturers_id_fk", + "tableFrom": "products", + "tableTo": "brand_manufacturers", + "columnsFrom": [ + "manufacturer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "products_category_id_taxonomy_categories_id_fk": { + "name": "products_category_id_taxonomy_categories_id_fk", + "tableFrom": "products", + "tableTo": "taxonomy_categories", + "columnsFrom": [ + "category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "products_season_id_brand_seasons_id_fk": { + "name": "products_season_id_brand_seasons_id_fk", + "tableFrom": "products", + "tableTo": "brand_seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "products_source_integration_id_brand_integrations_id_fk": { + "name": "products_source_integration_id_brand_integrations_id_fk", + "tableFrom": "products", + "tableTo": "brand_integrations", + "columnsFrom": [ + "source_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "products_select_for_brand_members": { + "name": "products_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "products_insert_by_brand_members": { + "name": "products_insert_by_brand_members", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "products_update_by_brand_members": { + "name": "products_update_by_brand_members", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "products_delete_by_brand_members": { + "name": "products_delete_by_brand_members", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_variants": { + "name": "product_variants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "barcode": { + "name": "barcode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sku": { + "name": "sku", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "upid": { + "name": "upid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "image_path": { + "name": "image_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_integration": { + "name": "source_integration", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_external_id": { + "name": "source_external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_ghost": { + "name": "is_ghost", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_product_variants_product_id": { + "name": "idx_product_variants_product_id", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_variants_product_created": { + "name": "idx_product_variants_product_created", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_variants_upid": { + "name": "idx_product_variants_upid", + "columns": [ + { + "expression": "upid", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "where": "(upid IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_unique_upid_global": { + "name": "idx_unique_upid_global", + "columns": [ + { + "expression": "upid", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "upid IS NOT NULL AND upid != ''", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_unique_barcode_per_brand": { + "name": "idx_unique_barcode_per_brand", + "columns": [ + { + "expression": "barcode", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "get_product_brand_id(product_id)", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "barcode IS NOT NULL AND barcode != ''", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_variants_product_id_products_id_fk": { + "name": "product_variants_product_id_products_id_fk", + "tableFrom": "product_variants", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "product_variants_select_for_brand_members": { + "name": "product_variants_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_variants_insert_by_brand_member": { + "name": "product_variants_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_variants_update_by_brand_member": { + "name": "product_variants_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_variants_delete_by_brand_member": { + "name": "product_variants_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_materials": { + "name": "product_materials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "brand_material_id": { + "name": "brand_material_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "percentage": { + "name": "percentage", + "type": "numeric(6, 2)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "product_materials_product_material_unq": { + "name": "product_materials_product_material_unq", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "brand_material_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_materials_product_id": { + "name": "idx_product_materials_product_id", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_materials_product_created": { + "name": "idx_product_materials_product_created", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_materials_product_id_products_id_fk": { + "name": "product_materials_product_id_products_id_fk", + "tableFrom": "product_materials", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "product_materials_brand_material_id_brand_materials_id_fk": { + "name": "product_materials_brand_material_id_brand_materials_id_fk", + "tableFrom": "product_materials", + "tableTo": "brand_materials", + "columnsFrom": [ + "brand_material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "product_materials_select_for_brand_members": { + "name": "product_materials_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_materials_insert_by_brand_member": { + "name": "product_materials_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_materials_update_by_brand_member": { + "name": "product_materials_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_materials_delete_by_brand_member": { + "name": "product_materials_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_journey_steps": { + "name": "product_journey_steps", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sort_index": { + "name": "sort_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "step_type": { + "name": "step_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "operator_id": { + "name": "operator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "product_journey_steps_product_sort_operator_unq": { + "name": "product_journey_steps_product_sort_operator_unq", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_index", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "operator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_journey_steps_product_id": { + "name": "idx_product_journey_steps_product_id", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_journey_steps_product_sort": { + "name": "idx_product_journey_steps_product_sort", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "sort_index", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_journey_steps_product_id_products_id_fk": { + "name": "product_journey_steps_product_id_products_id_fk", + "tableFrom": "product_journey_steps", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "product_journey_steps_operator_id_brand_operators_id_fk": { + "name": "product_journey_steps_operator_id_brand_operators_id_fk", + "tableFrom": "product_journey_steps", + "tableTo": "brand_operators", + "columnsFrom": [ + "operator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "product_journey_steps_select_for_brand_members": { + "name": "product_journey_steps_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_journey_steps_insert_by_brand_member": { + "name": "product_journey_steps_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_journey_steps_update_by_brand_member": { + "name": "product_journey_steps_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_journey_steps_delete_by_brand_member": { + "name": "product_journey_steps_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_environment": { + "name": "product_environment", + "schema": "", + "columns": { + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "numeric(12, 4)", + "primaryKey": false, + "notNull": false + }, + "unit": { + "name": "unit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metric": { + "name": "metric", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "product_environment_product_id_products_id_fk": { + "name": "product_environment_product_id_products_id_fk", + "tableFrom": "product_environment", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "product_environment_pkey": { + "name": "product_environment_pkey", + "columns": [ + "product_id", + "metric" + ] + } + }, + "uniqueConstraints": {}, + "policies": { + "product_environment_select_for_brand_members": { + "name": "product_environment_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_environment_insert_by_brand_member": { + "name": "product_environment_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_environment_update_by_brand_member": { + "name": "product_environment_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_environment_delete_by_brand_member": { + "name": "product_environment_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_tags": { + "name": "product_tags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tag_id": { + "name": "tag_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tags_on_product_tag_product_unq": { + "name": "tags_on_product_tag_product_unq", + "columns": [ + { + "expression": "tag_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tags_on_product_tag_id_idx": { + "name": "tags_on_product_tag_id_idx", + "columns": [ + { + "expression": "tag_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tags_on_product_product_id_idx": { + "name": "tags_on_product_product_id_idx", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_tags_tag_id_brand_tags_id_fk": { + "name": "product_tags_tag_id_brand_tags_id_fk", + "tableFrom": "product_tags", + "tableTo": "brand_tags", + "columnsFrom": [ + "tag_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "product_tags_product_id_products_id_fk": { + "name": "product_tags_product_id_products_id_fk", + "tableFrom": "product_tags", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "tags_on_product_select_for_brand_members": { + "name": "tags_on_product_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "tags_on_product_insert_by_brand_members": { + "name": "tags_on_product_insert_by_brand_members", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "tags_on_product_delete_by_brand_members": { + "name": "tags_on_product_delete_by_brand_members", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_variant_attributes": { + "name": "product_variant_attributes", + "schema": "", + "columns": { + "variant_id": { + "name": "variant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "attribute_value_id": { + "name": "attribute_value_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_product_variant_attributes_variant": { + "name": "idx_product_variant_attributes_variant", + "columns": [ + { + "expression": "variant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_variant_attributes_variant_id_product_variants_id_fk": { + "name": "product_variant_attributes_variant_id_product_variants_id_fk", + "tableFrom": "product_variant_attributes", + "tableTo": "product_variants", + "columnsFrom": [ + "variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "product_variant_attributes_attribute_value_id_brand_attribute_values_id_fk": { + "name": "product_variant_attributes_attribute_value_id_brand_attribute_values_id_fk", + "tableFrom": "product_variant_attributes", + "tableTo": "brand_attribute_values", + "columnsFrom": [ + "attribute_value_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "product_variant_attributes_pkey": { + "name": "product_variant_attributes_pkey", + "columns": [ + "variant_id", + "attribute_value_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": { + "product_variant_attributes_select_for_brand_members": { + "name": "product_variant_attributes_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "product_variant_attributes_insert_by_brand_member": { + "name": "product_variant_attributes_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "product_variant_attributes_update_by_brand_member": { + "name": "product_variant_attributes_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "product_variant_attributes_delete_by_brand_member": { + "name": "product_variant_attributes_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_weight": { + "name": "product_weight", + "schema": "", + "columns": { + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "weight": { + "name": "weight", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "weight_unit": { + "name": "weight_unit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "product_weight_product_id_products_id_fk": { + "name": "product_weight_product_id_products_id_fk", + "tableFrom": "product_weight", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "product_weight_select_for_brand_members": { + "name": "product_weight_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_weight_insert_by_brand_member": { + "name": "product_weight_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_weight_update_by_brand_member": { + "name": "product_weight_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_weight_delete_by_brand_member": { + "name": "product_weight_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_commercial": { + "name": "product_commercial", + "schema": "", + "columns": { + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "webshop_url": { + "name": "webshop_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sales_status": { + "name": "sales_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "product_commercial_product_id_products_id_fk": { + "name": "product_commercial_product_id_products_id_fk", + "tableFrom": "product_commercial", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "product_commercial_select_for_brand_members": { + "name": "product_commercial_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_commercial_insert_by_brand_member": { + "name": "product_commercial_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_commercial_update_by_brand_member": { + "name": "product_commercial_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + }, + "product_commercial_delete_by_brand_member": { + "name": "product_commercial_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM products \n WHERE products.id = product_id \n AND is_brand_member(products.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.variant_commercial": { + "name": "variant_commercial", + "schema": "", + "columns": { + "variant_id": { + "name": "variant_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "webshop_url": { + "name": "webshop_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sales_status": { + "name": "sales_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_integration": { + "name": "source_integration", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_external_id": { + "name": "source_external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "variant_commercial_variant_id_product_variants_id_fk": { + "name": "variant_commercial_variant_id_product_variants_id_fk", + "tableFrom": "variant_commercial", + "tableTo": "product_variants", + "columnsFrom": [ + "variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "variant_commercial_select_for_brand_members": { + "name": "variant_commercial_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_commercial_insert_by_brand_member": { + "name": "variant_commercial_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_commercial_update_by_brand_member": { + "name": "variant_commercial_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_commercial_delete_by_brand_member": { + "name": "variant_commercial_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.variant_environment": { + "name": "variant_environment", + "schema": "", + "columns": { + "variant_id": { + "name": "variant_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "carbon_kg_co2e": { + "name": "carbon_kg_co2e", + "type": "numeric(12, 4)", + "primaryKey": false, + "notNull": false + }, + "water_liters": { + "name": "water_liters", + "type": "numeric(12, 2)", + "primaryKey": false, + "notNull": false + }, + "source_integration": { + "name": "source_integration", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_external_id": { + "name": "source_external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "variant_environment_variant_id_product_variants_id_fk": { + "name": "variant_environment_variant_id_product_variants_id_fk", + "tableFrom": "variant_environment", + "tableTo": "product_variants", + "columnsFrom": [ + "variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "variant_environment_select_for_brand_members": { + "name": "variant_environment_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_environment_insert_by_brand_member": { + "name": "variant_environment_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_environment_update_by_brand_member": { + "name": "variant_environment_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_environment_delete_by_brand_member": { + "name": "variant_environment_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.variant_materials": { + "name": "variant_materials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "variant_id": { + "name": "variant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "brand_material_id": { + "name": "brand_material_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "percentage": { + "name": "percentage", + "type": "numeric(6, 2)", + "primaryKey": false, + "notNull": false + }, + "source_integration": { + "name": "source_integration", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_external_id": { + "name": "source_external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "variant_materials_variant_material_unq": { + "name": "variant_materials_variant_material_unq", + "columns": [ + { + "expression": "variant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "brand_material_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_variant_materials_variant_id": { + "name": "idx_variant_materials_variant_id", + "columns": [ + { + "expression": "variant_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "variant_materials_variant_id_product_variants_id_fk": { + "name": "variant_materials_variant_id_product_variants_id_fk", + "tableFrom": "variant_materials", + "tableTo": "product_variants", + "columnsFrom": [ + "variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "variant_materials_brand_material_id_brand_materials_id_fk": { + "name": "variant_materials_brand_material_id_brand_materials_id_fk", + "tableFrom": "variant_materials", + "tableTo": "brand_materials", + "columnsFrom": [ + "brand_material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "variant_materials_select_for_brand_members": { + "name": "variant_materials_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_materials_insert_by_brand_member": { + "name": "variant_materials_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_materials_update_by_brand_member": { + "name": "variant_materials_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )", + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_materials_delete_by_brand_member": { + "name": "variant_materials_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.variant_weight": { + "name": "variant_weight", + "schema": "", + "columns": { + "variant_id": { + "name": "variant_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "weight": { + "name": "weight", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "weight_unit": { + "name": "weight_unit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_integration": { + "name": "source_integration", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_external_id": { + "name": "source_external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "variant_weight_variant_id_product_variants_id_fk": { + "name": "variant_weight_variant_id_product_variants_id_fk", + "tableFrom": "variant_weight", + "tableTo": "product_variants", + "columnsFrom": [ + "variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "variant_weight_select_for_brand_members": { + "name": "variant_weight_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_weight_insert_by_brand_member": { + "name": "variant_weight_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_weight_update_by_brand_member": { + "name": "variant_weight_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_weight_delete_by_brand_member": { + "name": "variant_weight_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.variant_journey_steps": { + "name": "variant_journey_steps", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "variant_id": { + "name": "variant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sort_index": { + "name": "sort_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "step_type": { + "name": "step_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "operator_id": { + "name": "operator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_integration": { + "name": "source_integration", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_external_id": { + "name": "source_external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "variant_journey_steps_variant_sort_operator_unq": { + "name": "variant_journey_steps_variant_sort_operator_unq", + "columns": [ + { + "expression": "variant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_index", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "operator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_variant_journey_steps_variant_id": { + "name": "idx_variant_journey_steps_variant_id", + "columns": [ + { + "expression": "variant_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_variant_journey_steps_variant_sort": { + "name": "idx_variant_journey_steps_variant_sort", + "columns": [ + { + "expression": "variant_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "sort_index", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "int4_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_variant_journey_steps_operator_id": { + "name": "idx_variant_journey_steps_operator_id", + "columns": [ + { + "expression": "operator_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "variant_journey_steps_variant_id_product_variants_id_fk": { + "name": "variant_journey_steps_variant_id_product_variants_id_fk", + "tableFrom": "variant_journey_steps", + "tableTo": "product_variants", + "columnsFrom": [ + "variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "variant_journey_steps_operator_id_brand_operators_id_fk": { + "name": "variant_journey_steps_operator_id_brand_operators_id_fk", + "tableFrom": "variant_journey_steps", + "tableTo": "brand_operators", + "columnsFrom": [ + "operator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "variant_journey_steps_select_for_brand_members": { + "name": "variant_journey_steps_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_journey_steps_insert_by_brand_member": { + "name": "variant_journey_steps_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_journey_steps_update_by_brand_member": { + "name": "variant_journey_steps_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )", + "withCheck": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + }, + "variant_journey_steps_delete_by_brand_member": { + "name": "variant_journey_steps_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = variant_id\n AND is_brand_member(p.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_passports": { + "name": "product_passports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "upid": { + "name": "upid", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "working_variant_id": { + "name": "working_variant_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "current_version_id": { + "name": "current_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "dirty": { + "name": "dirty", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "orphaned_at": { + "name": "orphaned_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "sku": { + "name": "sku", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "barcode": { + "name": "barcode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "first_published_at": { + "name": "first_published_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_product_passports_brand_id": { + "name": "idx_product_passports_brand_id", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_passports_brand_dirty": { + "name": "idx_product_passports_brand_dirty", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "dirty", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "bool_ops" + } + ], + "isUnique": false, + "where": "dirty = true", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_passports_working_variant_id": { + "name": "idx_product_passports_working_variant_id", + "columns": [ + { + "expression": "working_variant_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "where": "working_variant_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_passports_status": { + "name": "idx_product_passports_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_passports_orphaned_at": { + "name": "idx_product_passports_orphaned_at", + "columns": [ + { + "expression": "orphaned_at", + "isExpression": false, + "asc": false, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "where": "status = 'orphaned'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_passports_brand_id_brands_id_fk": { + "name": "product_passports_brand_id_brands_id_fk", + "tableFrom": "product_passports", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "product_passports_working_variant_id_product_variants_id_fk": { + "name": "product_passports_working_variant_id_product_variants_id_fk", + "tableFrom": "product_passports", + "tableTo": "product_variants", + "columnsFrom": [ + "working_variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "product_passports_upid_unique": { + "name": "product_passports_upid_unique", + "nullsNotDistinct": false, + "columns": [ + "upid" + ] + } + }, + "policies": { + "product_passports_select_for_brand_members": { + "name": "product_passports_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "product_passports_insert_by_brand_members": { + "name": "product_passports_insert_by_brand_members", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "product_passports_update_by_brand_members": { + "name": "product_passports_update_by_brand_members", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "product_passports_delete_by_brand_members": { + "name": "product_passports_delete_by_brand_members", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "product_passports_select_public": { + "name": "product_passports_select_public", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "anon" + ], + "using": "\n current_version_id IS NOT NULL\n AND (\n working_variant_id IS NULL\n OR\n EXISTS (\n SELECT 1 FROM product_variants pv\n JOIN products p ON p.id = pv.product_id\n WHERE pv.id = working_variant_id\n AND p.status = 'published'\n )\n )\n " + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_passport_versions": { + "name": "product_passport_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "passport_id": { + "name": "passport_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "version_number": { + "name": "version_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "data_snapshot": { + "name": "data_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "compressed_snapshot": { + "name": "compressed_snapshot", + "type": "bytea", + "primaryKey": false, + "notNull": false + }, + "compressed_at": { + "name": "compressed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "schema_version": { + "name": "schema_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "published_at": { + "name": "published_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_product_passport_versions_passport_version": { + "name": "idx_product_passport_versions_passport_version", + "columns": [ + { + "expression": "passport_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_passport_versions_passport_id": { + "name": "idx_product_passport_versions_passport_id", + "columns": [ + { + "expression": "passport_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_product_passport_versions_passport_published": { + "name": "idx_product_passport_versions_passport_published", + "columns": [ + { + "expression": "passport_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "published_at", + "isExpression": false, + "asc": false, + "nulls": "last", + "opclass": "timestamptz_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_passport_versions_passport_id_product_passports_id_fk": { + "name": "product_passport_versions_passport_id_product_passports_id_fk", + "tableFrom": "product_passport_versions", + "tableTo": "product_passports", + "columnsFrom": [ + "passport_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "product_passport_versions_select_for_brand_members": { + "name": "product_passport_versions_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM product_passports\n WHERE product_passports.id = passport_id\n AND is_brand_member(product_passports.brand_id)\n )" + }, + "product_passport_versions_insert_by_brand_members": { + "name": "product_passport_versions_insert_by_brand_members", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM product_passports\n WHERE product_passports.id = passport_id\n AND is_brand_member(product_passports.brand_id)\n )" + }, + "product_passport_versions_select_public": { + "name": "product_passport_versions_select_public", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "anon" + ], + "using": "EXISTS (\n SELECT 1 FROM product_passports\n WHERE product_passports.id = passport_id\n AND product_passports.current_version_id IS NOT NULL\n )" + } + }, + "checkConstraints": { + "product_passport_versions_snapshot_presence_check": { + "name": "product_passport_versions_snapshot_presence_check", + "value": "num_nonnulls(\"product_passport_versions\".\"data_snapshot\", \"product_passport_versions\".\"compressed_snapshot\") = 1" + } + }, + "isRLSEnabled": false + }, + "public.file_assets": { + "name": "file_assets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bytes": { + "name": "bytes", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "file_assets_bucket_path_unq": { + "name": "file_assets_bucket_path_unq", + "columns": [ + { + "expression": "bucket", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "file_assets_brand_id_brands_id_fk": { + "name": "file_assets_brand_id_brands_id_fk", + "tableFrom": "file_assets", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "file_assets_select_for_brand_members": { + "name": "file_assets_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "(brand_id IS NULL) OR is_brand_member(brand_id)" + }, + "file_assets_insert_by_brand_member_or_system": { + "name": "file_assets_insert_by_brand_member_or_system", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "(brand_id IS NULL) OR is_brand_member(brand_id)" + }, + "file_assets_update_by_brand_member_or_system": { + "name": "file_assets_update_by_brand_member_or_system", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "(brand_id IS NULL) OR is_brand_member(brand_id)" + }, + "file_assets_delete_by_brand_member_or_system": { + "name": "file_assets_delete_by_brand_member_or_system", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "(brand_id IS NULL) OR is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.import_jobs": { + "name": "import_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "filename": { + "name": "filename", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'CREATE'" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "commit_started_at": { + "name": "commit_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'PENDING'" + }, + "requires_value_approval": { + "name": "requires_value_approval", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "has_exportable_failures": { + "name": "has_exportable_failures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "summary": { + "name": "summary", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "correction_file_path": { + "name": "correction_file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "correction_download_url": { + "name": "correction_download_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "correction_expires_at": { + "name": "correction_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "user_email": { + "name": "user_email", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "import_jobs_brand_id_brands_id_fk": { + "name": "import_jobs_brand_id_brands_id_fk", + "tableFrom": "import_jobs", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "import_jobs_select_for_brand_members": { + "name": "import_jobs_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "import_jobs_insert_by_brand_member": { + "name": "import_jobs_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "import_jobs_update_by_brand_member": { + "name": "import_jobs_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "import_jobs_delete_by_brand_member": { + "name": "import_jobs_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.import_rows": { + "name": "import_rows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "job_id": { + "name": "job_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "row_number": { + "name": "row_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "raw": { + "name": "raw", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "normalized": { + "name": "normalized", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'PENDING'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "import_rows_job_row_unq": { + "name": "import_rows_job_row_unq", + "columns": [ + { + "expression": "job_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "row_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "import_rows_job_id_import_jobs_id_fk": { + "name": "import_rows_job_id_import_jobs_id_fk", + "tableFrom": "import_rows", + "tableTo": "import_jobs", + "columnsFrom": [ + "job_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "import_rows_select_for_brand_members": { + "name": "import_rows_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM import_jobs \n WHERE import_jobs.id = job_id \n AND is_brand_member(import_jobs.brand_id)\n )" + }, + "import_rows_insert_by_brand_member": { + "name": "import_rows_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM import_jobs \n WHERE import_jobs.id = job_id \n AND is_brand_member(import_jobs.brand_id)\n )" + }, + "import_rows_update_by_brand_member": { + "name": "import_rows_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM import_jobs \n WHERE import_jobs.id = job_id \n AND is_brand_member(import_jobs.brand_id)\n )" + }, + "import_rows_delete_by_brand_member": { + "name": "import_rows_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM import_jobs \n WHERE import_jobs.id = job_id \n AND is_brand_member(import_jobs.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.export_jobs": { + "name": "export_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_email": { + "name": "user_email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'PENDING'" + }, + "selection_mode": { + "name": "selection_mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "include_ids": { + "name": "include_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "exclude_ids": { + "name": "exclude_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "filter_state": { + "name": "filter_state", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "search_query": { + "name": "search_query", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "total_products": { + "name": "total_products", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "products_processed": { + "name": "products_processed", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "download_url": { + "name": "download_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "export_jobs_brand_id_brands_id_fk": { + "name": "export_jobs_brand_id_brands_id_fk", + "tableFrom": "export_jobs", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "export_jobs_user_id_users_id_fk": { + "name": "export_jobs_user_id_users_id_fk", + "tableFrom": "export_jobs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "export_jobs_select_for_brand_members": { + "name": "export_jobs_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "export_jobs_insert_by_brand_member": { + "name": "export_jobs_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "export_jobs_update_by_brand_member": { + "name": "export_jobs_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "export_jobs_delete_by_brand_member": { + "name": "export_jobs_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.qr_export_jobs": { + "name": "qr_export_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_email": { + "name": "user_email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'PENDING'" + }, + "selection_mode": { + "name": "selection_mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "include_ids": { + "name": "include_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "exclude_ids": { + "name": "exclude_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "filter_state": { + "name": "filter_state", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "search_query": { + "name": "search_query", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "custom_domain": { + "name": "custom_domain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "total_products": { + "name": "total_products", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "total_variants": { + "name": "total_variants", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "eligible_variants": { + "name": "eligible_variants", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "variants_processed": { + "name": "variants_processed", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "download_url": { + "name": "download_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "qr_export_jobs_brand_id_brands_id_fk": { + "name": "qr_export_jobs_brand_id_brands_id_fk", + "tableFrom": "qr_export_jobs", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "qr_export_jobs_user_id_users_id_fk": { + "name": "qr_export_jobs_user_id_users_id_fk", + "tableFrom": "qr_export_jobs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "qr_export_jobs_select_for_brand_members": { + "name": "qr_export_jobs_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "qr_export_jobs_insert_by_brand_member": { + "name": "qr_export_jobs_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "qr_export_jobs_update_by_brand_member": { + "name": "qr_export_jobs_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)", + "withCheck": "is_brand_member(brand_id)" + }, + "qr_export_jobs_delete_by_brand_member": { + "name": "qr_export_jobs_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_notifications": { + "name": "user_notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resource_type": { + "name": "resource_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resource_id": { + "name": "resource_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "action_url": { + "name": "action_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action_data": { + "name": "action_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "seen_at": { + "name": "seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "dismissed_at": { + "name": "dismissed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_user_notifications_user_unread": { + "name": "idx_user_notifications_user_unread", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "(seen_at IS NULL AND dismissed_at IS NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_user_notifications_expires": { + "name": "idx_user_notifications_expires", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "(expires_at IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_user_notifications_resource": { + "name": "idx_user_notifications_resource", + "columns": [ + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "(resource_type IS NOT NULL AND resource_id IS NOT NULL)", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_notifications_user_id_users_id_fk": { + "name": "user_notifications_user_id_users_id_fk", + "tableFrom": "user_notifications", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "user_notifications_brand_id_brands_id_fk": { + "name": "user_notifications_brand_id_brands_id_fk", + "tableFrom": "user_notifications", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "user_notifications_select_own": { + "name": "user_notifications_select_own", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "auth.uid() = user_id" + }, + "user_notifications_insert_service": { + "name": "user_notifications_insert_service", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "user_notifications_update_own": { + "name": "user_notifications_update_own", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "auth.uid() = user_id" + }, + "user_notifications_delete_own": { + "name": "user_notifications_delete_own", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "auth.uid() = user_id" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.value_mappings": { + "name": "value_mappings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_column": { + "name": "source_column", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "raw_value": { + "name": "raw_value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target": { + "name": "target", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "value_mappings_brand_col_raw_unq": { + "name": "value_mappings_brand_col_raw_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + }, + { + "expression": "source_column", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + }, + { + "expression": "raw_value", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "uuid_ops" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "value_mappings_brand_id_brands_id_fk": { + "name": "value_mappings_brand_id_brands_id_fk", + "tableFrom": "value_mappings", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "value_mappings_delete_by_brand_member": { + "name": "value_mappings_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "value_mappings_insert_by_brand_member": { + "name": "value_mappings_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "value_mappings_select_for_brand_members": { + "name": "value_mappings_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "value_mappings_update_by_brand_member": { + "name": "value_mappings_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.brand_integrations": { + "name": "brand_integrations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "credentials": { + "name": "credentials", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credentials_iv": { + "name": "credentials_iv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shop_domain": { + "name": "shop_domain", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sync_interval": { + "name": "sync_interval", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 86400 + }, + "last_sync_at": { + "name": "last_sync_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_primary": { + "name": "is_primary", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "match_identifier": { + "name": "match_identifier", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'barcode'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "brand_integrations_brand_integration_unq": { + "name": "brand_integrations_brand_integration_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_integrations_brand_id": { + "name": "idx_brand_integrations_brand_id", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_brand_integrations_status": { + "name": "idx_brand_integrations_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "brand_integrations_primary_unq": { + "name": "brand_integrations_primary_unq", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"brand_integrations\".\"is_primary\" = TRUE", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "brand_integrations_brand_id_brands_id_fk": { + "name": "brand_integrations_brand_id_brands_id_fk", + "tableFrom": "brand_integrations", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "brand_integrations_integration_id_integrations_id_fk": { + "name": "brand_integrations_integration_id_integrations_id_fk", + "tableFrom": "brand_integrations", + "tableTo": "integrations", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "brand_integrations_select_for_brand_members": { + "name": "brand_integrations_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_integrations_insert_by_brand_member": { + "name": "brand_integrations_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "brand_integrations_update_by_brand_member": { + "name": "brand_integrations_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "brand_integrations_delete_by_brand_member": { + "name": "brand_integrations_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_field_configs": { + "name": "integration_field_configs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "field_key": { + "name": "field_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ownership_enabled": { + "name": "ownership_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "source_option_key": { + "name": "source_option_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_field_configs_integration_field_unq": { + "name": "integration_field_configs_integration_field_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "field_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_field_configs_integration": { + "name": "idx_integration_field_configs_integration", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_field_configs_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_field_configs_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_field_configs", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_field_configs_select_for_brand_members": { + "name": "integration_field_configs_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_field_configs_insert_by_brand_member": { + "name": "integration_field_configs_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_field_configs_update_by_brand_member": { + "name": "integration_field_configs_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_field_configs_delete_by_brand_member": { + "name": "integration_field_configs_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integrations": { + "name": "integrations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "auth_type": { + "name": "auth_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integrations_slug_unq": { + "name": "integrations_slug_unq", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integrations_select_for_authenticated": { + "name": "integrations_select_for_authenticated", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "true" + }, + "integrations_insert_by_service_role": { + "name": "integrations_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "integrations_update_by_service_role": { + "name": "integrations_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true" + }, + "integrations_delete_by_service_role": { + "name": "integrations_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_states": { + "name": "oauth_states", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "integration_slug": { + "name": "integration_slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "shop_domain": { + "name": "shop_domain", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_oauth_states_state": { + "name": "idx_oauth_states_state", + "columns": [ + { + "expression": "state", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_oauth_states_expires": { + "name": "idx_oauth_states_expires", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_states_brand_id_brands_id_fk": { + "name": "oauth_states_brand_id_brands_id_fk", + "tableFrom": "oauth_states", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "oauth_states_select_by_service_role": { + "name": "oauth_states_select_by_service_role", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "service_role" + ], + "using": "true" + }, + "oauth_states_insert_by_service_role": { + "name": "oauth_states_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "oauth_states_update_by_service_role": { + "name": "oauth_states_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true" + }, + "oauth_states_delete_by_service_role": { + "name": "oauth_states_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pending_installations": { + "name": "pending_installations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "shop_domain": { + "name": "shop_domain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credentials": { + "name": "credentials", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credentials_iv": { + "name": "credentials_iv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "pending_installations_shop_domain_unq": { + "name": "pending_installations_shop_domain_unq", + "columns": [ + { + "expression": "shop_domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_pending_installations_expires": { + "name": "idx_pending_installations_expires", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "pending_installations_select_by_service_role": { + "name": "pending_installations_select_by_service_role", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "service_role" + ], + "using": "true" + }, + "pending_installations_insert_by_service_role": { + "name": "pending_installations_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "pending_installations_update_by_service_role": { + "name": "pending_installations_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true" + }, + "pending_installations_delete_by_service_role": { + "name": "pending_installations_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_sync_jobs": { + "name": "integration_sync_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "trigger_type": { + "name": "trigger_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'scheduled'" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "variants_processed": { + "name": "variants_processed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "variants_created": { + "name": "variants_created", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "variants_updated": { + "name": "variants_updated", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "variants_failed": { + "name": "variants_failed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "variants_skipped": { + "name": "variants_skipped", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "products_total": { + "name": "products_total", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "products_processed": { + "name": "products_processed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "products_created": { + "name": "products_created", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "products_updated": { + "name": "products_updated", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "products_failed": { + "name": "products_failed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "products_skipped": { + "name": "products_skipped", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "entities_created": { + "name": "entities_created", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "error_summary": { + "name": "error_summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_log": { + "name": "error_log", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_integration_sync_jobs_integration": { + "name": "idx_integration_sync_jobs_integration", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_sync_jobs_status": { + "name": "idx_integration_sync_jobs_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_sync_jobs_created": { + "name": "idx_integration_sync_jobs_created", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_sync_jobs_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_sync_jobs_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_sync_jobs", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_sync_jobs_select_for_brand_members": { + "name": "integration_sync_jobs_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_sync_jobs_insert_by_brand_member": { + "name": "integration_sync_jobs_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_sync_jobs_update_by_brand_member": { + "name": "integration_sync_jobs_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_sync_jobs_delete_by_brand_member": { + "name": "integration_sync_jobs_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.promotion_operations": { + "name": "promotion_operations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_id": { + "name": "brand_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "new_primary_integration_id": { + "name": "new_primary_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "old_primary_integration_id": { + "name": "old_primary_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'preparing'" + }, + "phase": { + "name": "phase", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "variants_processed": { + "name": "variants_processed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_variants": { + "name": "total_variants", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "products_created": { + "name": "products_created", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "products_archived": { + "name": "products_archived", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "variants_moved": { + "name": "variants_moved", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "variants_orphaned": { + "name": "variants_orphaned", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "attributes_created": { + "name": "attributes_created", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_promotion_operations_brand_id": { + "name": "idx_promotion_operations_brand_id", + "columns": [ + { + "expression": "brand_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_promotion_operations_status": { + "name": "idx_promotion_operations_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "promotion_operations_brand_id_brands_id_fk": { + "name": "promotion_operations_brand_id_brands_id_fk", + "tableFrom": "promotion_operations", + "tableTo": "brands", + "columnsFrom": [ + "brand_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "promotion_operations_new_primary_integration_id_brand_integrations_id_fk": { + "name": "promotion_operations_new_primary_integration_id_brand_integrations_id_fk", + "tableFrom": "promotion_operations", + "tableTo": "brand_integrations", + "columnsFrom": [ + "new_primary_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "promotion_operations_old_primary_integration_id_brand_integrations_id_fk": { + "name": "promotion_operations_old_primary_integration_id_brand_integrations_id_fk", + "tableFrom": "promotion_operations", + "tableTo": "brand_integrations", + "columnsFrom": [ + "old_primary_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "promotion_operations_select_for_brand_members": { + "name": "promotion_operations_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "promotion_operations_insert_by_brand_member": { + "name": "promotion_operations_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "is_brand_member(brand_id)" + }, + "promotion_operations_update_by_brand_member": { + "name": "promotion_operations_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + }, + "promotion_operations_delete_by_brand_member": { + "name": "promotion_operations_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "is_brand_member(brand_id)" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_certification_links": { + "name": "integration_certification_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "certification_id": { + "name": "certification_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_name": { + "name": "external_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_certification_links_integration_external_unq": { + "name": "integration_certification_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_certification_links_integration_cert_unq": { + "name": "integration_certification_links_integration_cert_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "certification_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_certification_links_cert": { + "name": "idx_integration_certification_links_cert", + "columns": [ + { + "expression": "certification_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_certification_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_certification_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_certification_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_certification_links_certification_id_brand_certifications_id_fk": { + "name": "integration_certification_links_certification_id_brand_certifications_id_fk", + "tableFrom": "integration_certification_links", + "tableTo": "brand_certifications", + "columnsFrom": [ + "certification_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_certification_links_select_for_brand_members": { + "name": "integration_certification_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_certification_links_insert_by_brand_member": { + "name": "integration_certification_links_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_certification_links_update_by_brand_member": { + "name": "integration_certification_links_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_certification_links_delete_by_brand_member": { + "name": "integration_certification_links_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_manufacturer_links": { + "name": "integration_manufacturer_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "manufacturer_id": { + "name": "manufacturer_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_name": { + "name": "external_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_manufacturer_links_integration_external_unq": { + "name": "integration_manufacturer_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_manufacturer_links_integration_mfr_unq": { + "name": "integration_manufacturer_links_integration_mfr_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "manufacturer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_manufacturer_links_mfr": { + "name": "idx_integration_manufacturer_links_mfr", + "columns": [ + { + "expression": "manufacturer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_manufacturer_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_manufacturer_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_manufacturer_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_manufacturer_links_manufacturer_id_brand_manufacturers_id_fk": { + "name": "integration_manufacturer_links_manufacturer_id_brand_manufacturers_id_fk", + "tableFrom": "integration_manufacturer_links", + "tableTo": "brand_manufacturers", + "columnsFrom": [ + "manufacturer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_manufacturer_links_select_for_brand_members": { + "name": "integration_manufacturer_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_manufacturer_links_insert_by_brand_member": { + "name": "integration_manufacturer_links_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_manufacturer_links_update_by_brand_member": { + "name": "integration_manufacturer_links_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_manufacturer_links_delete_by_brand_member": { + "name": "integration_manufacturer_links_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_material_links": { + "name": "integration_material_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "material_id": { + "name": "material_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_name": { + "name": "external_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_material_links_integration_external_unq": { + "name": "integration_material_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_material_links_integration_material_unq": { + "name": "integration_material_links_integration_material_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "material_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_material_links_material": { + "name": "idx_integration_material_links_material", + "columns": [ + { + "expression": "material_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_material_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_material_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_material_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_material_links_material_id_brand_materials_id_fk": { + "name": "integration_material_links_material_id_brand_materials_id_fk", + "tableFrom": "integration_material_links", + "tableTo": "brand_materials", + "columnsFrom": [ + "material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_material_links_select_for_brand_members": { + "name": "integration_material_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_material_links_insert_by_brand_member": { + "name": "integration_material_links_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_material_links_update_by_brand_member": { + "name": "integration_material_links_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_material_links_delete_by_brand_member": { + "name": "integration_material_links_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_operator_links": { + "name": "integration_operator_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "operator_id": { + "name": "operator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_name": { + "name": "external_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_operator_links_integration_external_unq": { + "name": "integration_operator_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_operator_links_integration_operator_unq": { + "name": "integration_operator_links_integration_operator_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "operator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_operator_links_operator": { + "name": "idx_integration_operator_links_operator", + "columns": [ + { + "expression": "operator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_operator_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_operator_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_operator_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_operator_links_operator_id_brand_operators_id_fk": { + "name": "integration_operator_links_operator_id_brand_operators_id_fk", + "tableFrom": "integration_operator_links", + "tableTo": "brand_operators", + "columnsFrom": [ + "operator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_operator_links_select_for_brand_members": { + "name": "integration_operator_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_operator_links_insert_by_brand_member": { + "name": "integration_operator_links_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_operator_links_update_by_brand_member": { + "name": "integration_operator_links_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_operator_links_delete_by_brand_member": { + "name": "integration_operator_links_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_season_links": { + "name": "integration_season_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_name": { + "name": "external_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_season_links_integration_external_unq": { + "name": "integration_season_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_season_links_integration_season_unq": { + "name": "integration_season_links_integration_season_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_season_links_season": { + "name": "idx_integration_season_links_season", + "columns": [ + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_season_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_season_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_season_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_season_links_season_id_brand_seasons_id_fk": { + "name": "integration_season_links_season_id_brand_seasons_id_fk", + "tableFrom": "integration_season_links", + "tableTo": "brand_seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_season_links_select_for_brand_members": { + "name": "integration_season_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_season_links_insert_by_brand_member": { + "name": "integration_season_links_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_season_links_update_by_brand_member": { + "name": "integration_season_links_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_season_links_delete_by_brand_member": { + "name": "integration_season_links_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_tag_links": { + "name": "integration_tag_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tag_id": { + "name": "tag_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_name": { + "name": "external_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_tag_links_integration_external_unq": { + "name": "integration_tag_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_tag_links_integration_tag_unq": { + "name": "integration_tag_links_integration_tag_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "tag_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_tag_links_tag": { + "name": "idx_integration_tag_links_tag", + "columns": [ + { + "expression": "tag_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_tag_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_tag_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_tag_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_tag_links_tag_id_brand_tags_id_fk": { + "name": "integration_tag_links_tag_id_brand_tags_id_fk", + "tableFrom": "integration_tag_links", + "tableTo": "brand_tags", + "columnsFrom": [ + "tag_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_tag_links_select_for_brand_members": { + "name": "integration_tag_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_tag_links_insert_by_brand_member": { + "name": "integration_tag_links_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_tag_links_update_by_brand_member": { + "name": "integration_tag_links_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_tag_links_delete_by_brand_member": { + "name": "integration_tag_links_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_product_links": { + "name": "integration_product_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_name": { + "name": "external_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_synced_hash": { + "name": "last_synced_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_canonical": { + "name": "is_canonical", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_product_links_integration_external_unq": { + "name": "integration_product_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_product_links_canonical_unq": { + "name": "integration_product_links_canonical_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "is_canonical = true", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_product_links_product": { + "name": "idx_integration_product_links_product", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_product_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_product_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_product_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_product_links_product_id_products_id_fk": { + "name": "integration_product_links_product_id_products_id_fk", + "tableFrom": "integration_product_links", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_product_links_select_for_brand_members": { + "name": "integration_product_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_product_links_insert_by_brand_member": { + "name": "integration_product_links_insert_by_brand_member", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "authenticated", + "service_role" + ], + "withCheck": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_product_links_update_by_brand_member": { + "name": "integration_product_links_update_by_brand_member", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_product_links_delete_by_brand_member": { + "name": "integration_product_links_delete_by_brand_member", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_variant_links": { + "name": "integration_variant_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "brand_integration_id": { + "name": "brand_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "variant_id": { + "name": "variant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_product_id": { + "name": "external_product_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "external_sku": { + "name": "external_sku", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "external_barcode": { + "name": "external_barcode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_synced_hash": { + "name": "last_synced_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_variant_links_integration_external_unq": { + "name": "integration_variant_links_integration_external_unq", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_variant_links_variant": { + "name": "idx_integration_variant_links_variant", + "columns": [ + { + "expression": "variant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_integration_variant_links_integration": { + "name": "idx_integration_variant_links_integration", + "columns": [ + { + "expression": "brand_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_variant_links_brand_integration_id_brand_integrations_id_fk": { + "name": "integration_variant_links_brand_integration_id_brand_integrations_id_fk", + "tableFrom": "integration_variant_links", + "tableTo": "brand_integrations", + "columnsFrom": [ + "brand_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "integration_variant_links_variant_id_product_variants_id_fk": { + "name": "integration_variant_links_variant_id_product_variants_id_fk", + "tableFrom": "integration_variant_links", + "tableTo": "product_variants", + "columnsFrom": [ + "variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "integration_variant_links_select_for_brand_members": { + "name": "integration_variant_links_select_for_brand_members", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "EXISTS (\n SELECT 1 FROM brand_integrations bi\n WHERE bi.id = brand_integration_id\n AND is_brand_member(bi.brand_id)\n )" + }, + "integration_variant_links_insert_by_service": { + "name": "integration_variant_links_insert_by_service", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "integration_variant_links_update_by_service": { + "name": "integration_variant_links_update_by_service", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true" + }, + "integration_variant_links_delete_by_service": { + "name": "integration_variant_links_delete_by_service", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.stripe_webhook_events": { + "name": "stripe_webhook_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "stripe_event_id": { + "name": "stripe_event_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "stripe_webhook_events_stripe_event_id_unq": { + "name": "stripe_webhook_events_stripe_event_id_unq", + "columns": [ + { + "expression": "stripe_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_stripe_webhook_events_processed_at": { + "name": "idx_stripe_webhook_events_processed_at", + "columns": [ + { + "expression": "processed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "stripe_webhook_events_select_by_service_role": { + "name": "stripe_webhook_events_select_by_service_role", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "service_role" + ], + "using": "true" + }, + "stripe_webhook_events_insert_by_service_role": { + "name": "stripe_webhook_events_insert_by_service_role", + "as": "PERMISSIVE", + "for": "INSERT", + "to": [ + "service_role" + ], + "withCheck": "true" + }, + "stripe_webhook_events_update_by_service_role": { + "name": "stripe_webhook_events_update_by_service_role", + "as": "PERMISSIVE", + "for": "UPDATE", + "to": [ + "service_role" + ], + "using": "true", + "withCheck": "true" + }, + "stripe_webhook_events_delete_by_service_role": { + "name": "stripe_webhook_events_delete_by_service_role", + "as": "PERMISSIVE", + "for": "DELETE", + "to": [ + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.taxonomy_categories": { + "name": "taxonomy_categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "public_id": { + "name": "public_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_id": { + "name": "parent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "categories_parent_name_unq": { + "name": "categories_parent_name_unq", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "categories_parent_id_idx": { + "name": "categories_parent_id_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "taxonomy_categories_parent_id_taxonomy_categories_id_fk": { + "name": "taxonomy_categories_parent_id_taxonomy_categories_id_fk", + "tableFrom": "taxonomy_categories", + "tableTo": "taxonomy_categories", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "taxonomy_categories_public_id_unique": { + "name": "taxonomy_categories_public_id_unique", + "nullsNotDistinct": false, + "columns": [ + "public_id" + ] + } + }, + "policies": { + "categories_select_for_authenticated": { + "name": "categories_select_for_authenticated", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "true" + }, + "categories_modify_system_only": { + "name": "categories_modify_system_only", + "as": "RESTRICTIVE", + "for": "ALL", + "to": [ + "authenticated", + "service_role" + ], + "using": "false", + "withCheck": "false" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.taxonomy_attributes": { + "name": "taxonomy_attributes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "friendly_id": { + "name": "friendly_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "public_id": { + "name": "public_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "taxonomy_attributes_friendly_id_idx": { + "name": "taxonomy_attributes_friendly_id_idx", + "columns": [ + { + "expression": "friendly_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "taxonomy_attributes_friendly_id_unique": { + "name": "taxonomy_attributes_friendly_id_unique", + "nullsNotDistinct": false, + "columns": [ + "friendly_id" + ] + }, + "taxonomy_attributes_public_id_unique": { + "name": "taxonomy_attributes_public_id_unique", + "nullsNotDistinct": false, + "columns": [ + "public_id" + ] + } + }, + "policies": { + "taxonomy_attributes_select_for_authenticated": { + "name": "taxonomy_attributes_select_for_authenticated", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.taxonomy_values": { + "name": "taxonomy_values", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "attribute_id": { + "name": "attribute_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "friendly_id": { + "name": "friendly_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "public_id": { + "name": "public_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "public_attribute_id": { + "name": "public_attribute_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "taxonomy_values_attribute_id_idx": { + "name": "taxonomy_values_attribute_id_idx", + "columns": [ + { + "expression": "attribute_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "taxonomy_values_friendly_id_idx": { + "name": "taxonomy_values_friendly_id_idx", + "columns": [ + { + "expression": "friendly_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "taxonomy_values_attribute_id_taxonomy_attributes_id_fk": { + "name": "taxonomy_values_attribute_id_taxonomy_attributes_id_fk", + "tableFrom": "taxonomy_values", + "tableTo": "taxonomy_attributes", + "columnsFrom": [ + "attribute_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "taxonomy_values_friendly_id_unique": { + "name": "taxonomy_values_friendly_id_unique", + "nullsNotDistinct": false, + "columns": [ + "friendly_id" + ] + }, + "taxonomy_values_public_id_unique": { + "name": "taxonomy_values_public_id_unique", + "nullsNotDistinct": false, + "columns": [ + "public_id" + ] + } + }, + "policies": { + "taxonomy_values_select_for_authenticated": { + "name": "taxonomy_values_select_for_authenticated", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.taxonomy_external_mappings": { + "name": "taxonomy_external_mappings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_system": { + "name": "source_system", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_taxonomy": { + "name": "source_taxonomy", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_taxonomy": { + "name": "target_taxonomy", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "taxonomy_external_mappings_slug_unq": { + "name": "taxonomy_external_mappings_slug_unq", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "taxonomy_external_mappings_source_idx": { + "name": "taxonomy_external_mappings_source_idx", + "columns": [ + { + "expression": "source_system", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_taxonomy", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": { + "taxonomy_external_mappings_select_for_authenticated": { + "name": "taxonomy_external_mappings_select_for_authenticated", + "as": "PERMISSIVE", + "for": "SELECT", + "to": [ + "authenticated", + "service_role" + ], + "using": "true" + } + }, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.user_role": { + "name": "user_role", + "schema": "public", + "values": [ + "owner", + "member" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/api/supabase/migrations/meta/_journal.json b/apps/api/supabase/migrations/meta/_journal.json index 521093cf..5f82012d 100644 --- a/apps/api/supabase/migrations/meta/_journal.json +++ b/apps/api/supabase/migrations/meta/_journal.json @@ -225,6 +225,27 @@ "when": 1772619565718, "tag": "20260304101925_sticky_lady_deathstrike", "breakpoints": true + }, + { + "idx": 32, + "version": "7", + "when": 1772791856105, + "tag": "20260306101056_bouncy_cyclops", + "breakpoints": true + }, + { + "idx": 33, + "version": "7", + "when": 1772795721386, + "tag": "20260306111521_white_iron_lad", + "breakpoints": true + }, + { + "idx": 34, + "version": "7", + "when": 1772801455621, + "tag": "20260306125055_remarkable_mattie_franklin", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/app/next.config.mjs b/apps/app/next.config.mjs index b37c9f77..e4ed5c64 100644 --- a/apps/app/next.config.mjs +++ b/apps/app/next.config.mjs @@ -25,6 +25,7 @@ const nextConfig = { serverExternalPackages: ["pino", "thread-stream"], images: { unoptimized: isLocal, + qualities: [75, 90], remotePatterns: [ { protocol: storageUrl.protocol.replace(":", ""), diff --git a/apps/dpp/next.config.mjs b/apps/dpp/next.config.mjs index f519e7c9..5054d3ef 100644 --- a/apps/dpp/next.config.mjs +++ b/apps/dpp/next.config.mjs @@ -1,7 +1,11 @@ +/** + * Next.js configuration for the DPP frontend. + */ /** @type {import('next').NextConfig} */ const nextConfig = { output: "standalone", images: { + qualities: [75, 90], remotePatterns: [ { protocol: "https", diff --git a/packages/db/__tests__/integration/queries/dpp-version-compression.test.ts b/packages/db/__tests__/integration/queries/dpp-version-compression.test.ts new file mode 100644 index 00000000..683e2f1e --- /dev/null +++ b/packages/db/__tests__/integration/queries/dpp-version-compression.test.ts @@ -0,0 +1,196 @@ +/** + * Integration Tests: DPP Version Compression + * + * Verifies the phase 4 historical version compression flow: + * - only superseded versions are compressed + * - current versions remain readable as JSONB + * - historical reads transparently decompress compressed snapshots + */ + +import "../../setup"; + +import { beforeEach, describe, expect, it } from "bun:test"; +import { desc, eq } from "drizzle-orm"; +import { getPublicDppVersion } from "@v1/db/queries/dpp"; +import { + batchCompressSupersededVersions, + decompressVersion, + publishProductsSetBased, +} from "@v1/db/queries/products"; +import * as schema from "@v1/db/schema"; +import { cleanupTables, createTestBrand, testDb } from "@v1/db/testing"; + +/** + * Build a stable random suffix for test identifiers. + */ +function randomSuffix(): string { + // Generate a short alphanumeric suffix. + return Math.random().toString(36).slice(2, 10); +} + +/** + * Create a published product with one variant for version compression tests. + */ +async function createPublishedProductWithVariant(input: { + brandId: string; + name: string; + upid?: string; +}) { + // Seed a published product so the set-based projector can materialize versions. + const productId = crypto.randomUUID(); + await testDb.insert(schema.products).values({ + id: productId, + brandId: input.brandId, + name: input.name, + productHandle: `product-${randomSuffix()}`, + status: "published", + }); + + const variantId = crypto.randomUUID(); + const upid = input.upid ?? `UPID-${randomSuffix()}`; + await testDb.insert(schema.productVariants).values({ + id: variantId, + productId, + upid, + sku: `SKU-${randomSuffix()}`, + barcode: `${Math.floor(1_000_000_000_000 + Math.random() * 9_000_000_000_000)}`, + }); + + return { productId, variantId, upid }; +} + +/** + * Load the passport row linked to a variant. + */ +async function getPassportByVariantId(variantId: string) { + // Query the passport created for the test variant. + const [passport] = await testDb + .select({ + id: schema.productPassports.id, + currentVersionId: schema.productPassports.currentVersionId, + }) + .from(schema.productPassports) + .where(eq(schema.productPassports.workingVariantId, variantId)) + .limit(1); + + return passport ?? null; +} + +/** + * Load all versions for a passport, newest first. + */ +async function getVersionsForPassport(passportId: string) { + // Query both JSON and compressed columns for assertions. + return testDb + .select({ + id: schema.productPassportVersions.id, + versionNumber: schema.productPassportVersions.versionNumber, + dataSnapshot: schema.productPassportVersions.dataSnapshot, + compressedSnapshot: schema.productPassportVersions.compressedSnapshot, + compressedAt: schema.productPassportVersions.compressedAt, + }) + .from(schema.productPassportVersions) + .where(eq(schema.productPassportVersions.passportId, passportId)) + .orderBy(desc(schema.productPassportVersions.versionNumber)); +} + +describe("DPP Version Compression", () => { + let brandId: string; + + beforeEach(async () => { + // Reset test data between cases. + await cleanupTables(); + brandId = await createTestBrand("DPP Version Compression Brand"); + }); + + it("compresses superseded versions while leaving the current version uncompressed", async () => { + // Publish twice so the first version becomes compressible history. + const seeded = await createPublishedProductWithVariant({ + brandId, + name: "Compression Product", + upid: "CMPRESS000000001", + }); + + await publishProductsSetBased(testDb, { + brandId, + productIds: [seeded.productId], + variantChunkSize: 10, + }); + + await testDb + .update(schema.products) + .set({ name: "Compression Product Updated" }) + .where(eq(schema.products.id, seeded.productId)); + + await publishProductsSetBased(testDb, { + brandId, + productIds: [seeded.productId], + variantChunkSize: 10, + }); + + const passport = await getPassportByVariantId(seeded.variantId); + expect(passport).not.toBeNull(); + + const compression = await batchCompressSupersededVersions(testDb, { + limit: 10, + }); + expect(compression.scanned).toBe(1); + expect(compression.compressed).toBe(1); + expect(compression.skipped).toBe(0); + + const versions = await getVersionsForPassport(passport!.id); + expect(versions).toHaveLength(2); + + const currentVersion = versions.find((version) => version.versionNumber === 2); + const historicalVersion = versions.find( + (version) => version.versionNumber === 1, + ); + + expect(currentVersion?.dataSnapshot).not.toBeNull(); + expect(currentVersion?.compressedSnapshot).toBeNull(); + expect(historicalVersion?.dataSnapshot).toBeNull(); + expect(historicalVersion?.compressedSnapshot).not.toBeNull(); + expect(historicalVersion?.compressedAt).toBeTruthy(); + + const decompressed = await decompressVersion(testDb, historicalVersion!.id); + expect(decompressed?.productAttributes.name).toBe("Compression Product"); + }); + + it("returns a decompressed snapshot when reading a historical public version", async () => { + // Compress an old version, then read it back through the public query path. + const seeded = await createPublishedProductWithVariant({ + brandId, + name: "Historical Public Product", + upid: "CMPRESS000000002", + }); + + await publishProductsSetBased(testDb, { + brandId, + productIds: [seeded.productId], + variantChunkSize: 10, + }); + + await testDb + .update(schema.products) + .set({ name: "Historical Public Product Updated" }) + .where(eq(schema.products.id, seeded.productId)); + + await publishProductsSetBased(testDb, { + brandId, + productIds: [seeded.productId], + variantChunkSize: 10, + }); + + const passport = await getPassportByVariantId(seeded.variantId); + expect(passport).not.toBeNull(); + + await batchCompressSupersededVersions(testDb, { limit: 10 }); + + const historical = await getPublicDppVersion(testDb, seeded.upid, 1); + expect(historical.found).toBe(true); + expect(historical.snapshot?.productAttributes.name).toBe( + "Historical Public Product", + ); + expect(historical.version?.versionNumber).toBe(1); + }); +}); diff --git a/packages/db/__tests__/integration/queries/passport-dirty.test.ts b/packages/db/__tests__/integration/queries/passport-dirty.test.ts new file mode 100644 index 00000000..0e2b1037 --- /dev/null +++ b/packages/db/__tests__/integration/queries/passport-dirty.test.ts @@ -0,0 +1,318 @@ +/** + * Integration Tests: Passport Dirty Infrastructure + * + * Verifies the phase 1 passport dirty-flag primitives: + * - passport creation starts clean with no firstPublishedAt value + * - dirty marking respects published product status + * - dirty clearing resets selected passports only + */ + +import "../../setup"; + +import { beforeEach, describe, expect, it } from "bun:test"; +import { inArray } from "drizzle-orm"; +import * as schema from "@v1/db/schema"; +import { + batchClearDirtyFlags, + batchCreatePassportsForVariants, + clearDirtyFlag, + createPassportForVariant, + createProductPassport, + markAllBrandPassportsDirty, + markPassportsDirtyByProductIds, + markPassportsDirtyByVariantIds, +} from "@v1/db/queries/products"; +import { + cleanupTables, + createTestBrand, + createTestProduct, + createTestVariant, + testDb, +} from "@v1/db/testing"; + +/** + * Create a product, variant, and passport fixture for dirty-flag tests. + */ +async function createPassportFixture( + brandId: string, + status: "published" | "unpublished", + options: { + upid: string; + sku?: string | null; + barcode?: string | null; + }, +) { + // Create a product/variant pair so the passport can link back to working data. + const product = await createTestProduct(brandId, { + status, + productHandle: `passport-dirty-${Math.random().toString(36).slice(2, 8)}`, + }); + const variant = await createTestVariant(product.id, { + upid: options.upid, + sku: options.sku ?? undefined, + barcode: options.barcode ?? null, + }); + const passport = requireValue( + await createPassportForVariant(testDb, variant.id, brandId, { + upid: variant.upid!, + sku: variant.sku, + barcode: variant.barcode, + }), + "Failed to create passport fixture", + ); + + return { product, variant, passport }; +} + +/** + * Assert that a test setup helper returned a value. + */ +function requireValue(value: T | undefined, message: string): T { + // Fail fast so setup issues are surfaced as test errors instead of type noise. + if (value === undefined) { + throw new Error(message); + } + + return value; +} + +describe("Passport dirty infrastructure", () => { + beforeEach(async () => { + // Reset the mutable tables so each test starts from a known baseline. + await cleanupTables(); + }); + + it("creates passports with null firstPublishedAt and dirty=false", async () => { + const brandId = await createTestBrand("Dirty Infra Brand"); + const product = await createTestProduct(brandId, { + status: "published", + productHandle: `passport-creation-${Math.random().toString(36).slice(2, 8)}`, + }); + + const variantA = await createTestVariant(product.id, { + upid: `DIRTYA${Math.random().toString(36).slice(2, 10)}`, + sku: "SKU-A", + barcode: "1111111111111", + }); + const variantB = await createTestVariant(product.id, { + upid: `DIRTYB${Math.random().toString(36).slice(2, 10)}`, + sku: "SKU-B", + barcode: "2222222222222", + }); + const variantC = await createTestVariant(product.id, { + upid: `DIRTYC${Math.random().toString(36).slice(2, 10)}`, + sku: "SKU-C", + barcode: "3333333333333", + }); + const variantD = await createTestVariant(product.id, { + upid: `DIRTYD${Math.random().toString(36).slice(2, 10)}`, + sku: "SKU-D", + barcode: "4444444444444", + }); + + const passportA = requireValue( + await createProductPassport(testDb, variantA.id, brandId), + "Failed to create passport A", + ); + const passportB = requireValue( + await createPassportForVariant(testDb, variantB.id, brandId, { + upid: variantB.upid!, + sku: variantB.sku, + barcode: variantB.barcode, + }), + "Failed to create passport B", + ); + const passportsCAndD = await batchCreatePassportsForVariants(testDb, brandId, [ + { + variantId: variantC.id, + upid: variantC.upid!, + sku: variantC.sku, + barcode: variantC.barcode, + }, + { + variantId: variantD.id, + upid: variantD.upid!, + sku: variantD.sku, + barcode: variantD.barcode, + }, + ]); + + const createdPassportIds = [ + passportA.id, + passportB.id, + ...passportsCAndD.map((passport) => passport.id), + ]; + const rows = await testDb + .select({ + id: schema.productPassports.id, + dirty: schema.productPassports.dirty, + firstPublishedAt: schema.productPassports.firstPublishedAt, + }) + .from(schema.productPassports) + .where(inArray(schema.productPassports.id, createdPassportIds)); + + expect(rows).toHaveLength(4); + expect(rows.every((row) => row.dirty === false)).toBe(true); + expect(rows.every((row) => row.firstPublishedAt === null)).toBe(true); + }); + + it("marks only published passports dirty when targeting variant IDs", async () => { + const brandId = await createTestBrand("Variant Dirty Brand"); + const publishedFixture = await createPassportFixture(brandId, "published", { + upid: `PUBVAR${Math.random().toString(36).slice(2, 10)}`, + }); + const unpublishedFixture = await createPassportFixture( + brandId, + "unpublished", + { + upid: `UNPUBVAR${Math.random().toString(36).slice(2, 10)}`, + }, + ); + + const result = await markPassportsDirtyByVariantIds(testDb, [ + publishedFixture.variant.id, + unpublishedFixture.variant.id, + ]); + const rows = await testDb + .select({ + id: schema.productPassports.id, + dirty: schema.productPassports.dirty, + }) + .from(schema.productPassports) + .where( + inArray(schema.productPassports.id, [ + publishedFixture.passport.id, + unpublishedFixture.passport.id, + ]), + ); + + expect(result.marked).toBe(1); + expect(result.passportIds).toEqual([publishedFixture.passport.id]); + expect(rows.find((row) => row.id === publishedFixture.passport.id)?.dirty).toBe( + true, + ); + expect( + rows.find((row) => row.id === unpublishedFixture.passport.id)?.dirty, + ).toBe(false); + }); + + it("marks product passports dirty and clears them again", async () => { + const brandId = await createTestBrand("Product Dirty Brand"); + const publishedProduct = await createTestProduct(brandId, { + status: "published", + productHandle: `dirty-product-${Math.random().toString(36).slice(2, 8)}`, + }); + const unpublishedProduct = await createTestProduct(brandId, { + status: "unpublished", + productHandle: `clean-product-${Math.random().toString(36).slice(2, 8)}`, + }); + + const publishedVariantA = await createTestVariant(publishedProduct.id, { + upid: `PRODPA${Math.random().toString(36).slice(2, 10)}`, + }); + const publishedVariantB = await createTestVariant(publishedProduct.id, { + upid: `PRODPB${Math.random().toString(36).slice(2, 10)}`, + }); + const unpublishedVariant = await createTestVariant(unpublishedProduct.id, { + upid: `PRODPC${Math.random().toString(36).slice(2, 10)}`, + }); + + const publishedPassportA = requireValue( + await createPassportForVariant(testDb, publishedVariantA.id, brandId, { + upid: publishedVariantA.upid!, + }), + "Failed to create published passport A", + ); + const publishedPassportB = requireValue( + await createPassportForVariant(testDb, publishedVariantB.id, brandId, { + upid: publishedVariantB.upid!, + }), + "Failed to create published passport B", + ); + const unpublishedPassport = requireValue( + await createPassportForVariant(testDb, unpublishedVariant.id, brandId, { + upid: unpublishedVariant.upid!, + }), + "Failed to create unpublished passport", + ); + + const marked = await markPassportsDirtyByProductIds(testDb, brandId, [ + publishedProduct.id, + unpublishedProduct.id, + ]); + + expect(marked.marked).toBe(2); + expect(marked.passportIds.sort()).toEqual( + [publishedPassportA.id, publishedPassportB.id].sort(), + ); + + const clearedSingle = await clearDirtyFlag(testDb, publishedPassportA.id); + const clearedBatch = await batchClearDirtyFlags(testDb, [ + publishedPassportB.id, + unpublishedPassport.id, + ]); + const rows = await testDb + .select({ + id: schema.productPassports.id, + dirty: schema.productPassports.dirty, + }) + .from(schema.productPassports) + .where( + inArray(schema.productPassports.id, [ + publishedPassportA.id, + publishedPassportB.id, + unpublishedPassport.id, + ]), + ); + + expect(clearedSingle?.id).toBe(publishedPassportA.id); + expect(clearedBatch.cleared).toBe(1); + expect(rows.every((row) => row.dirty === false)).toBe(true); + }); + + it("marks only the selected brand's published passports dirty", async () => { + const brandAId = await createTestBrand("Brand A Dirty"); + const brandBId = await createTestBrand("Brand B Dirty"); + + const brandAPublished = await createPassportFixture(brandAId, "published", { + upid: `BRANDA${Math.random().toString(36).slice(2, 10)}`, + }); + const brandAUnpublished = await createPassportFixture( + brandAId, + "unpublished", + { + upid: `BRANDAU${Math.random().toString(36).slice(2, 10)}`, + }, + ); + const brandBPublished = await createPassportFixture(brandBId, "published", { + upid: `BRANDB${Math.random().toString(36).slice(2, 10)}`, + }); + + const marked = await markAllBrandPassportsDirty(testDb, brandAId); + const rows = await testDb + .select({ + id: schema.productPassports.id, + dirty: schema.productPassports.dirty, + }) + .from(schema.productPassports) + .where( + inArray(schema.productPassports.id, [ + brandAPublished.passport.id, + brandAUnpublished.passport.id, + brandBPublished.passport.id, + ]), + ); + + expect(marked.marked).toBe(1); + expect(marked.passportIds).toEqual([brandAPublished.passport.id]); + expect(rows.find((row) => row.id === brandAPublished.passport.id)?.dirty).toBe( + true, + ); + expect( + rows.find((row) => row.id === brandAUnpublished.passport.id)?.dirty, + ).toBe(false); + expect(rows.find((row) => row.id === brandBPublished.passport.id)?.dirty).toBe( + false, + ); + }); +}); diff --git a/packages/db/src/queries/dpp/public.ts b/packages/db/src/queries/dpp/public.ts index 1bf58d9c..ddc4c1bd 100644 --- a/packages/db/src/queries/dpp/public.ts +++ b/packages/db/src/queries/dpp/public.ts @@ -20,8 +20,10 @@ import { brands, productPassportVersions, productPassports, + productVariants, + products, } from "../../schema"; -import type { DppSnapshot } from "../products/dpp-versions"; +import { getVersionSnapshot, type DppSnapshot } from "../products/dpp-versions"; // ============================================================================= // TYPES @@ -62,8 +64,11 @@ export interface PublicDppResult { id: string; brandId: string | null; workingVariantId: string | null; - firstPublishedAt: string; + firstPublishedAt: string | null; + dirty: boolean; } | null; + /** Working product publication state when the passport is still linked */ + productStatus: "published" | "unpublished" | "scheduled" | null; /** The current version's snapshot data */ snapshot: DppSnapshot | null; /** Version metadata */ @@ -108,7 +113,7 @@ export async function getPublicDppByUpid( db: Database, upid: string, ): Promise { - // Step 1: Fetch passport by UPID + // Step 1: Fetch passport by UPID, including the working product status for gating. const [passport] = await db .select({ id: productPassports.id, @@ -117,8 +122,15 @@ export async function getPublicDppByUpid( workingVariantId: productPassports.workingVariantId, currentVersionId: productPassports.currentVersionId, firstPublishedAt: productPassports.firstPublishedAt, + dirty: productPassports.dirty, + productStatus: products.status, }) .from(productPassports) + .leftJoin( + productVariants, + eq(productVariants.id, productPassports.workingVariantId), + ) + .leftJoin(products, eq(products.id, productVariants.productId)) .where(eq(productPassports.upid, upid)) .limit(1); @@ -128,6 +140,7 @@ export async function getPublicDppByUpid( isInactive: false, upid, passport: null, + productStatus: null, snapshot: null, version: null, theme: null, @@ -135,62 +148,37 @@ export async function getPublicDppByUpid( }; } - // Check if the passport has been published - if (!passport.currentVersionId) { - return { - found: false, - isInactive: false, - upid, - passport: { - id: passport.id, - brandId: passport.brandId, - workingVariantId: passport.workingVariantId, - firstPublishedAt: passport.firstPublishedAt, - }, - snapshot: null, - version: null, - theme: null, - error: "Passport has not been published yet", - }; - } - - // Step 2: Fetch the current version's snapshot - const [version] = await db - .select({ - id: productPassportVersions.id, - versionNumber: productPassportVersions.versionNumber, - dataSnapshot: productPassportVersions.dataSnapshot, - contentHash: productPassportVersions.contentHash, - schemaVersion: productPassportVersions.schemaVersion, - publishedAt: productPassportVersions.publishedAt, - }) - .from(productPassportVersions) - .where(eq(productPassportVersions.id, passport.currentVersionId)) - .limit(1); - - if (!version) { - return { - found: false, - isInactive: false, - upid, - passport: { - id: passport.id, - brandId: passport.brandId, - workingVariantId: passport.workingVariantId, - firstPublishedAt: passport.firstPublishedAt, - }, - snapshot: null, - version: null, - theme: null, - error: "Version not found", - }; - } + // Step 2: Fetch the current version's snapshot when one exists. + const version = passport.currentVersionId + ? ( + await db + .select({ + id: productPassportVersions.id, + versionNumber: productPassportVersions.versionNumber, + dataSnapshot: productPassportVersions.dataSnapshot, + compressedSnapshot: productPassportVersions.compressedSnapshot, + compressedAt: productPassportVersions.compressedAt, + contentHash: productPassportVersions.contentHash, + schemaVersion: productPassportVersions.schemaVersion, + publishedAt: productPassportVersions.publishedAt, + }) + .from(productPassportVersions) + .where(eq(productPassportVersions.id, passport.currentVersionId)) + .limit(1) + )[0] ?? null + : null; // Step 3: Fetch brand theme data const theme = await fetchBrandTheme(db, passport.brandId); // Check if working variant has been deleted (passport is inactive) const isInactive = passport.workingVariantId === null; + const snapshot = version + ? getVersionSnapshot({ + dataSnapshot: version.dataSnapshot as DppSnapshot | null, + compressedSnapshot: version.compressedSnapshot, + }) + : null; return { found: true, @@ -201,16 +189,23 @@ export async function getPublicDppByUpid( brandId: passport.brandId, workingVariantId: passport.workingVariantId, firstPublishedAt: passport.firstPublishedAt, + dirty: passport.dirty, }, - snapshot: version.dataSnapshot as DppSnapshot, - version: { - id: version.id, - versionNumber: version.versionNumber, - schemaVersion: version.schemaVersion, - publishedAt: version.publishedAt, - contentHash: version.contentHash, - }, + productStatus: + (passport.productStatus as "published" | "unpublished" | "scheduled" | null) ?? + null, + snapshot, + version: version + ? { + id: version.id, + versionNumber: version.versionNumber, + schemaVersion: version.schemaVersion, + publishedAt: version.publishedAt, + contentHash: version.contentHash, + } + : null, theme, + error: version ? undefined : "Passport has not been materialized yet", }; } @@ -302,6 +297,7 @@ export async function getPublicDppVersion( isInactive: false, upid, passport: null, + productStatus: null, snapshot: null, version: null, theme: null, @@ -315,6 +311,8 @@ export async function getPublicDppVersion( id: productPassportVersions.id, versionNumber: productPassportVersions.versionNumber, dataSnapshot: productPassportVersions.dataSnapshot, + compressedSnapshot: productPassportVersions.compressedSnapshot, + compressedAt: productPassportVersions.compressedAt, contentHash: productPassportVersions.contentHash, schemaVersion: productPassportVersions.schemaVersion, publishedAt: productPassportVersions.publishedAt, @@ -338,7 +336,9 @@ export async function getPublicDppVersion( brandId: passport.brandId, workingVariantId: passport.workingVariantId, firstPublishedAt: passport.firstPublishedAt, + dirty: false, }, + productStatus: null, snapshot: null, version: null, theme: null, @@ -349,6 +349,10 @@ export async function getPublicDppVersion( // Fetch brand theme data const theme = await fetchBrandTheme(db, passport.brandId); const isInactive = passport.workingVariantId === null; + const snapshot = getVersionSnapshot({ + dataSnapshot: version.dataSnapshot as DppSnapshot | null, + compressedSnapshot: version.compressedSnapshot, + }); return { found: true, @@ -359,8 +363,10 @@ export async function getPublicDppVersion( brandId: passport.brandId, workingVariantId: passport.workingVariantId, firstPublishedAt: passport.firstPublishedAt, + dirty: false, }, - snapshot: version.dataSnapshot as DppSnapshot, + productStatus: null, + snapshot, version: { id: version.id, versionNumber: version.versionNumber, @@ -387,10 +393,25 @@ export async function isPassportPublished( const [passport] = await db .select({ currentVersionId: productPassports.currentVersionId, + workingVariantId: productPassports.workingVariantId, + productStatus: products.status, }) .from(productPassports) + .leftJoin( + productVariants, + eq(productVariants.id, productPassports.workingVariantId), + ) + .leftJoin(products, eq(products.id, productVariants.productId)) .where(eq(productPassports.upid, upid)) .limit(1); - return passport?.currentVersionId != null; + if (!passport?.currentVersionId) { + return false; + } + + if (!passport.workingVariantId) { + return true; + } + + return passport.productStatus === "published"; } diff --git a/packages/db/src/queries/products/dpp-versions.ts b/packages/db/src/queries/products/dpp-versions.ts index f18eb140..222e0948 100644 --- a/packages/db/src/queries/products/dpp-versions.ts +++ b/packages/db/src/queries/products/dpp-versions.ts @@ -1,14 +1,25 @@ /** - * DPP Version operations. + * DPP version operations. * - * Manages product_passport_versions records - the immutable version history. - * Each publish action creates a new version; versions are NEVER updated or deleted. + * Manages immutable product_passport_versions records, including historical + * snapshot compression for superseded versions. */ import { createHash } from "node:crypto"; -import { desc, eq, max } from "drizzle-orm"; +import * as zlib from "node:zlib"; +import { + and, + asc, + desc, + eq, + isNotNull, + isNull, + max, + ne, + or, +} from "drizzle-orm"; import type { Database } from "../../client"; -import { productPassportVersions } from "../../schema"; +import { productPassportVersions, productPassports } from "../../schema"; // ============================================================================= // TYPES @@ -102,6 +113,43 @@ export interface DppSnapshot { }; } +/** + * Stored passport version row shape used by query helpers. + */ +export interface StoredDppVersion { + id: string; + passportId: string; + versionNumber: number; + dataSnapshot: DppSnapshot | null; + compressedSnapshot: Buffer | null; + compressedAt: string | null; + contentHash: string; + schemaVersion: string; + publishedAt: string; +} + +/** + * Options for batch historical version compression. + */ +export interface BatchCompressSupersededVersionsOptions { + limit?: number; +} + +/** + * Result summary for batch historical version compression. + */ +export interface BatchCompressSupersededVersionsResult { + scanned: number; + compressed: number; + skipped: number; + versionIds: string[]; +} + +const ZSTD_SNAPSHOT_PREFIX = Buffer.from("dpp.zstd.v1:"); +const BROTLI_SNAPSHOT_PREFIX = Buffer.from("dpp.brotli.v1:"); + +type SnapshotCompressionCodec = "zstd" | "brotli" | "legacy-zstd"; + // ============================================================================= // HELPER FUNCTIONS // ============================================================================= @@ -130,7 +178,6 @@ function sortObjectKeys(obj: unknown): unknown { /** * Calculate SHA-256 hash of the canonical JSON representation. - * Used for integrity verification. */ function calculateContentHash(data: unknown): string { // Canonicalize nested objects before hashing to keep snapshots stable. @@ -139,18 +186,149 @@ function calculateContentHash(data: unknown): string { return createHash("sha256").update(canonicalJson, "utf8").digest("hex"); } +/** + * Detect whether the active runtime supports zstd through node:zlib. + */ +function supportsZstdCompression(): boolean { + // Gate zstd usage behind runtime feature detection to keep Node 21 imports working. + return ( + typeof zlib.zstdCompressSync === "function" && + typeof zlib.zstdDecompressSync === "function" + ); +} + +/** + * Add a codec marker ahead of the compressed snapshot bytes. + */ +function encodeCompressedSnapshot( + codec: Exclude, + payload: Buffer, +): Buffer { + // Prefix the payload so mixed-codec environments can decode rows safely. + return Buffer.concat([ + codec === "zstd" ? ZSTD_SNAPSHOT_PREFIX : BROTLI_SNAPSHOT_PREFIX, + payload, + ]); +} + +/** + * Split a stored compressed snapshot into its codec marker and payload bytes. + */ +function decodeCompressedSnapshot( + compressedSnapshot: Buffer, +): { codec: SnapshotCompressionCodec; payload: Buffer } { + // Preserve support for early rows that were written as raw zstd without a prefix. + if ( + compressedSnapshot.subarray(0, ZSTD_SNAPSHOT_PREFIX.length).equals( + ZSTD_SNAPSHOT_PREFIX, + ) + ) { + return { + codec: "zstd", + payload: compressedSnapshot.subarray(ZSTD_SNAPSHOT_PREFIX.length), + }; + } + + if ( + compressedSnapshot.subarray(0, BROTLI_SNAPSHOT_PREFIX.length).equals( + BROTLI_SNAPSHOT_PREFIX, + ) + ) { + return { + codec: "brotli", + payload: compressedSnapshot.subarray(BROTLI_SNAPSHOT_PREFIX.length), + }; + } + + return { + codec: "legacy-zstd", + payload: compressedSnapshot, + }; +} + +/** + * Convert a JSON snapshot into a compressed binary form supported by the runtime. + */ +function compressSnapshotPayload(snapshot: DppSnapshot): Buffer { + // Prefer zstd where available and fall back to brotli on older Node runtimes. + const payload = Buffer.from(JSON.stringify(snapshot), "utf8"); + + if (supportsZstdCompression()) { + return encodeCompressedSnapshot( + "zstd", + Buffer.from(zlib.zstdCompressSync(payload)), + ); + } + + return encodeCompressedSnapshot( + "brotli", + Buffer.from(zlib.brotliCompressSync(payload)), + ); +} + +/** + * Inflate a historical snapshot from whichever codec it was stored with. + */ +function decompressSnapshotPayload(compressedSnapshot: Buffer): DppSnapshot { + // Read the prefixed codec so cross-runtime deployments can decode older rows. + const { codec, payload } = decodeCompressedSnapshot(compressedSnapshot); + + if (codec === "brotli") { + return JSON.parse(zlib.brotliDecompressSync(payload).toString("utf8")) as DppSnapshot; + } + + if (!supportsZstdCompression()) { + throw new Error( + "Encountered a zstd-compressed passport version on a runtime without zstd support", + ); + } + + return JSON.parse(zlib.zstdDecompressSync(payload).toString("utf8")) as DppSnapshot; +} + +/** + * Shared select shape for stored version rows. + */ +function versionSelection() { + // Reuse the same selection fields across create/read/compression helpers. + return { + id: productPassportVersions.id, + passportId: productPassportVersions.passportId, + versionNumber: productPassportVersions.versionNumber, + dataSnapshot: productPassportVersions.dataSnapshot, + compressedSnapshot: productPassportVersions.compressedSnapshot, + compressedAt: productPassportVersions.compressedAt, + contentHash: productPassportVersions.contentHash, + schemaVersion: productPassportVersions.schemaVersion, + publishedAt: productPassportVersions.publishedAt, + }; +} + +/** + * Extract a JSON snapshot from either jsonb or compressed bytea storage. + */ +export function getVersionSnapshot( + version: { + dataSnapshot: unknown | null; + compressedSnapshot: Buffer | null; + }, +): DppSnapshot | null { + // Prefer the active jsonb payload and fall back to decompression for history. + if (version.dataSnapshot !== null) { + return version.dataSnapshot as DppSnapshot; + } + if (version.compressedSnapshot !== null) { + return decompressSnapshotPayload(version.compressedSnapshot); + } + return null; +} + // ============================================================================= // CREATE OPERATIONS // ============================================================================= /** * Create a new DPP version record. - * - * @param db - Database instance - * @param passportId - The passport this version belongs to - * @param dataSnapshot - The complete JSON-LD snapshot - * @param schemaVersion - The schema version (e.g., "1.0") - * @returns The created version record */ export async function createDppVersion( db: Database, @@ -158,7 +336,7 @@ export async function createDppVersion( dataSnapshot: DppSnapshot, schemaVersion = "1.0", ) { - // Get the next version number for this passport + // Get the next version number for this passport. const [result] = await db .select({ maxVersion: max(productPassportVersions.versionNumber), @@ -168,7 +346,7 @@ export async function createDppVersion( const nextVersionNumber = (result?.maxVersion ?? 0) + 1; - // Update the snapshot metadata with the actual version number + // Update the snapshot metadata with the actual version number. const snapshotWithMetadata: DppSnapshot = { ...dataSnapshot, metadata: { @@ -179,10 +357,10 @@ export async function createDppVersion( }, }; - // Calculate content hash + // Calculate content hash. const contentHash = calculateContentHash(snapshotWithMetadata); - // Insert the version record + // Insert the version record with an uncompressed active snapshot. const [version] = await db .insert(productPassportVersions) .values({ @@ -192,17 +370,15 @@ export async function createDppVersion( contentHash, schemaVersion, }) - .returning({ - id: productPassportVersions.id, - passportId: productPassportVersions.passportId, - versionNumber: productPassportVersions.versionNumber, - dataSnapshot: productPassportVersions.dataSnapshot, - contentHash: productPassportVersions.contentHash, - schemaVersion: productPassportVersions.schemaVersion, - publishedAt: productPassportVersions.publishedAt, - }); + .onConflictDoNothing({ + target: [ + productPassportVersions.passportId, + productPassportVersions.versionNumber, + ], + }) + .returning(versionSelection()); - return version; + return (version as StoredDppVersion | undefined) ?? null; } // ============================================================================= @@ -211,26 +387,130 @@ export async function createDppVersion( /** * Get the latest (current) version for a passport. - * - * @param db - Database instance - * @param passportId - The passport ID - * @returns The latest version, or null if none exist */ -export async function getLatestVersion(db: Database, passportId: string) { +export async function getLatestVersion( + db: Database, + passportId: string, +): Promise { + // Fetch the newest immutable version row for the passport. const [version] = await db - .select({ - id: productPassportVersions.id, - passportId: productPassportVersions.passportId, - versionNumber: productPassportVersions.versionNumber, - dataSnapshot: productPassportVersions.dataSnapshot, - contentHash: productPassportVersions.contentHash, - schemaVersion: productPassportVersions.schemaVersion, - publishedAt: productPassportVersions.publishedAt, - }) + .select(versionSelection()) .from(productPassportVersions) .where(eq(productPassportVersions.passportId, passportId)) .orderBy(desc(productPassportVersions.versionNumber)) .limit(1); - return version ?? null; + return (version as StoredDppVersion | undefined) ?? null; +} + +/** + * Compress a superseded version's JSON snapshot into bytea storage. + */ +export async function compressVersion( + db: Database, + versionId: string, +): Promise { + // Load the target version so we can skip already-compressed rows. + const [version] = await db + .select(versionSelection()) + .from(productPassportVersions) + .where(eq(productPassportVersions.id, versionId)) + .limit(1); + + if (!version) { + return null; + } + if (version.compressedSnapshot !== null || version.dataSnapshot === null) { + return version as StoredDppVersion; + } + + const now = new Date().toISOString(); + const [updated] = await db + .update(productPassportVersions) + .set({ + compressedSnapshot: compressSnapshotPayload( + version.dataSnapshot as DppSnapshot, + ), + compressedAt: now, + dataSnapshot: null, + }) + .where( + and( + eq(productPassportVersions.id, versionId), + isNotNull(productPassportVersions.dataSnapshot), + isNull(productPassportVersions.compressedSnapshot), + ), + ) + .returning(versionSelection()); + + return (updated as StoredDppVersion | undefined) ?? (version as StoredDppVersion); +} + +/** + * Decompress and return a version's JSON snapshot. + */ +export async function decompressVersion( + db: Database, + versionId: string, +): Promise { + // Fetch the stored version payload and normalize it to JSON. + const [version] = await db + .select(versionSelection()) + .from(productPassportVersions) + .where(eq(productPassportVersions.id, versionId)) + .limit(1); + + if (!version) { + return null; + } + + return getVersionSnapshot(version as StoredDppVersion); +} + +/** + * Compress superseded historical versions in small batches. + */ +export async function batchCompressSupersededVersions( + db: Database, + options: BatchCompressSupersededVersionsOptions = {}, +): Promise { + // Bound the batch size so the job can iterate safely over large histories. + const limit = Math.max(1, options.limit ?? 500); + + const versions = (await db + .select(versionSelection()) + .from(productPassportVersions) + .innerJoin( + productPassports, + eq(productPassports.id, productPassportVersions.passportId), + ) + .where( + and( + isNotNull(productPassportVersions.dataSnapshot), + isNull(productPassportVersions.compressedSnapshot), + or( + isNull(productPassports.currentVersionId), + ne(productPassportVersions.id, productPassports.currentVersionId), + ), + ), + ) + .orderBy(asc(productPassportVersions.publishedAt)) + .limit(limit)) as StoredDppVersion[]; + + let compressed = 0; + + for (const version of versions) { + // Compress one superseded version at a time to keep memory bounded. + const updated = await compressVersion(db, version.id); + if (updated?.compressedSnapshot !== null) { + compressed++; + } + } + + return { + scanned: versions.length, + compressed, + skipped: versions.length - compressed, + versionIds: versions.map((version) => version.id), + }; } diff --git a/packages/db/src/queries/products/index.ts b/packages/db/src/queries/products/index.ts index 8d90a016..26a0cbbe 100644 --- a/packages/db/src/queries/products/index.ts +++ b/packages/db/src/queries/products/index.ts @@ -19,7 +19,9 @@ export * from "./passports"; export * from "./dpp-versions"; export * from "./snapshot"; export * from "./publish"; +export * from "./projector"; export * from "./publish-batch"; +export * from "./mark-dirty"; export * from "./upid-generation"; export * from "./qr-export"; export * from "./catalog-fan-out"; diff --git a/packages/db/src/queries/products/mark-dirty.ts b/packages/db/src/queries/products/mark-dirty.ts new file mode 100644 index 00000000..8a2602cc --- /dev/null +++ b/packages/db/src/queries/products/mark-dirty.ts @@ -0,0 +1,184 @@ +/** + * Passport Dirty Marking Queries. + * + * Centralizes the lightweight write paths that mark published passports as dirty + * when working-layer data changes. + */ + +import { and, eq, inArray } from "drizzle-orm"; +import type { DatabaseOrTransaction } from "../../client"; +import { productPassports, productVariants, products } from "../../schema"; + +/** + * Result of a dirty-marking operation. + */ +export interface MarkDirtyResult { + marked: number; + passportIds: string[]; +} + +/** + * Persist dirty=true for a known set of passport IDs. + * + * @param db - Database instance or transaction + * @param passportIds - Passport IDs to mark dirty + * @returns Count and IDs of passports that transitioned to dirty + */ +async function markDirtyByPassportIds( + db: DatabaseOrTransaction, + passportIds: string[], +): Promise { + // Deduplicate IDs up front so repeated inputs do not inflate the UPDATE work. + const uniquePassportIds = [...new Set(passportIds)]; + if (uniquePassportIds.length === 0) { + return { marked: 0, passportIds: [] }; + } + + const now = new Date().toISOString(); + const updatedPassports = await db + .update(productPassports) + .set({ + dirty: true, + updatedAt: now, + }) + .where( + and( + inArray(productPassports.id, uniquePassportIds), + eq(productPassports.dirty, false), + ), + ) + .returning({ id: productPassports.id }); + + return { + marked: updatedPassports.length, + passportIds: updatedPassports.map((passport) => passport.id), + }; +} + +/** + * Mark a single passport dirty. + * + * @param db - Database instance or transaction + * @param passportId - Passport ID to mark dirty + * @returns Count and IDs of passports that transitioned to dirty + */ +export async function markPassportDirty( + db: DatabaseOrTransaction, + passportId: string, +): Promise { + // Delegate to the shared updater so single and batch paths stay identical. + return markDirtyByPassportIds(db, [passportId]); +} + +/** + * Mark passports dirty for a set of variants, but only when their products are published. + * + * @param db - Database instance or transaction + * @param variantIds - Working variant IDs to inspect + * @returns Count and IDs of passports that transitioned to dirty + */ +export async function markPassportsDirtyByVariantIds( + db: DatabaseOrTransaction, + variantIds: string[], +): Promise { + // Skip empty batches so mutation paths can forward raw variant lists directly. + if (variantIds.length === 0) { + return { marked: 0, passportIds: [] }; + } + + const passports = await db + .select({ id: productPassports.id }) + .from(productPassports) + .innerJoin( + productVariants, + eq(productVariants.id, productPassports.workingVariantId), + ) + .innerJoin(products, eq(products.id, productVariants.productId)) + .where( + and( + inArray(productVariants.id, variantIds), + eq(products.status, "published"), + eq(productPassports.dirty, false), + ), + ); + + return markDirtyByPassportIds( + db, + passports.map((passport) => passport.id), + ); +} + +/** + * Mark passports dirty for all variants under the supplied products, but only when published. + * + * @param db - Database instance or transaction + * @param brandId - Brand scope for the product IDs + * @param productIds - Product IDs whose passports should be marked dirty + * @returns Count and IDs of passports that transitioned to dirty + */ +export async function markPassportsDirtyByProductIds( + db: DatabaseOrTransaction, + brandId: string, + productIds: string[], +): Promise { + // Skip empty batches so bulk mutation paths can pass through filtered IDs. + if (productIds.length === 0) { + return { marked: 0, passportIds: [] }; + } + + const passports = await db + .select({ id: productPassports.id }) + .from(productPassports) + .innerJoin( + productVariants, + eq(productVariants.id, productPassports.workingVariantId), + ) + .innerJoin(products, eq(products.id, productVariants.productId)) + .where( + and( + eq(products.brandId, brandId), + inArray(products.id, productIds), + eq(products.status, "published"), + eq(productPassports.dirty, false), + ), + ); + + return markDirtyByPassportIds( + db, + passports.map((passport) => passport.id), + ); +} + +/** + * Mark all published passports for a brand dirty. + * + * @param db - Database instance or transaction + * @param brandId - Brand whose published passports should be marked dirty + * @returns Count and IDs of passports that transitioned to dirty + */ +export async function markAllBrandPassportsDirty( + db: DatabaseOrTransaction, + brandId: string, +): Promise { + // Scope the lookup to published products so unpublished data stays cold. + const passports = await db + .select({ id: productPassports.id }) + .from(productPassports) + .innerJoin( + productVariants, + eq(productVariants.id, productPassports.workingVariantId), + ) + .innerJoin(products, eq(products.id, productVariants.productId)) + .where( + and( + eq(products.brandId, brandId), + eq(products.status, "published"), + eq(productPassports.dirty, false), + ), + ); + + return markDirtyByPassportIds( + db, + passports.map((passport) => passport.id), + ); +} diff --git a/packages/db/src/queries/products/passports.ts b/packages/db/src/queries/products/passports.ts index 9642e72b..33dc12e8 100644 --- a/packages/db/src/queries/products/passports.ts +++ b/packages/db/src/queries/products/passports.ts @@ -6,7 +6,7 @@ * QR codes remain resolvable indefinitely. */ -import { eq, inArray } from "drizzle-orm"; +import { and, eq, inArray } from "drizzle-orm"; import type { Database, DatabaseOrTransaction } from "../../client"; import { productPassportVersions, @@ -35,6 +35,7 @@ export async function createProductPassport( variantId: string, brandId: string, ) { + // Load the variant identifiers that are copied onto the passport record. // Get the variant's data - UPID must exist, also get sku/barcode const [variant] = await db .select({ @@ -56,7 +57,6 @@ export async function createProductPassport( ); } - const now = new Date().toISOString(); const [passport] = await db .insert(productPassports) .values({ @@ -66,7 +66,7 @@ export async function createProductPassport( status: "active", sku: variant.sku, barcode: variant.barcode, - firstPublishedAt: now, + firstPublishedAt: null, }) .returning({ id: productPassports.id, @@ -237,6 +237,7 @@ export async function updatePassportCurrentVersion( passportId: string, versionId: string, ) { + // Promote the supplied version to be the passport's active snapshot. const now = new Date().toISOString(); const [updated] = await db @@ -264,6 +265,82 @@ export async function updatePassportCurrentVersion( return updated ?? null; } +/** + * Clear the dirty flag for a single passport after successful projection. + * + * @param db - Database instance or transaction + * @param passportId - The passport ID to clear + * @returns The updated passport, or null if the passport was already clean or missing + */ +export async function clearDirtyFlag( + db: DatabaseOrTransaction, + passportId: string, +) { + // Only touch passports that are currently dirty so repeated clears stay idempotent. + const now = new Date().toISOString(); + + const [updated] = await db + .update(productPassports) + .set({ + dirty: false, + updatedAt: now, + }) + .where( + and(eq(productPassports.id, passportId), eq(productPassports.dirty, true)), + ) + .returning({ + id: productPassports.id, + upid: productPassports.upid, + brandId: productPassports.brandId, + workingVariantId: productPassports.workingVariantId, + currentVersionId: productPassports.currentVersionId, + status: productPassports.status, + orphanedAt: productPassports.orphanedAt, + sku: productPassports.sku, + barcode: productPassports.barcode, + firstPublishedAt: productPassports.firstPublishedAt, + createdAt: productPassports.createdAt, + updatedAt: productPassports.updatedAt, + }); + + return updated ?? null; +} + +/** + * Clear the dirty flag for multiple passports after batch projection. + * + * @param db - Database instance or transaction + * @param passportIds - The passport IDs to clear + * @returns Count of passports cleared + */ +export async function batchClearDirtyFlags( + db: DatabaseOrTransaction, + passportIds: string[], +) { + // Skip empty batches so callers can forward chunk results directly. + if (passportIds.length === 0) { + return { cleared: 0 }; + } + + const now = new Date().toISOString(); + + const result = await db + .update(productPassports) + .set({ + dirty: false, + updatedAt: now, + }) + .where( + and( + inArray(productPassports.id, passportIds), + eq(productPassports.dirty, true), + ), + ) + .returning({ id: productPassports.id }); + + return { cleared: result.length }; +} + /** * Sync passport SKU and barcode fields when variant data is updated. * @@ -283,6 +360,7 @@ export async function syncPassportMetadata( barcode?: string | null; }, ) { + // Skip writes when the caller did not supply any identifier changes. // Only proceed if there are fields to update const hasSkuUpdate = updates.sku !== undefined; const hasBarcodeUpdate = updates.barcode !== undefined; @@ -340,6 +418,7 @@ export async function batchSyncPassportMetadata( db: DatabaseOrTransaction, updates: Map, ) { + // Skip empty batches to keep bulk callers cheap. if (updates.size === 0) { return { updated: 0 }; } @@ -394,6 +473,7 @@ export async function getOrCreatePassport( variantId: string, brandId: string, ) { + // Reuse the existing passport when present to keep the UPID stable. // First, check if a passport already exists const existing = await getPassportByVariantId(db, variantId); if (existing) { @@ -421,6 +501,7 @@ export async function getOrCreatePassport( * @returns The updated passport, or null if not found */ export async function orphanPassport(db: Database, passportId: string) { + // Sever the working-layer link while keeping the immutable passport history. const now = new Date().toISOString(); const [updated] = await db @@ -466,6 +547,7 @@ export async function batchOrphanPassportsByVariantIds( db: DatabaseOrTransaction, variantIds: string[], ) { + // Skip empty batches so delete flows can pass through raw variant lists. if (variantIds.length === 0) { return { orphaned: 0 }; } @@ -511,8 +593,7 @@ export async function createPassportForVariant( barcode?: string | null; }, ) { - const now = new Date().toISOString(); - + // Persist the durable passport identity alongside the new working variant. const [passport] = await db .insert(productPassports) .values({ @@ -522,7 +603,7 @@ export async function createPassportForVariant( status: "active", sku: variantData.sku ?? null, barcode: variantData.barcode ?? null, - firstPublishedAt: now, + firstPublishedAt: null, }) .returning({ id: productPassports.id, @@ -562,12 +643,12 @@ export async function batchCreatePassportsForVariants( barcode?: string | null; }>, ) { + // Skip empty batches so import flows can pass through filtered variant lists. if (variants.length === 0) { return []; } - const now = new Date().toISOString(); - + // Insert one clean passport per variant and defer firstPublishedAt until projection. const passports = await db .insert(productPassports) .values( @@ -578,7 +659,7 @@ export async function batchCreatePassportsForVariants( status: "active", sku: v.sku ?? null, barcode: v.barcode ?? null, - firstPublishedAt: now, + firstPublishedAt: null, })), ) .returning({ diff --git a/packages/db/src/queries/products/projector.ts b/packages/db/src/queries/products/projector.ts new file mode 100644 index 00000000..a35e33b3 --- /dev/null +++ b/packages/db/src/queries/products/projector.ts @@ -0,0 +1,834 @@ +/** + * Passport projector operations. + * + * Centralizes snapshot materialization behind a single module: + * - projectSinglePassport() for on-demand public reads and explicit variant publish + * - projectDirtyPassports() for inline product publish and background projection + * - projectDirtyPassportsAllBrands() for scheduled projector jobs + */ + +import { createHash } from "node:crypto"; +import { and, eq, inArray, isNotNull, isNull } from "drizzle-orm"; +import type { Database } from "../../client"; +import { + productPassportVersions, + productPassports, + productVariants, + products, +} from "../../schema"; +import { + type DppSnapshot, + createDppVersion, + getLatestVersion, + getVersionSnapshot, +} from "./dpp-versions"; +import { batchClearDirtyFlags } from "./passports"; +import { + type PublishProductsSetBasedResult, + publishProductsSetBased, +} from "./publish-batch"; +import { generateDppSnapshot } from "./snapshot"; + +type PassportVersionRow = { + id: string; + versionNumber: number; + dataSnapshot: unknown; + compressedSnapshot: Buffer | null; + contentHash: string; + schemaVersion: string; + publishedAt: string; +}; + +type PassportProjectionRow = { + id: string; + upid: string; + barcode: string | null; + currentVersionId: string | null; + firstPublishedAt: string | null; + dirty: boolean; + workingVariantId: string | null; + brandId: string | null; +}; + +type PassportProjectionStateRow = { + id: string; + currentVersionId: string | null; + firstPublishedAt: string | null; +}; + +type DirtyPassportRow = { + passportId: string; + productId: string; +}; + +type ProjectedPassportIdentifierRow = { + id: string; + upid: string; + barcode: string | null; + currentVersionId: string | null; + firstPublishedAt: string | null; +}; + +/** + * Options controlling batch passport projection. + */ +export interface ProjectDirtyPassportsOptions { + /** Limit projection to these published products instead of scanning dirty passports. */ + productIds?: string[]; + /** Override the variant chunk size used by the set-based projector. */ + variantChunkSize?: number; + /** Override the version schema identifier written to new versions. */ + schemaVersion?: string; +} + +/** + * Result of projecting a single passport. + */ +export interface ProjectSinglePassportResult { + found: boolean; + versionCreated: boolean; + dirtyCleared: boolean; + passport: PassportProjectionRow | null; + snapshot: DppSnapshot | null; + version: { + id: string; + versionNumber: number; + schemaVersion: string; + publishedAt: string; + contentHash: string; + } | null; + error?: string; +} + +/** + * Result of projecting a batch of dirty passports for one brand. + */ +export interface ProjectDirtyPassportsResult { + brandId: string; + totalProductsProjected: number; + totalDirtyPassportsRequested: number; + totalPassportsProjected: number; + totalVariantsConsidered: number; + totalVariantsSkippedNoUpid: number; + passportsCreated: number; + versionsCreated: number; + versionsSkippedUnchanged: number; + dirtyFlagsCleared: number; + firstPublishedSet: number; + upids: string[]; + barcodes: string[]; +} + +/** + * Result of projecting dirty passports across all brands. + */ +export interface ProjectDirtyPassportsAllBrandsResult { + brandsProcessed: number; + totalProductsProjected: number; + totalDirtyPassportsRequested: number; + totalPassportsProjected: number; + totalVariantsConsidered: number; + totalVariantsSkippedNoUpid: number; + passportsCreated: number; + versionsCreated: number; + versionsSkippedUnchanged: number; + dirtyFlagsCleared: number; + firstPublishedSet: number; + brands: ProjectDirtyPassportsResult[]; +} + +/** + * Recursively sort object keys for deterministic JSON hashing. + */ +function sortObjectKeys(value: unknown): unknown { + // Return primitives directly. + if (value === null || typeof value !== "object") { + return value; + } + + // Preserve array order while sorting nested objects. + if (Array.isArray(value)) { + return value.map(sortObjectKeys); + } + + const sorted: Record = {}; + for (const key of Object.keys(value).sort()) { + sorted[key] = sortObjectKeys((value as Record)[key]); + } + return sorted; +} + +/** + * Calculate the content-only hash used for version deduplication. + */ +function calculateContentOnlyHash(snapshot: DppSnapshot): string { + // Ignore publish metadata so unchanged content does not create new versions. + const contentOnly = { + "@context": snapshot["@context"], + "@type": snapshot["@type"], + "@id": snapshot["@id"], + productIdentifiers: snapshot.productIdentifiers, + productAttributes: snapshot.productAttributes, + environmental: snapshot.environmental, + materials: snapshot.materials, + supplyChain: snapshot.supplyChain, + }; + + const canonicalJson = JSON.stringify(sortObjectKeys(contentOnly)); + return createHash("sha256").update(canonicalJson, "utf8").digest("hex"); +} + +/** + * Convert a version row into the public projector shape. + */ +function mapProjectedVersion(version: PassportVersionRow | null) { + // Return null for passports that still have no materialized version. + if (!version) { + return null; + } + + return { + id: version.id, + versionNumber: version.versionNumber, + schemaVersion: version.schemaVersion, + publishedAt: version.publishedAt, + contentHash: version.contentHash, + }; +} + +/** + * Remove duplicates while preserving the first-seen order. + */ +function uniqueStrings(values: Array): string[] { + // Filter nullish and blank strings before deduping. + return Array.from( + new Set( + values + .map((value) => value?.trim() ?? null) + .filter((value): value is string => Boolean(value)), + ), + ); +} + +/** + * Reuse an already-materialized version and clear the dirty flag. + */ +async function reuseProjectedVersion( + db: Database, + passport: PassportProjectionRow, + version: PassportVersionRow, + snapshot: DppSnapshot, +): Promise { + // Point the passport at the winner version and clear dirty after reused projection. + await finalizeProjectedPassport( + db, + passport, + version.id, + !passport.firstPublishedAt ? version.publishedAt : undefined, + ); + + const refreshedPassport = await getPassportProjectionRow(db, passport.id); + + return { + found: true, + versionCreated: false, + dirtyCleared: passport.dirty, + passport: refreshedPassport, + snapshot, + version: mapProjectedVersion(version), + }; +} + +/** + * Fetch the current version row by ID. + */ +async function getVersionById( + db: Database, + versionId: string | null, +): Promise { + // Skip the lookup when no version pointer is present. + if (!versionId) { + return null; + } + + const [version] = await db + .select({ + id: productPassportVersions.id, + versionNumber: productPassportVersions.versionNumber, + dataSnapshot: productPassportVersions.dataSnapshot, + compressedSnapshot: productPassportVersions.compressedSnapshot, + contentHash: productPassportVersions.contentHash, + schemaVersion: productPassportVersions.schemaVersion, + publishedAt: productPassportVersions.publishedAt, + }) + .from(productPassportVersions) + .where(eq(productPassportVersions.id, versionId)) + .limit(1); + + return version ?? null; +} + +/** + * Load the lightweight passport state used by the single-passport projector. + */ +async function getPassportProjectionRow( + db: Database, + passportId: string, +): Promise { + // Read only the columns needed to materialize or serve the passport. + const [passport] = await db + .select({ + id: productPassports.id, + upid: productPassports.upid, + barcode: productPassports.barcode, + currentVersionId: productPassports.currentVersionId, + firstPublishedAt: productPassports.firstPublishedAt, + dirty: productPassports.dirty, + workingVariantId: productPassports.workingVariantId, + brandId: productPassports.brandId, + }) + .from(productPassports) + .where(eq(productPassports.id, passportId)) + .limit(1); + + return passport ?? null; +} + +/** + * Persist the current-version pointer, dirty flag, and optional first-publish timestamp. + */ +async function finalizeProjectedPassport( + db: Database, + passport: PassportProjectionRow, + versionId: string, + firstPublishedAt?: string, +): Promise { + // Apply the projector state transition in a single update. + const updatePayload: { + currentVersionId: string; + dirty: boolean; + updatedAt: string; + firstPublishedAt?: string; + } = { + currentVersionId: versionId, + dirty: false, + updatedAt: new Date().toISOString(), + }; + + if (!passport.firstPublishedAt && firstPublishedAt) { + updatePayload.firstPublishedAt = firstPublishedAt; + } + + await db + .update(productPassports) + .set(updatePayload) + .where(eq(productPassports.id, passport.id)); +} + +/** + * List the subset of requested products that are currently published. + */ +async function listPublishedProductIds( + db: Database, + brandId: string, + productIds: string[], +): Promise { + // Normalize explicit product filters against the brand and publish state. + if (productIds.length === 0) { + return []; + } + + const rows = await db + .select({ id: products.id }) + .from(products) + .where( + and( + eq(products.brandId, brandId), + inArray(products.id, productIds), + eq(products.status, "published"), + ), + ); + + return rows.map((row) => row.id); +} + +/** + * Load dirty passports for the selected published products. + */ +async function listDirtyPassportsForProducts( + db: Database, + brandId: string, + productIds: string[], +): Promise { + // Only dirty active passports with a published working product need projection. + if (productIds.length === 0) { + return []; + } + + return db + .select({ + passportId: productPassports.id, + productId: products.id, + }) + .from(productPassports) + .innerJoin( + productVariants, + eq(productVariants.id, productPassports.workingVariantId), + ) + .innerJoin(products, eq(products.id, productVariants.productId)) + .where( + and( + eq(productPassports.brandId, brandId), + inArray(products.id, productIds), + eq(products.status, "published"), + eq(productPassports.dirty, true), + ), + ); +} + +/** + * Load the pre-projection passport state for all published products being processed. + */ +async function listExistingPassportStatesForProducts( + db: Database, + brandId: string, + productIds: string[], +): Promise> { + // Capture the old current-version pointers so firstPublishedAt is only set once. + if (productIds.length === 0) { + return new Map(); + } + + const rows = await db + .select({ + id: productPassports.id, + currentVersionId: productPassports.currentVersionId, + firstPublishedAt: productPassports.firstPublishedAt, + }) + .from(productPassports) + .innerJoin( + productVariants, + eq(productVariants.id, productPassports.workingVariantId), + ) + .innerJoin(products, eq(products.id, productVariants.productId)) + .where( + and(eq(products.brandId, brandId), inArray(products.id, productIds)), + ); + + return new Map(rows.map((row) => [row.id, row])); +} + +/** + * Load all published passports and identifiers for the processed products after projection. + */ +async function listProjectedPassportsForProducts( + db: Database, + brandId: string, + productIds: string[], +): Promise { + // Read the final passport state used for cache revalidation and result reporting. + if (productIds.length === 0) { + return []; + } + + return db + .select({ + id: productPassports.id, + upid: productPassports.upid, + barcode: productPassports.barcode, + currentVersionId: productPassports.currentVersionId, + firstPublishedAt: productPassports.firstPublishedAt, + }) + .from(productPassports) + .innerJoin( + productVariants, + eq(productVariants.id, productPassports.workingVariantId), + ) + .innerJoin(products, eq(products.id, productVariants.productId)) + .where( + and( + eq(products.brandId, brandId), + inArray(products.id, productIds), + eq(products.status, "published"), + isNotNull(productPassports.currentVersionId), + ), + ); +} + +/** + * Set firstPublishedAt for passports that received their first version in this run. + */ +async function setFirstPublishedAtForProjectedPassports( + db: Database, + passportIds: string[], +): Promise { + // Skip the update when no passport crossed the unpublished -> published boundary. + if (passportIds.length === 0) { + return 0; + } + + const now = new Date().toISOString(); + const updated = await db + .update(productPassports) + .set({ + firstPublishedAt: now, + updatedAt: now, + }) + .where( + and( + inArray(productPassports.id, passportIds), + isNull(productPassports.firstPublishedAt), + isNotNull(productPassports.currentVersionId), + ), + ) + .returning({ id: productPassports.id }); + + return updated.length; +} + +/** + * Materialize a single passport and return the fresh snapshot. + */ +export async function projectSinglePassport( + db: Database, + passportId: string, +): Promise { + // Load the passport record first so the projector can decide how to proceed. + const passport = await getPassportProjectionRow(db, passportId); + + if (!passport) { + return { + found: false, + versionCreated: false, + dirtyCleared: false, + passport: null, + snapshot: null, + version: null, + error: "Passport not found", + }; + } + + const currentVersion = await getVersionById(db, passport.currentVersionId); + + // Orphaned passports keep serving their last materialized snapshot. + if (!passport.workingVariantId) { + const dirtyCleared = passport.dirty + ? (await batchClearDirtyFlags(db, [passport.id])).cleared > 0 + : false; + const refreshedPassport = + dirtyCleared || !currentVersion + ? await getPassportProjectionRow(db, passport.id) + : passport; + + return { + found: true, + versionCreated: false, + dirtyCleared, + passport: refreshedPassport, + snapshot: currentVersion ? getVersionSnapshot(currentVersion) : null, + version: mapProjectedVersion(currentVersion), + error: currentVersion ? undefined : "Passport has not been published yet", + }; + } + + const snapshot = await generateDppSnapshot( + db, + passport.workingVariantId, + passport.upid, + ); + + if (!snapshot) { + return { + found: true, + versionCreated: false, + dirtyCleared: false, + passport, + snapshot: currentVersion ? getVersionSnapshot(currentVersion) : null, + version: mapProjectedVersion(currentVersion), + error: "Failed to generate snapshot", + }; + } + + const latestVersion = await getLatestVersion(db, passport.id); + if (latestVersion) { + const existingSnapshot = getVersionSnapshot(latestVersion); + if ( + existingSnapshot && + calculateContentOnlyHash(snapshot) === + calculateContentOnlyHash(existingSnapshot) + ) { + // Reuse the current version when the materialized content is unchanged. + return reuseProjectedVersion(db, passport, latestVersion, existingSnapshot); + } + } + + // Write a new immutable version when the snapshot content changed. + const version = await createDppVersion(db, passport.id, snapshot, "1.0"); + if (!version) { + const concurrentVersion = await getLatestVersion(db, passport.id); + const concurrentSnapshot = concurrentVersion + ? getVersionSnapshot(concurrentVersion) + : null; + + if ( + concurrentVersion && + concurrentSnapshot && + calculateContentOnlyHash(snapshot) === + calculateContentOnlyHash(concurrentSnapshot) + ) { + // Another projector won the race with equivalent content, so reuse it. + return reuseProjectedVersion( + db, + passport, + concurrentVersion, + concurrentSnapshot, + ); + } + + return { + found: true, + versionCreated: false, + dirtyCleared: false, + passport, + snapshot: + concurrentSnapshot ?? (currentVersion ? getVersionSnapshot(currentVersion) : null), + version: mapProjectedVersion( + (concurrentVersion as PassportVersionRow | null) ?? currentVersion, + ), + error: "Failed to create version record", + }; + } + + await finalizeProjectedPassport( + db, + passport, + version.id, + !passport.currentVersionId ? version.publishedAt : undefined, + ); + + const refreshedPassport = await getPassportProjectionRow(db, passport.id); + return { + found: true, + versionCreated: true, + dirtyCleared: true, + passport: refreshedPassport, + snapshot: getVersionSnapshot(version), + version: mapProjectedVersion(version), + }; +} + +/** + * Project dirty passports for one brand, optionally forcing a published product subset. + */ +export async function projectDirtyPassports( + db: Database, + brandId: string, + options: ProjectDirtyPassportsOptions = {}, +): Promise { + // Normalize the explicit product filter first so empty inputs short-circuit early. + const requestedProductIds = Array.from(new Set(options.productIds ?? [])); + const hasExplicitProductFilter = requestedProductIds.length > 0; + const explicitPublishedProductIds = + hasExplicitProductFilter + ? await listPublishedProductIds(db, brandId, requestedProductIds) + : []; + + if (hasExplicitProductFilter && explicitPublishedProductIds.length === 0) { + return { + brandId, + totalProductsProjected: 0, + totalDirtyPassportsRequested: 0, + totalPassportsProjected: 0, + totalVariantsConsidered: 0, + totalVariantsSkippedNoUpid: 0, + passportsCreated: 0, + versionsCreated: 0, + versionsSkippedUnchanged: 0, + dirtyFlagsCleared: 0, + firstPublishedSet: 0, + upids: [], + barcodes: [], + }; + } + + const dirtyPassports = + hasExplicitProductFilter + ? await listDirtyPassportsForProducts(db, brandId, explicitPublishedProductIds) + : await db + .select({ + passportId: productPassports.id, + productId: products.id, + }) + .from(productPassports) + .innerJoin( + productVariants, + eq(productVariants.id, productPassports.workingVariantId), + ) + .innerJoin(products, eq(products.id, productVariants.productId)) + .where( + and( + eq(productPassports.brandId, brandId), + eq(products.status, "published"), + eq(productPassports.dirty, true), + ), + ); + + const publishedProductIds = + hasExplicitProductFilter + ? explicitPublishedProductIds + : Array.from(new Set(dirtyPassports.map((row) => row.productId))); + + if (publishedProductIds.length === 0) { + return { + brandId, + totalProductsProjected: 0, + totalDirtyPassportsRequested: dirtyPassports.length, + totalPassportsProjected: 0, + totalVariantsConsidered: 0, + totalVariantsSkippedNoUpid: 0, + passportsCreated: 0, + versionsCreated: 0, + versionsSkippedUnchanged: 0, + dirtyFlagsCleared: 0, + firstPublishedSet: 0, + upids: [], + barcodes: [], + }; + } + + const preProjectionState = await listExistingPassportStatesForProducts( + db, + brandId, + publishedProductIds, + ); + + const batchResult: PublishProductsSetBasedResult = await publishProductsSetBased( + db, + { + brandId, + productIds: publishedProductIds, + variantChunkSize: options.variantChunkSize, + schemaVersion: options.schemaVersion, + }, + ); + + const projectedPassports = await listProjectedPassportsForProducts( + db, + brandId, + publishedProductIds, + ); + + const firstPublishedPassportIds = projectedPassports + .filter((passport) => { + const previous = preProjectionState.get(passport.id); + return ( + passport.currentVersionId !== null && + passport.firstPublishedAt === null && + (previous?.currentVersionId ?? null) === null + ); + }) + .map((passport) => passport.id); + + const [firstPublishedSet, dirtyClearResult] = await Promise.all([ + setFirstPublishedAtForProjectedPassports(db, firstPublishedPassportIds), + batchClearDirtyFlags( + db, + dirtyPassports.map((row) => row.passportId), + ), + ]); + + return { + brandId, + totalProductsProjected: publishedProductIds.length, + totalDirtyPassportsRequested: dirtyPassports.length, + totalPassportsProjected: projectedPassports.length, + totalVariantsConsidered: batchResult.totalVariantsConsidered, + totalVariantsSkippedNoUpid: batchResult.totalVariantsSkippedNoUpid, + passportsCreated: batchResult.passportsCreated, + versionsCreated: batchResult.versionsCreated, + versionsSkippedUnchanged: batchResult.versionsSkippedUnchanged, + dirtyFlagsCleared: dirtyClearResult.cleared, + firstPublishedSet, + upids: uniqueStrings(projectedPassports.map((passport) => passport.upid)), + barcodes: uniqueStrings( + projectedPassports.map((passport) => passport.barcode), + ), + }; +} + +/** + * Project dirty passports across every brand that currently has pending work. + */ +export async function projectDirtyPassportsAllBrands( + db: Database, + options: Omit = {}, +): Promise { + // Scan the dirty-passport index and process brands one at a time. + const brandRows = await db + .select({ brandId: productPassports.brandId }) + .from(productPassports) + .where( + and(eq(productPassports.dirty, true), isNotNull(productPassports.brandId)), + ) + .groupBy(productPassports.brandId); + + const results: ProjectDirtyPassportsResult[] = []; + + for (const row of brandRows) { + if (!row.brandId) { + continue; + } + + results.push( + await projectDirtyPassports(db, row.brandId, { + variantChunkSize: options.variantChunkSize, + schemaVersion: options.schemaVersion, + }), + ); + } + + return { + brandsProcessed: results.length, + totalProductsProjected: results.reduce( + (sum, result) => sum + result.totalProductsProjected, + 0, + ), + totalDirtyPassportsRequested: results.reduce( + (sum, result) => sum + result.totalDirtyPassportsRequested, + 0, + ), + totalPassportsProjected: results.reduce( + (sum, result) => sum + result.totalPassportsProjected, + 0, + ), + totalVariantsConsidered: results.reduce( + (sum, result) => sum + result.totalVariantsConsidered, + 0, + ), + totalVariantsSkippedNoUpid: results.reduce( + (sum, result) => sum + result.totalVariantsSkippedNoUpid, + 0, + ), + passportsCreated: results.reduce( + (sum, result) => sum + result.passportsCreated, + 0, + ), + versionsCreated: results.reduce( + (sum, result) => sum + result.versionsCreated, + 0, + ), + versionsSkippedUnchanged: results.reduce( + (sum, result) => sum + result.versionsSkippedUnchanged, + 0, + ), + dirtyFlagsCleared: results.reduce( + (sum, result) => sum + result.dirtyFlagsCleared, + 0, + ), + firstPublishedSet: results.reduce( + (sum, result) => sum + result.firstPublishedSet, + 0, + ), + brands: results, + }; +} diff --git a/packages/db/src/queries/products/publish-batch.ts b/packages/db/src/queries/products/publish-batch.ts index 8f32426e..a7f08ae6 100644 --- a/packages/db/src/queries/products/publish-batch.ts +++ b/packages/db/src/queries/products/publish-batch.ts @@ -35,7 +35,7 @@ import { buildProductImageUrl, getSupabaseUrlFromEnv, } from "../../utils/storage-url"; -import type { DppSnapshot } from "./dpp-versions"; +import { getVersionSnapshot, type DppSnapshot } from "./dpp-versions"; import { batchCreatePassportsForVariants } from "./passports"; // ============================================================================= @@ -966,6 +966,7 @@ async function publishVariantChunk( passportId: productPassportVersions.passportId, versionNumber: productPassportVersions.versionNumber, dataSnapshot: productPassportVersions.dataSnapshot, + compressedSnapshot: productPassportVersions.compressedSnapshot, }) .from(productPassportVersions) .where(inArray(productPassportVersions.id, currentVersionIds)) @@ -993,10 +994,15 @@ async function publishVariantChunk( const currentVersion = currentVersionByPassportId.get(target.passportId); if (currentVersion) { - const existingSnapshot = currentVersion.dataSnapshot as DppSnapshot; + const existingSnapshot = getVersionSnapshot({ + dataSnapshot: currentVersion.dataSnapshot as DppSnapshot | null, + compressedSnapshot: currentVersion.compressedSnapshot, + }); const newContentHash = calculateContentOnlyHash(snapshot); - const existingContentHash = calculateContentOnlyHash(existingSnapshot); - if (newContentHash === existingContentHash) { + const existingContentHash = existingSnapshot + ? calculateContentOnlyHash(existingSnapshot) + : null; + if (existingContentHash !== null && newContentHash === existingContentHash) { versionsSkippedUnchanged++; continue; } diff --git a/packages/db/src/queries/products/publish.ts b/packages/db/src/queries/products/publish.ts index 056ae3e9..7f00f626 100644 --- a/packages/db/src/queries/products/publish.ts +++ b/packages/db/src/queries/products/publish.ts @@ -1,444 +1,22 @@ /** - * Publish Operations. + * Publishing state queries. * - * Orchestrates the complete publish flow for variants and products. - * Publishing creates an immutable version record that persists independently - * of the working layer. + * Keeps the lightweight product-level publishing state helpers that are still + * used by the UI, while snapshot materialization now lives in projector.ts. */ -import { createHash } from "node:crypto"; -import { and, eq, inArray } from "drizzle-orm"; +import { and, eq } from "drizzle-orm"; import type { Database } from "../../client"; -import { productVariants, products } from "../../schema"; -import { - type DppSnapshot, - createDppVersion, - getLatestVersion, -} from "./dpp-versions"; -import { getOrCreatePassport, updatePassportCurrentVersion } from "./passports"; -import { generateDppSnapshot } from "./snapshot"; - -// ============================================================================= -// TYPES -// ============================================================================= - -/** - * Result of publishing a single variant. - */ -export interface PublishVariantResult { - success: boolean; - variantId: string; - passport: { - id: string; - upid: string; - isNew: boolean; - } | null; - version: { - id: string; - versionNumber: number; - publishedAt: string; - } | null; - error?: string; -} - -/** - * Result of publishing an entire product (all its variants). - */ -export interface PublishProductResult { - success: boolean; - productId: string; - variants: PublishVariantResult[]; - totalPublished: number; - totalFailed: number; - error?: string; -} - -/** - * Result of bulk publishing multiple products. - */ -export interface BulkPublishResult { - success: boolean; - products: PublishProductResult[]; - totalProductsPublished: number; - totalVariantsPublished: number; - totalFailed: number; -} - -// ============================================================================= -// HELPER FUNCTIONS -// ============================================================================= - -/** - * Recursively sort all object keys for canonical JSON serialization. - * This ensures deterministic hashing regardless of property order. - */ -function sortObjectKeys(obj: unknown): unknown { - if (obj === null || typeof obj !== "object") { - return obj; - } - if (Array.isArray(obj)) { - return obj.map(sortObjectKeys); - } - const sorted: Record = {}; - for (const key of Object.keys(obj).sort()) { - sorted[key] = sortObjectKeys((obj as Record)[key]); - } - return sorted; -} - -/** - * Calculate a content-only hash for deduplication. - * This excludes metadata (publishedAt, versionNumber, schemaVersion) which change on every publish. - * Used to determine if content has actually changed since the last version. - */ -function calculateContentOnlyHash(snapshot: DppSnapshot): string { - // Extract only the content fields, excluding metadata - const contentOnly = { - "@context": snapshot["@context"], - "@type": snapshot["@type"], - "@id": snapshot["@id"], - productIdentifiers: snapshot.productIdentifiers, - productAttributes: snapshot.productAttributes, - environmental: snapshot.environmental, - materials: snapshot.materials, - supplyChain: snapshot.supplyChain, - }; - // Recursively sort all keys for deterministic serialization - const sortedContent = sortObjectKeys(contentOnly); - const canonicalJson = JSON.stringify(sortedContent); - return createHash("sha256").update(canonicalJson, "utf8").digest("hex"); -} - -/** - * Get variant data needed for publishing. - */ -async function getVariantForPublish(db: Database, variantId: string) { - const [variant] = await db - .select({ - id: productVariants.id, - productId: productVariants.productId, - upid: productVariants.upid, - }) - .from(productVariants) - .where(eq(productVariants.id, variantId)) - .limit(1); - - if (!variant) return null; - - // Get product data - const [product] = await db - .select({ - id: products.id, - brandId: products.brandId, - status: products.status, - }) - .from(products) - .where(eq(products.id, variant.productId)) - .limit(1); - - if (!product) return null; - - return { - variant, - product, - }; -} - -/** - * Update product status after publishing. - */ -async function updateProductStatus( - db: Database, - productId: string, - brandId: string, -) { - await db - .update(products) - .set({ - status: "published", - updatedAt: new Date().toISOString(), - }) - .where(and(eq(products.id, productId), eq(products.brandId, brandId))); -} - -// ============================================================================= -// PUBLISH VARIANT -// ============================================================================= - -/** - * Publish a single variant. - * - * This is the core publish operation that: - * 1. Creates or retrieves the passport for the variant - * 2. Generates a snapshot from current working data - * 3. Creates a new immutable version record - * 4. Updates the passport's current version - * 5. Updates the product's publishing state - * - * @param db - Database instance - * @param variantId - The variant ID to publish - * @param brandId - The brand ID (for authorization verification) - * @returns The publish result - */ -export async function publishVariant( - db: Database, - variantId: string, - brandId: string, -): Promise { - try { - // Get variant and product data - const data = await getVariantForPublish(db, variantId); - if (!data) { - return { - success: false, - variantId, - passport: null, - version: null, - error: "Variant not found", - }; - } - - // Verify brand ownership - if (data.product.brandId !== brandId) { - return { - success: false, - variantId, - passport: null, - version: null, - error: "Unauthorized: variant does not belong to this brand", - }; - } - - // Step 1: Get or create passport - const { passport, isNew } = await getOrCreatePassport( - db, - variantId, - brandId, - ); - - if (!passport) { - return { - success: false, - variantId, - passport: null, - version: null, - error: "Failed to create or retrieve passport", - }; - } - - // Step 2: Generate snapshot from current working data - const snapshot = await generateDppSnapshot(db, variantId, passport.upid); - if (!snapshot) { - return { - success: false, - variantId, - passport: { id: passport.id, upid: passport.upid, isNew }, - version: null, - error: "Failed to generate snapshot", - }; - } - - // Step 2.5: Check if content has changed (deduplication) - // Compare the content-only hash with the latest version's content - const latestVersion = await getLatestVersion(db, passport.id); - if (latestVersion) { - const existingSnapshot = latestVersion.dataSnapshot as DppSnapshot; - const newContentHash = calculateContentOnlyHash(snapshot); - const existingContentHash = calculateContentOnlyHash(existingSnapshot); - - if (newContentHash === existingContentHash) { - // Content hasn't changed, skip creating a new version - return { - success: true, - variantId, - passport: { id: passport.id, upid: passport.upid, isNew: false }, - version: { - id: latestVersion.id, - versionNumber: latestVersion.versionNumber, - publishedAt: latestVersion.publishedAt, - }, - }; - } - } - - // Step 3: Create new version record (content has changed or no previous version) - const version = await createDppVersion(db, passport.id, snapshot, "1.0"); - if (!version) { - return { - success: false, - variantId, - passport: { id: passport.id, upid: passport.upid, isNew }, - version: null, - error: "Failed to create version record", - }; - } - - // Step 4: Update passport's current version - await updatePassportCurrentVersion(db, passport.id, version.id); - - // Step 5: Update product status - await updateProductStatus(db, data.product.id, brandId); - - return { - success: true, - variantId, - passport: { id: passport.id, upid: passport.upid, isNew }, - version: { - id: version.id, - versionNumber: version.versionNumber, - publishedAt: version.publishedAt, - }, - }; - } catch (error) { - console.error("[PUBLISH DEBUG] publishVariant error:", error); - return { - success: false, - variantId, - passport: null, - version: null, - error: error instanceof Error ? error.message : "Unknown error", - }; - } -} - -// ============================================================================= -// PUBLISH PRODUCT -// ============================================================================= - -/** - * Publish all variants of a product. - * - * @param db - Database instance - * @param productId - The product ID - * @param brandId - The brand ID (for authorization verification) - * @returns The publish result for all variants - */ -export async function publishProduct( - db: Database, - productId: string, - brandId: string, -): Promise { - try { - // Verify product exists and belongs to brand - const [product] = await db - .select({ - id: products.id, - brandId: products.brandId, - }) - .from(products) - .where(and(eq(products.id, productId), eq(products.brandId, brandId))) - .limit(1); - - if (!product) { - return { - success: false, - productId, - variants: [], - totalPublished: 0, - totalFailed: 0, - error: "Product not found or unauthorized", - }; - } - - // Get all variants for this product - const variants = await db - .select({ id: productVariants.id }) - .from(productVariants) - .where(eq(productVariants.productId, productId)); - - if (variants.length === 0) { - return { - success: false, - productId, - variants: [], - totalPublished: 0, - totalFailed: 0, - error: "No variants found for product", - }; - } - - // Publish each variant concurrently - const results = await Promise.all( - variants.map((variant) => publishVariant(db, variant.id, brandId)), - ); - - const totalPublished = results.filter((r) => r.success).length; - const totalFailed = results.filter((r) => !r.success).length; - - return { - success: totalFailed === 0, - productId, - variants: results, - totalPublished, - totalFailed, - }; - } catch (error) { - return { - success: false, - productId, - variants: [], - totalPublished: 0, - totalFailed: 0, - error: error instanceof Error ? error.message : "Unknown error", - }; - } -} - -// ============================================================================= -// BULK PUBLISH -// ============================================================================= +import { products } from "../../schema"; /** - * Publish all variants of multiple products. - * - * @param db - Database instance - * @param productIds - Array of product IDs to publish - * @param brandId - The brand ID (for authorization verification) - * @returns The bulk publish result - */ -export async function bulkPublishProducts( - db: Database, - productIds: string[], - brandId: string, -): Promise { - const productResults: PublishProductResult[] = []; - - for (const productId of productIds) { - const result = await publishProduct(db, productId, brandId); - productResults.push(result); - } - - const totalProductsPublished = productResults.filter((r) => r.success).length; - const totalVariantsPublished = productResults.reduce( - (sum, r) => sum + r.totalPublished, - 0, - ); - const totalFailed = productResults.reduce((sum, r) => sum + r.totalFailed, 0); - - return { - success: totalFailed === 0, - products: productResults, - totalProductsPublished, - totalVariantsPublished, - totalFailed, - }; -} - -// ============================================================================= -// UTILITY FUNCTIONS -// ============================================================================= - -/** - * Check if a product has any published variants. - * - * @param db - Database instance - * @param productId - The product ID - * @returns True if at least one variant has been published + * Check whether a product is currently published. */ export async function hasPublishedVariants( db: Database, productId: string, ): Promise { + // A product-level published status means its variants are publicly visible. const [product] = await db .select({ status: products.status }) .from(products) @@ -449,18 +27,14 @@ export async function hasPublishedVariants( } /** - * Get the publishing state for a product. - * - * @param db - Database instance - * @param productId - The product ID - * @param brandId - The brand ID (for authorization verification) - * @returns The product's publishing state + * Read the current publishing state for a brand-owned product. */ export async function getPublishingState( db: Database, productId: string, brandId: string, ) { + // Scope the lookup to the active brand before returning UI state. const [product] = await db .select({ status: products.status, @@ -469,7 +43,9 @@ export async function getPublishingState( .where(and(eq(products.id, productId), eq(products.brandId, brandId))) .limit(1); - if (!product) return null; + if (!product) { + return null; + } return { status: product.status as "published" | "unpublished" | "scheduled", diff --git a/packages/db/src/schema/products/product-passport-versions.ts b/packages/db/src/schema/products/product-passport-versions.ts index 80beec6d..f17471d1 100644 --- a/packages/db/src/schema/products/product-passport-versions.ts +++ b/packages/db/src/schema/products/product-passport-versions.ts @@ -1,5 +1,7 @@ import { sql } from "drizzle-orm"; import { + check, + customType, index, integer, jsonb, @@ -12,6 +14,38 @@ import { } from "drizzle-orm/pg-core"; import { productPassports } from "./product-passports"; +/** + * Postgres bytea column mapped to a Node Buffer. + */ +const bytea = customType<{ + data: Buffer; + driverData: Buffer | Uint8Array | string; +}>({ + dataType() { + return "bytea"; + }, + toDriver(value) { + // Forward binary data directly to postgres.js. + return value; + }, + fromDriver(value) { + // Normalize driver bytea values into a Buffer for compression helpers. + if (Buffer.isBuffer(value)) { + return value; + } + if (value instanceof Uint8Array) { + return Buffer.from(value); + } + if (typeof value === "string") { + if (value.startsWith("\\x")) { + return Buffer.from(value.slice(2), "hex"); + } + return Buffer.from(value, "base64"); + } + return Buffer.from([]); + }, +}); + /** * Product Passport Versions Table * @@ -51,8 +85,21 @@ export const productPassportVersions = pgTable( * Complete DPP content as a self-contained JSON-LD object. * Contains all product data, materials, supply chain, environmental info, etc. * This snapshot can render the passport without any additional queries. + * Nullable once the snapshot has been compressed into compressed_snapshot. + */ + dataSnapshot: jsonb("data_snapshot"), + /** + * Zstd-compressed historical snapshot payload. + * Populated only for superseded versions after the compression job runs. */ - dataSnapshot: jsonb("data_snapshot").notNull(), + compressedSnapshot: bytea("compressed_snapshot"), + /** + * Timestamp when the JSON snapshot was compressed into bytea storage. + */ + compressedAt: timestamp("compressed_at", { + withTimezone: true, + mode: "string", + }), /** * SHA-256 hash of the canonical JSON representation of data_snapshot. * Used for integrity verification and to detect content changes. @@ -81,6 +128,11 @@ export const productPassportVersions = pgTable( table.passportId, table.versionNumber, ), + // Require every version row to keep either live JSON or compressed bytes. + check( + "product_passport_versions_snapshot_presence_check", + sql`num_nonnulls(${table.dataSnapshot}, ${table.compressedSnapshot}) = 1`, + ), // Index for fetching all versions of a passport (for audit UI) index("idx_product_passport_versions_passport_id").using( "btree", diff --git a/packages/db/src/schema/products/product-passports.ts b/packages/db/src/schema/products/product-passports.ts index a6e0c4d7..cd8c4fde 100644 --- a/packages/db/src/schema/products/product-passports.ts +++ b/packages/db/src/schema/products/product-passports.ts @@ -1,5 +1,13 @@ +/** + * Product Passports schema. + * + * Defines the durable passport identity layer that sits between working product + * data and immutable published passport versions. + */ + import { sql } from "drizzle-orm"; import { + boolean, index, pgPolicy, pgTable, @@ -68,6 +76,11 @@ export const productPassports = pgTable( * - 'orphaned': Working variant was deleted; passport persists for QR code resolution */ status: text("status").default("active").notNull(), + /** + * Dirty flag indicating the working data has diverged from the published snapshot. + * The projector clears this after it materializes a fresh version. + */ + dirty: boolean("dirty").default(false).notNull(), /** * Timestamp when the passport became orphaned. * Set when working_variant_id becomes NULL due to variant deletion. @@ -89,12 +102,12 @@ export const productPassports = pgTable( barcode: text("barcode"), /** * Timestamp when the passport was first published. - * Set once and never modified. + * NULL until the first immutable version is actually materialized. */ firstPublishedAt: timestamp("first_published_at", { withTimezone: true, mode: "string", - }).notNull(), + }), createdAt: timestamp("created_at", { withTimezone: true, mode: "string" }) .defaultNow() .notNull(), @@ -110,6 +123,14 @@ export const productPassports = pgTable( "btree", table.brandId.asc().nullsLast().op("uuid_ops"), ), + // Index for efficiently scanning dirty passports by brand during projection. + index("idx_product_passports_brand_dirty") + .using( + "btree", + table.brandId.asc().nullsLast().op("uuid_ops"), + table.dirty.asc().nullsLast().op("bool_ops"), + ) + .where(sql`dirty = true`), // Index for finding passport by working variant (used during publish) index("idx_product_passports_working_variant_id") .using("btree", table.workingVariantId.asc().nullsLast().op("uuid_ops")) diff --git a/packages/integrations/src/sync/engine.ts b/packages/integrations/src/sync/engine.ts index 56513c5a..a9a8a7ac 100644 --- a/packages/integrations/src/sync/engine.ts +++ b/packages/integrations/src/sync/engine.ts @@ -80,6 +80,7 @@ export async function syncProducts(ctx: SyncContext): Promise { entitiesCreated: 0, productsSkippedNoMatch: 0, variantsSkippedNoMatch: 0, + affectedProductIds: [], errors: [], }; @@ -167,6 +168,12 @@ export async function syncProducts(ctx: SyncContext): Promise { result.entitiesCreated += batchResult.entitiesCreated; result.productsSkippedNoMatch += batchResult.productsSkippedNoMatch; result.variantsSkippedNoMatch += batchResult.variantsSkippedNoMatch; + result.affectedProductIds = Array.from( + new Set([ + ...(result.affectedProductIds ?? []), + ...batchResult.affectedProductIds, + ]), + ); result.errors.push(...batchResult.errors); } @@ -251,6 +258,8 @@ interface BatchResult { productsSkippedNoMatch: number; /** Variants skipped because no match found (secondary integrations only) */ variantsSkippedNoMatch: number; + /** Product IDs whose passport inputs changed in this batch */ + affectedProductIds: string[]; errors: Array<{ externalId: string; message: string }>; timing: { entityExtraction: number; @@ -293,6 +302,7 @@ async function processBatch( entitiesCreated: 0, productsSkippedNoMatch: 0, variantsSkippedNoMatch: 0, + affectedProductIds: [], errors: [], timing: { entityExtraction: 0, @@ -459,6 +469,7 @@ async function processBatch( // Track handles used within this batch to avoid collisions const usedHandlesInBatch = new Set(); + const affectedProductIds = new Set(); // Track products that have been assigned a canonical link within THIS batch // This prevents multiple products in the same batch from all being marked as canonical @@ -484,6 +495,16 @@ async function processBatch( if (processed.productCreated) result.productsCreated++; else if (processed.productUpdated) result.productsUpdated++; + if ( + processed.productId && + (processed.productCreated || + processed.productUpdated || + processed.variantsCreated > 0 || + processed.variantsUpdated > 0) + ) { + affectedProductIds.add(processed.productId); + } + // Track secondary integration skips if (processed.productSkippedNoMatch) result.productsSkippedNoMatch++; result.variantsSkippedNoMatch += processed.variantsSkippedNoMatch; @@ -542,6 +563,7 @@ async function processBatch( } } result.timing.compute = Date.now() - computeStart; + result.affectedProductIds = Array.from(affectedProductIds); // PHASE 4: Execute all batch operations (PARALLELIZED where possible) // Note: Database-level throttling in realtime.broadcast_domain_changes() ensures diff --git a/packages/integrations/src/types.ts b/packages/integrations/src/types.ts index 568ae2d4..8a3dccf7 100644 --- a/packages/integrations/src/types.ts +++ b/packages/integrations/src/types.ts @@ -183,6 +183,11 @@ export interface SyncResult { * Primary integrations create new variants instead of skipping. */ variantsSkippedNoMatch: number; + /** + * Product IDs whose passport inputs changed during this sync run. + * Used by downstream jobs to mark published passports dirty. + */ + affectedProductIds?: string[]; // Errors errors: SyncError[]; } diff --git a/packages/jobs/__tests__/unit/catalog-fan-out/product-resolution.test.ts b/packages/jobs/__tests__/unit/catalog-fan-out/product-resolution.test.ts deleted file mode 100644 index 1d65e927..00000000 --- a/packages/jobs/__tests__/unit/catalog-fan-out/product-resolution.test.ts +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Unit Tests: Catalog Fan-Out Product Resolution - * - * Verifies how the fan-out job chooses affected product IDs before publishing. - */ - -import { describe, expect, it, mock } from "bun:test"; -import { resolveCatalogFanOutProductIds } from "../../../src/trigger/catalog/fan-out"; - -describe("resolveCatalogFanOutProductIds", () => { - it("prefers product IDs supplied in the payload", async () => { - // Skip entity lookups when the API already captured the affected products. - const manufacturerResolver = mock(async () => { - throw new Error( - "Manufacturer resolver should not run when productIds are provided", - ); - }); - - const result = await resolveCatalogFanOutProductIds( - { - brandId: "brand_1", - entityType: "manufacturer", - entityId: "manufacturer_1", - productIds: ["product_1", "product_2", "product_1"], - }, - { - findPublishedProductIdsByCertification: mock(async () => []), - findPublishedProductIdsByManufacturer: manufacturerResolver as any, - findPublishedProductIdsByMaterial: mock(async () => []), - findPublishedProductIdsByOperator: mock(async () => []), - }, - ); - - expect(result).toEqual(["product_1", "product_2"]); - expect(manufacturerResolver).toHaveBeenCalledTimes(0); - }); - - it("falls back to the entity resolver when payload product IDs are absent", async () => { - // Delegate to the entity-specific resolver when no pre-delete IDs are available. - let seenBrandId: string | null = null; - let seenEntityId: string | null = null; - - const manufacturerResolver = mock( - async (_db: unknown, brandId: string, entityId: string) => { - seenBrandId = brandId; - seenEntityId = entityId; - return ["product_3"]; - }, - ); - - const result = await resolveCatalogFanOutProductIds( - { - brandId: "brand_2", - entityType: "manufacturer", - entityId: "manufacturer_2", - }, - { - findPublishedProductIdsByCertification: mock(async () => []), - findPublishedProductIdsByManufacturer: manufacturerResolver, - findPublishedProductIdsByMaterial: mock(async () => []), - findPublishedProductIdsByOperator: mock(async () => []), - }, - ); - - expect(result).toEqual(["product_3"]); - expect(manufacturerResolver).toHaveBeenCalledTimes(1); - expect(seenBrandId ?? "").toBe("brand_2"); - expect(seenEntityId ?? "").toBe("manufacturer_2"); - }); -}); diff --git a/packages/jobs/package.json b/packages/jobs/package.json index de30c41d..b11141b0 100644 --- a/packages/jobs/package.json +++ b/packages/jobs/package.json @@ -43,6 +43,6 @@ "./trigger/integrations": "./src/trigger/integrations/index.ts", "./trigger/integrations/sync": "./src/trigger/integrations/sync.ts", "./trigger/integrations/scheduler": "./src/trigger/integrations/scheduler.ts", - "./trigger/catalog": "./src/trigger/catalog/index.ts" + "./trigger/passports": "./src/trigger/passports/index.ts" } } diff --git a/packages/jobs/src/trigger/bulk/commit-to-production.ts b/packages/jobs/src/trigger/bulk/commit-to-production.ts index 55bb349d..78a2ca7e 100644 --- a/packages/jobs/src/trigger/bulk/commit-to-production.ts +++ b/packages/jobs/src/trigger/bulk/commit-to-production.ts @@ -32,7 +32,7 @@ import { import { publishNotificationEvent } from "@v1/db/queries/notifications"; import { generateGloballyUniqueUpids, - publishProductsSetBased, + markPassportsDirtyByProductIds, } from "@v1/db/queries/products"; import * as schema from "@v1/db/schema"; import { sendBulkBroadcast } from "@v1/db/utils"; @@ -234,9 +234,6 @@ const IMAGE_CONCURRENCY = 15; /** Image upload timeout in ms */ const IMAGE_TIMEOUT_MS = 60_000; -/** Variant publish chunk size for set-based publishing */ -const PUBLISH_VARIANT_CHUNK_SIZE = 500; - /** Email from address */ const EMAIL_FROM = "Avelero "; @@ -370,15 +367,12 @@ export const commitToProduction = task({ try { await batchExecuteProductionOps(db, ops); - // PHASE 6.1: Publish immutable snapshots for products in published state. - // Uses set-based operations (no per-product publish loop). - await publishProductsSetBased(db, { + // PHASE 6.1: Mark published passports dirty instead of materializing snapshots here. + await markPassportsDirtyByProductIds( + db, brandId, - productIds: batchData.map( - (product) => product.existingProductId ?? product.id, - ), - variantChunkSize: PUBLISH_VARIANT_CHUNK_SIZE, - }); + batchData.map((product) => product.existingProductId ?? product.id), + ); // Accumulate stats totalStats.productsCreated += batchStats.productsCreated; diff --git a/packages/jobs/src/trigger/catalog/fan-out.ts b/packages/jobs/src/trigger/catalog/fan-out.ts deleted file mode 100644 index 2004d443..00000000 --- a/packages/jobs/src/trigger/catalog/fan-out.ts +++ /dev/null @@ -1,195 +0,0 @@ -/** - * Catalog Fan-Out Task - * - * Republishes DPP snapshots for all published products affected by a catalog - * entity change (manufacturer, material, certification, or operator). - * - * Each run is triggered with a 45-second delay from the API. Multiple triggers - * within the same window create multiple delayed runs, but publish is - * content-hash-deduplicated so only genuine content changes produce new versions - * — redundant runs are fast no-ops (versionsSkippedUnchanged will be high). - * - * Effective refresh latency: 45–90 seconds from the triggering edit. - */ - -import "../configure-trigger"; -import { logger, task } from "@trigger.dev/sdk/v3"; -import { serviceDb as db } from "@v1/db/client"; -import { eq, inArray } from "@v1/db/index"; -import { - findPublishedProductIdsByCertification, - findPublishedProductIdsByManufacturer, - findPublishedProductIdsByMaterial, - findPublishedProductIdsByOperator, - publishProductsSetBased, -} from "@v1/db/queries/products"; -import { productVariants, products } from "@v1/db/schema"; -import { and } from "drizzle-orm"; -import { - revalidateBarcodes, - revalidatePassports, -} from "../../lib/dpp-revalidation"; - -// ============================================================================= -// TYPES -// ============================================================================= - -export type CatalogEntityType = - | "manufacturer" - | "material" - | "certification" - | "operator"; - -export interface CatalogFanOutPayload { - brandId: string; - entityType: CatalogEntityType; - entityId: string; - productIds?: string[]; -} - -type CatalogFanOutProductResolvers = { - findPublishedProductIdsByCertification: typeof findPublishedProductIdsByCertification; - findPublishedProductIdsByManufacturer: typeof findPublishedProductIdsByManufacturer; - findPublishedProductIdsByMaterial: typeof findPublishedProductIdsByMaterial; - findPublishedProductIdsByOperator: typeof findPublishedProductIdsByOperator; -}; - -const defaultCatalogFanOutResolvers: CatalogFanOutProductResolvers = { - findPublishedProductIdsByCertification, - findPublishedProductIdsByManufacturer, - findPublishedProductIdsByMaterial, - findPublishedProductIdsByOperator, -}; - -// ============================================================================= -// TASK -// ============================================================================= - -/** - * Resolve the published product IDs for a catalog fan-out run. - */ -export async function resolveCatalogFanOutProductIds( - payload: CatalogFanOutPayload, - resolvers: CatalogFanOutProductResolvers = defaultCatalogFanOutResolvers, -): Promise { - // Prefer pre-delete product IDs captured by the API before FK nullification. - if (payload.productIds) { - return Array.from(new Set(payload.productIds)); - } - - switch (payload.entityType) { - case "manufacturer": - return resolvers.findPublishedProductIdsByManufacturer( - db, - payload.brandId, - payload.entityId, - ); - case "material": - return resolvers.findPublishedProductIdsByMaterial( - db, - payload.brandId, - payload.entityId, - ); - case "certification": - return resolvers.findPublishedProductIdsByCertification( - db, - payload.brandId, - payload.entityId, - ); - case "operator": - return resolvers.findPublishedProductIdsByOperator( - db, - payload.brandId, - payload.entityId, - ); - default: - logger.warn("Unknown entity type, skipping", { - entityType: payload.entityType, - }); - return []; - } -} - -export const catalogFanOut = task({ - id: "catalog-fan-out", - // A per-brand concurrency key plus a queue limit of 1 prevents overlapping - // fan-outs for the same brand from stepping on each other's snapshot writes. - queue: { - name: "catalog-fan-out", - concurrencyLimit: 1, - }, - run: async (payload: CatalogFanOutPayload) => { - // Resolve affected products, preferring any pre-delete IDs from the payload. - const { brandId, entityType, entityId } = payload; - - logger.info("Starting catalog fan-out", { brandId, entityType, entityId }); - - const productIds = await resolveCatalogFanOutProductIds(payload); - - logger.info("Resolved affected products", { - entityType, - entityId, - productCount: productIds.length, - resolvedFromPayload: Boolean(payload.productIds), - }); - - if (productIds.length === 0) { - logger.info("No published products affected, skipping publish"); - return { - productCount: 0, - versionsCreated: 0, - versionsSkippedUnchanged: 0, - }; - } - - // Step 2: Collect UPIDs and barcodes for cache revalidation after publish. - // We query before publishing so we have the identifiers even if publish - // produces no new versions (content-hash deduplication). - const variantRows = await db - .select({ - upid: productVariants.upid, - barcode: productVariants.barcode, - }) - .from(productVariants) - .innerJoin(products, eq(products.id, productVariants.productId)) - .where( - and( - eq(products.brandId, brandId), - inArray(productVariants.productId, productIds), - ), - ); - - const upids = variantRows - .map((r) => r.upid) - .filter((u): u is string => u !== null && u.trim().length > 0); - - const barcodes = variantRows - .map((r) => r.barcode) - .filter((b): b is string => b !== null && b.trim().length > 0); - - // Step 3: Republish snapshots for all affected products. - const publishResult = await publishProductsSetBased(db, { - brandId, - productIds, - }); - - logger.info("Fan-out publish complete", { - entityType, - entityId, - ...publishResult, - }); - - // Step 4: Revalidate DPP cache for affected passports and barcodes. - // Fire-and-forget: revalidation failures don't affect publish correctness. - await Promise.allSettled([ - revalidatePassports(upids), - revalidateBarcodes(brandId, barcodes), - ]); - - return { - productCount: productIds.length, - versionsCreated: publishResult.versionsCreated, - versionsSkippedUnchanged: publishResult.versionsSkippedUnchanged, - }; - }, -}); diff --git a/packages/jobs/src/trigger/catalog/index.ts b/packages/jobs/src/trigger/catalog/index.ts deleted file mode 100644 index 73e5b8c0..00000000 --- a/packages/jobs/src/trigger/catalog/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { catalogFanOut } from "./fan-out"; -export type { CatalogEntityType, CatalogFanOutPayload } from "./fan-out"; diff --git a/packages/jobs/src/trigger/index.ts b/packages/jobs/src/trigger/index.ts index b85b2104..8f6d5d17 100644 --- a/packages/jobs/src/trigger/index.ts +++ b/packages/jobs/src/trigger/index.ts @@ -16,5 +16,5 @@ export { syncIntegration, integrationSyncScheduler } from "./integrations"; // Certification expiry reminder task export { certificationExpiryReminder } from "./certification-expiry-reminder"; -// Catalog fan-out task -export { catalogFanOut } from "./catalog"; +// Passport projector task. +export { compressPassportVersions, passportProjector } from "./passports"; diff --git a/packages/jobs/src/trigger/integrations/sync.ts b/packages/jobs/src/trigger/integrations/sync.ts index 7ee5bb8d..67b7c699 100644 --- a/packages/jobs/src/trigger/integrations/sync.ts +++ b/packages/jobs/src/trigger/integrations/sync.ts @@ -19,6 +19,7 @@ import { updateBrandIntegration, updateSyncJob, } from "@v1/db/queries/integrations"; +import { markPassportsDirtyByProductIds } from "@v1/db/queries/products"; import { decryptCredentials } from "@v1/db/utils"; import { type FieldConfig, @@ -233,6 +234,19 @@ export const syncIntegration = task({ errorCount: result.errors.length, }); + if ((result.affectedProductIds?.length ?? 0) > 0) { + // Mark published passports dirty for any products touched by the sync run. + await markPassportsDirtyByProductIds( + db, + brandId, + result.affectedProductIds ?? [], + ); + + logger.info("Marked synced passports dirty", { + productCount: result.affectedProductIds?.length ?? 0, + }); + } + // Update sync job with results await updateSyncJob(db, syncJobId, { status: result.success ? "completed" : "failed", diff --git a/packages/jobs/src/trigger/passports/compress-versions-job.ts b/packages/jobs/src/trigger/passports/compress-versions-job.ts new file mode 100644 index 00000000..f553b6f6 --- /dev/null +++ b/packages/jobs/src/trigger/passports/compress-versions-job.ts @@ -0,0 +1,71 @@ +/** + * Passport Version Compression Job + * + * Compresses superseded historical passport versions into zstd-compressed + * bytea storage while leaving current versions untouched. + */ + +import "../configure-trigger"; +import { logger, schedules } from "@trigger.dev/sdk/v3"; +import { serviceDb as db } from "@v1/db/client"; +import { batchCompressSupersededVersions } from "@v1/db/queries/products"; + +const DEFAULT_COMPRESSION_CRON = "0 3 * * *"; +const DEFAULT_BATCH_SIZE = 500; + +/** + * Scheduled historical passport version compression task. + */ +export const compressPassportVersions = schedules.task({ + id: "compress-passport-versions", + // Run daily during a low-traffic window, unless overridden by env. + cron: + process.env.PASSPORT_VERSION_COMPRESSION_CRON ?? DEFAULT_COMPRESSION_CRON, + queue: { + name: "compress-passport-versions", + concurrencyLimit: 1, + }, + run: async () => { + const limit = Math.max( + 1, + Number.parseInt( + process.env.PASSPORT_VERSION_COMPRESSION_BATCH_SIZE ?? "", + 10, + ) || DEFAULT_BATCH_SIZE, + ); + + logger.info("Starting passport version compression run", { limit }); + + let scanned = 0; + let compressed = 0; + let skipped = 0; + let batches = 0; + + while (true) { + // Continue until the batch query reports there are no superseded versions left. + const result = await batchCompressSupersededVersions(db, { limit }); + batches++; + scanned += result.scanned; + compressed += result.compressed; + skipped += result.skipped; + + if (result.scanned === 0 || result.compressed === 0) { + break; + } + } + + logger.info("Passport version compression run complete", { + batches, + scanned, + compressed, + skipped, + }); + + return { + batches, + scanned, + compressed, + skipped, + }; + }, +}); diff --git a/packages/jobs/src/trigger/passports/index.ts b/packages/jobs/src/trigger/passports/index.ts new file mode 100644 index 00000000..43d36da2 --- /dev/null +++ b/packages/jobs/src/trigger/passports/index.ts @@ -0,0 +1,8 @@ +/** + * Passport projector tasks. + * + * Groups the background jobs that materialize and maintain passport versions. + */ + +export { passportProjector } from "./projector-job"; +export { compressPassportVersions } from "./compress-versions-job"; diff --git a/packages/jobs/src/trigger/passports/projector-job.ts b/packages/jobs/src/trigger/passports/projector-job.ts new file mode 100644 index 00000000..2a4782a4 --- /dev/null +++ b/packages/jobs/src/trigger/passports/projector-job.ts @@ -0,0 +1,56 @@ +/** + * Passport Projector Job + * + * Runs on a schedule to materialize dirty passports in the background and + * revalidate the public cache for any affected identifiers. + */ + +import "../configure-trigger"; +import { logger, schedules } from "@trigger.dev/sdk/v3"; +import { serviceDb as db } from "@v1/db/client"; +import { projectDirtyPassportsAllBrands } from "@v1/db/queries/products"; +import { + revalidateBarcodes, + revalidatePassports, +} from "../../lib/dpp-revalidation"; + +const DEFAULT_PROJECTOR_CRON = "0 * * * *"; + +/** + * Scheduled passport projector task. + */ +export const passportProjector = schedules.task({ + id: "passport-projector", + // Allow teams to override the schedule without code changes. + cron: process.env.PASSPORT_PROJECTOR_CRON ?? DEFAULT_PROJECTOR_CRON, + queue: { + name: "passport-projector", + concurrencyLimit: 1, + }, + run: async () => { + logger.info("Starting passport projector run"); + + const result = await projectDirtyPassportsAllBrands(db); + + for (const brandResult of result.brands) { + // Revalidate the public pages for every brand that materialized passports. + await Promise.allSettled([ + revalidatePassports(brandResult.upids), + revalidateBarcodes(brandResult.brandId, brandResult.barcodes), + ]); + } + + logger.info("Passport projector run complete", { + brandsProcessed: result.brandsProcessed, + totalProductsProjected: result.totalProductsProjected, + totalDirtyPassportsRequested: result.totalDirtyPassportsRequested, + totalPassportsProjected: result.totalPassportsProjected, + versionsCreated: result.versionsCreated, + versionsSkippedUnchanged: result.versionsSkippedUnchanged, + dirtyFlagsCleared: result.dirtyFlagsCleared, + firstPublishedSet: result.firstPublishedSet, + }); + + return result; + }, +}); diff --git a/packages/jobs/trigger.config.ts b/packages/jobs/trigger.config.ts index d3218286..9995f8d6 100644 --- a/packages/jobs/trigger.config.ts +++ b/packages/jobs/trigger.config.ts @@ -9,6 +9,7 @@ import type { TriggerConfig } from "@trigger.dev/sdk/v3"; export const config: TriggerConfig = { project: "proj_mqxiyipljbptdmfeivig", + runtime: "node-22", logLevel: "log", maxDuration: 60, retries: { diff --git a/packages/supabase/src/types/db.ts b/packages/supabase/src/types/db.ts index 0a6a8aa5..014021ff 100644 --- a/packages/supabase/src/types/db.ts +++ b/packages/supabase/src/types/db.ts @@ -1996,8 +1996,10 @@ export type Database = { }; product_passport_versions: { Row: { + compressed_at: string | null; + compressed_snapshot: string | null; content_hash: string; - data_snapshot: Json; + data_snapshot: Json | null; id: string; passport_id: string; published_at: string; @@ -2005,8 +2007,10 @@ export type Database = { version_number: number; }; Insert: { + compressed_at?: string | null; + compressed_snapshot?: string | null; content_hash: string; - data_snapshot: Json; + data_snapshot?: Json | null; id?: string; passport_id: string; published_at?: string; @@ -2014,8 +2018,10 @@ export type Database = { version_number: number; }; Update: { + compressed_at?: string | null; + compressed_snapshot?: string | null; content_hash?: string; - data_snapshot?: Json; + data_snapshot?: Json | null; id?: string; passport_id?: string; published_at?: string; @@ -2038,7 +2044,8 @@ export type Database = { brand_id: string | null; created_at: string; current_version_id: string | null; - first_published_at: string; + dirty: boolean; + first_published_at: string | null; id: string; orphaned_at: string | null; sku: string | null; @@ -2052,7 +2059,8 @@ export type Database = { brand_id?: string | null; created_at?: string; current_version_id?: string | null; - first_published_at: string; + dirty?: boolean; + first_published_at?: string | null; id?: string; orphaned_at?: string | null; sku?: string | null; @@ -2066,7 +2074,8 @@ export type Database = { brand_id?: string | null; created_at?: string; current_version_id?: string | null; - first_published_at?: string; + dirty?: boolean; + first_published_at?: string | null; id?: string; orphaned_at?: string | null; sku?: string | null;