From 7be79f48407fbb327b0ebf36f10bc7d7ccecd8c0 Mon Sep 17 00:00:00 2001 From: FragginWagon Date: Tue, 8 Sep 2026 22:00:18 -0400 Subject: [PATCH] feat: persist image derivative jobs --- apps/api/src/app/server.js | 3 + .../api/src/infrastructure/database/schema.js | 16 + .../image-derivative-job-repository.js | 20 + .../services/inventory-image-service.js | 8 +- .../inventory-image-service.test.js | 9 + .../migrations/0010_plain_charles_xavier.sql | 198 +++ database/migrations/meta/0010_snapshot.json | 1513 +++++++++++++++++ database/migrations/meta/_journal.json | 7 + 8 files changed, 1773 insertions(+), 1 deletion(-) create mode 100644 apps/api/src/modules/inventory/repositories/image-derivative-job-repository.js create mode 100644 database/migrations/0010_plain_charles_xavier.sql create mode 100644 database/migrations/meta/0010_snapshot.json diff --git a/apps/api/src/app/server.js b/apps/api/src/app/server.js index 149d018..1469cff 100644 --- a/apps/api/src/app/server.js +++ b/apps/api/src/app/server.js @@ -22,6 +22,7 @@ import { createInventoryItemService } from '../modules/inventory/services/invent import { createPublicInventoryService } from '../modules/inventory/services/public-inventory-service.js'; import { createPublicImageService } from '../modules/inventory/services/public-image-service.js'; import { createInventoryImageRepository } from '../modules/inventory/repositories/inventory-image-repository.js'; +import { createImageDerivativeJobRepository } from '../modules/inventory/repositories/image-derivative-job-repository.js'; import { createInventoryImageService } from '../modules/inventory/services/inventory-image-service.js'; import { createLocalImageStorage } from '../infrastructure/images/local-image-storage.js'; import { createConditionRepository } from '../modules/inventory/repositories/condition-repository.js'; @@ -51,6 +52,7 @@ async function startServer() { const productRepository = createProductRepository(database.db); const inventoryItemRepository = createInventoryItemRepository(database.db); const inventoryImageRepository = createInventoryImageRepository(database.db); + const imageDerivativeJobRepository = createImageDerivativeJobRepository(database.db); const listingRepository = createListingRepository(database.db); const sellerRepository = createSellerRepository(database.db); const conditionRepository = createConditionRepository(database.db); @@ -104,6 +106,7 @@ async function startServer() { publicInventoryService: createPublicInventoryService({ inventoryItemRepository, publicImageService }), inventoryImageService: createInventoryImageService({ imageStorage: createLocalImageStorage(process.env.IMAGE_STORAGE_ROOT ?? '.local/image-storage'), + imageDerivativeJobRepository, inventoryImageRepository, }), listingService: createListingService({ listingRepository, inventoryImageRepository }), diff --git a/apps/api/src/infrastructure/database/schema.js b/apps/api/src/infrastructure/database/schema.js index 6dd5515..d812ab4 100644 --- a/apps/api/src/infrastructure/database/schema.js +++ b/apps/api/src/infrastructure/database/schema.js @@ -264,6 +264,22 @@ export const inventoryImages = pgTable( (table) => [index('inventory_images_inventory_item_id_index').on(table.inventoryItemId)], ); +export const imageDerivativeJobs = pgTable( + 'image_derivative_jobs', + { + id: uuid('id').defaultRandom().primaryKey(), + inventoryImageId: uuid('inventory_image_id') + .notNull() + .references(() => inventoryImages.id), + sourceKey: text('source_key').notNull(), + status: text('status').default('PENDING').notNull(), + derivatives: jsonb('derivatives').default([]).notNull(), + error: text('error'), + ...timestamps, + }, + (table) => [index('image_derivative_jobs_image_id_index').on(table.inventoryImageId)], +); + export const listings = pgTable('listings', { id: uuid('id').defaultRandom().primaryKey(), inventoryItemId: uuid('inventory_item_id') diff --git a/apps/api/src/modules/inventory/repositories/image-derivative-job-repository.js b/apps/api/src/modules/inventory/repositories/image-derivative-job-repository.js new file mode 100644 index 0000000..074f3f4 --- /dev/null +++ b/apps/api/src/modules/inventory/repositories/image-derivative-job-repository.js @@ -0,0 +1,20 @@ +import { asc, eq } from 'drizzle-orm'; + +import { imageDerivativeJobs } from '../../../infrastructure/database/schema.js'; + +export function createImageDerivativeJobRepository(db) { + return { + async create(job) { + const [createdJob] = await db.insert(imageDerivativeJobs).values(job).returning(); + return createdJob; + }, + + async listByImageId(inventoryImageId) { + return db + .select() + .from(imageDerivativeJobs) + .where(eq(imageDerivativeJobs.inventoryImageId, inventoryImageId)) + .orderBy(asc(imageDerivativeJobs.createdAt)); + }, + }; +} \ No newline at end of file diff --git a/apps/api/src/modules/inventory/services/inventory-image-service.js b/apps/api/src/modules/inventory/services/inventory-image-service.js index 2bc6415..ca75443 100644 --- a/apps/api/src/modules/inventory/services/inventory-image-service.js +++ b/apps/api/src/modules/inventory/services/inventory-image-service.js @@ -1,7 +1,7 @@ import { createApplicationError } from '../../../shared/errors/application-error.js'; import { validateImageSignature, validateInventoryImageMetadata } from '../domain/inventory-image.js'; -export function createInventoryImageService({ inventoryImageRepository, imageStorage }) { +export function createInventoryImageService({ inventoryImageRepository, imageDerivativeJobRepository, imageStorage }) { return { async create(input) { validateInventoryImageMetadata(input); @@ -14,6 +14,12 @@ export function createInventoryImageService({ inventoryImageRepository, imageSto throw createApplicationError('INVENTORY_ITEM_NOT_FOUND', 'Inventory item was not found.', 404); } + await imageDerivativeJobRepository?.create({ + inventoryImageId: image.id, + sourceKey: image.storageKey, + status: 'PENDING', + }); + return image; }, diff --git a/apps/api/tests/application/inventory-image-service.test.js b/apps/api/tests/application/inventory-image-service.test.js index 5b8151f..2830d28 100644 --- a/apps/api/tests/application/inventory-image-service.test.js +++ b/apps/api/tests/application/inventory-image-service.test.js @@ -17,6 +17,7 @@ const input = { test('stores validated upload bytes before persisting image metadata', async () => { const stored = []; + const jobs = []; const service = createInventoryImageService({ imageStorage: { async put(value) { stored.push(value); }, @@ -27,6 +28,9 @@ test('stores validated upload bytes before persisting image metadata', async () return { id: 'image-1', ...image }; }, }, + imageDerivativeJobRepository: { + async create(job) { jobs.push(job); }, + }, }); const image = await service.upload(input); @@ -36,6 +40,11 @@ test('stores validated upload bytes before persisting image metadata', async () assert.equal(image.id, 'image-1'); assert.equal(image.processingStatus, 'PENDING'); assert.equal('data' in image, false); + assert.deepEqual(jobs, [{ + inventoryImageId: 'image-1', + sourceKey: input.storageKey, + status: 'PENDING', + }]); }); test('removes stored bytes when metadata persistence fails', async () => { diff --git a/database/migrations/0010_plain_charles_xavier.sql b/database/migrations/0010_plain_charles_xavier.sql new file mode 100644 index 0000000..8fd2c8c --- /dev/null +++ b/database/migrations/0010_plain_charles_xavier.sql @@ -0,0 +1,198 @@ +CREATE TYPE "public"."inventory_status" AS ENUM('ACTIVE', 'SOLD', 'ARCHIVED');--> statement-breakpoint +CREATE TYPE "public"."listing_status" AS ENUM('DRAFT', 'AVAILABLE', 'PENDING', 'CLOSED', 'ARCHIVED');--> statement-breakpoint +CREATE TYPE "public"."user_role" AS ENUM('ADMIN');--> statement-breakpoint +CREATE TABLE "categories" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "name" text NOT NULL, + "slug" text NOT NULL, + "active" boolean DEFAULT true NOT NULL, + "sort" integer DEFAULT 0 NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "condition_profiles" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "category_id" uuid NOT NULL, + "name" text NOT NULL, + "active" boolean DEFAULT true NOT NULL, + "sort" integer DEFAULT 0 NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "conditions" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "condition_profile_id" uuid NOT NULL, + "name" text NOT NULL, + "code" text NOT NULL, + "quality_rank" integer NOT NULL, + "description" text, + "active" boolean DEFAULT true NOT NULL, + "sort" integer DEFAULT 0 NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "image_derivative_jobs" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "inventory_image_id" uuid NOT NULL, + "source_key" text NOT NULL, + "status" text DEFAULT 'PENDING' NOT NULL, + "derivatives" jsonb DEFAULT '[]'::jsonb NOT NULL, + "error" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "inventory_images" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "inventory_item_id" uuid NOT NULL, + "storage_key" text NOT NULL, + "original_filename" text NOT NULL, + "mime_type" text NOT NULL, + "file_size" integer NOT NULL, + "width" integer NOT NULL, + "height" integer NOT NULL, + "alt_text" text, + "sort_order" integer DEFAULT 0 NOT NULL, + "is_primary" boolean DEFAULT false NOT NULL, + "processing_status" text DEFAULT 'PENDING' NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "inventory_items" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "product_id" uuid NOT NULL, + "condition_id" uuid NOT NULL, + "status" "inventory_status" DEFAULT 'ACTIVE' NOT NULL, + "notes" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "listings" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "inventory_item_id" uuid NOT NULL, + "product_name" text NOT NULL, + "category_id" uuid NOT NULL, + "condition_id" uuid NOT NULL, + "asking_price_minor" integer NOT NULL, + "status" "listing_status" DEFAULT 'DRAFT' NOT NULL, + "notes" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "mfa_methods" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" uuid NOT NULL, + "type" text NOT NULL, + "secret_ciphertext" text NOT NULL, + "name" text NOT NULL, + "is_enabled" boolean DEFAULT false NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "last_used_at" timestamp with time zone, + "disabled_at" timestamp with time zone +); +--> statement-breakpoint +CREATE TABLE "password_reset_tokens" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" uuid NOT NULL, + "token_hash" text NOT NULL, + "expires_at" timestamp with time zone NOT NULL, + "used_at" timestamp with time zone, + "created_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "products" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "seller_id" uuid NOT NULL, + "category_id" uuid NOT NULL, + "name" text NOT NULL, + "slug" text NOT NULL, + "description" text, + "brand" text, + "set_name" text, + "card_number" text, + "event_name" text, + "release_year" integer, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "products_release_year_valid" CHECK ("products"."release_year" IS NULL OR "products"."release_year" BETWEEN 1996 AND 9999) +); +--> statement-breakpoint +CREATE TABLE "security_audit_events" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" uuid, + "event" text NOT NULL, + "ip_address" text, + "user_agent" text, + "metadata" jsonb DEFAULT '{}'::jsonb NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "sellers" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" uuid NOT NULL, + "display_name" text NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "sessions" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" uuid NOT NULL, + "session_token_hash" text NOT NULL, + "csrf_token_hash" text NOT NULL, + "expires_at" timestamp with time zone NOT NULL, + "last_activity_at" timestamp with time zone DEFAULT now() NOT NULL, + "ip_address" text, + "user_agent" text, + "revoked_at" timestamp with time zone, + "created_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "users" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "email" text NOT NULL, + "password_hash" text NOT NULL, + "role" "user_role" DEFAULT 'ADMIN' NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +ALTER TABLE "condition_profiles" ADD CONSTRAINT "condition_profiles_category_id_categories_id_fk" FOREIGN KEY ("category_id") REFERENCES "public"."categories"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "conditions" ADD CONSTRAINT "conditions_condition_profile_id_condition_profiles_id_fk" FOREIGN KEY ("condition_profile_id") REFERENCES "public"."condition_profiles"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "image_derivative_jobs" ADD CONSTRAINT "image_derivative_jobs_inventory_image_id_inventory_images_id_fk" FOREIGN KEY ("inventory_image_id") REFERENCES "public"."inventory_images"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "inventory_images" ADD CONSTRAINT "inventory_images_inventory_item_id_inventory_items_id_fk" FOREIGN KEY ("inventory_item_id") REFERENCES "public"."inventory_items"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "inventory_items" ADD CONSTRAINT "inventory_items_product_id_products_id_fk" FOREIGN KEY ("product_id") REFERENCES "public"."products"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "inventory_items" ADD CONSTRAINT "inventory_items_condition_id_conditions_id_fk" FOREIGN KEY ("condition_id") REFERENCES "public"."conditions"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "listings" ADD CONSTRAINT "listings_inventory_item_id_inventory_items_id_fk" FOREIGN KEY ("inventory_item_id") REFERENCES "public"."inventory_items"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "listings" ADD CONSTRAINT "listings_condition_id_conditions_id_fk" FOREIGN KEY ("condition_id") REFERENCES "public"."conditions"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "mfa_methods" ADD CONSTRAINT "mfa_methods_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "password_reset_tokens" ADD CONSTRAINT "password_reset_tokens_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "products" ADD CONSTRAINT "products_seller_id_sellers_id_fk" FOREIGN KEY ("seller_id") REFERENCES "public"."sellers"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "products" ADD CONSTRAINT "products_category_id_categories_id_fk" FOREIGN KEY ("category_id") REFERENCES "public"."categories"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "security_audit_events" ADD CONSTRAINT "security_audit_events_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "sellers" ADD CONSTRAINT "sellers_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "sessions" ADD CONSTRAINT "sessions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +CREATE UNIQUE INDEX "categories_name_unique" ON "categories" USING btree ("name");--> statement-breakpoint +CREATE UNIQUE INDEX "categories_slug_unique" ON "categories" USING btree ("slug");--> statement-breakpoint +CREATE UNIQUE INDEX "conditions_profile_code_unique" ON "conditions" USING btree ("condition_profile_id","code");--> statement-breakpoint +CREATE INDEX "image_derivative_jobs_image_id_index" ON "image_derivative_jobs" USING btree ("inventory_image_id");--> statement-breakpoint +CREATE INDEX "inventory_images_inventory_item_id_index" ON "inventory_images" USING btree ("inventory_item_id");--> statement-breakpoint +CREATE INDEX "listings_inventory_item_id_index" ON "listings" USING btree ("inventory_item_id");--> statement-breakpoint +CREATE INDEX "listings_status_index" ON "listings" USING btree ("status");--> statement-breakpoint +CREATE INDEX "mfa_methods_user_id_index" ON "mfa_methods" USING btree ("user_id");--> statement-breakpoint +CREATE UNIQUE INDEX "password_reset_tokens_token_hash_unique" ON "password_reset_tokens" USING btree ("token_hash");--> statement-breakpoint +CREATE INDEX "password_reset_tokens_user_id_index" ON "password_reset_tokens" USING btree ("user_id");--> statement-breakpoint +CREATE UNIQUE INDEX "products_seller_slug_unique" ON "products" USING btree ("seller_id","slug");--> statement-breakpoint +CREATE INDEX "security_audit_events_user_id_index" ON "security_audit_events" USING btree ("user_id");--> statement-breakpoint +CREATE INDEX "security_audit_events_event_created_at_index" ON "security_audit_events" USING btree ("event","created_at");--> statement-breakpoint +CREATE UNIQUE INDEX "sellers_user_id_unique" ON "sellers" USING btree ("user_id");--> statement-breakpoint +CREATE UNIQUE INDEX "sessions_token_hash_unique" ON "sessions" USING btree ("session_token_hash");--> statement-breakpoint +CREATE INDEX "sessions_user_id_index" ON "sessions" USING btree ("user_id");--> statement-breakpoint +CREATE INDEX "sessions_expires_at_index" ON "sessions" USING btree ("expires_at");--> statement-breakpoint +CREATE UNIQUE INDEX "users_email_unique" ON "users" USING btree ("email"); \ No newline at end of file diff --git a/database/migrations/meta/0010_snapshot.json b/database/migrations/meta/0010_snapshot.json new file mode 100644 index 0000000..1350fcf --- /dev/null +++ b/database/migrations/meta/0010_snapshot.json @@ -0,0 +1,1513 @@ +{ + "id": "1d50a2de-e069-49c0-91d3-10f405ff623b", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.categories": { + "name": "categories", + "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": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "sort": { + "name": "sort", + "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": { + "categories_name_unique": { + "name": "categories_name_unique", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "categories_slug_unique": { + "name": "categories_slug_unique", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.condition_profiles": { + "name": "condition_profiles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "category_id": { + "name": "category_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "sort": { + "name": "sort", + "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": {}, + "foreignKeys": { + "condition_profiles_category_id_categories_id_fk": { + "name": "condition_profiles_category_id_categories_id_fk", + "tableFrom": "condition_profiles", + "tableTo": "categories", + "columnsFrom": [ + "category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.conditions": { + "name": "conditions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "condition_profile_id": { + "name": "condition_profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quality_rank": { + "name": "quality_rank", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "sort": { + "name": "sort", + "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": { + "conditions_profile_code_unique": { + "name": "conditions_profile_code_unique", + "columns": [ + { + "expression": "condition_profile_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "conditions_condition_profile_id_condition_profiles_id_fk": { + "name": "conditions_condition_profile_id_condition_profiles_id_fk", + "tableFrom": "conditions", + "tableTo": "condition_profiles", + "columnsFrom": [ + "condition_profile_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.image_derivative_jobs": { + "name": "image_derivative_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "inventory_image_id": { + "name": "inventory_image_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_key": { + "name": "source_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'PENDING'" + }, + "derivatives": { + "name": "derivatives", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "error": { + "name": "error", + "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": { + "image_derivative_jobs_image_id_index": { + "name": "image_derivative_jobs_image_id_index", + "columns": [ + { + "expression": "inventory_image_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "image_derivative_jobs_inventory_image_id_inventory_images_id_fk": { + "name": "image_derivative_jobs_inventory_image_id_inventory_images_id_fk", + "tableFrom": "image_derivative_jobs", + "tableTo": "inventory_images", + "columnsFrom": [ + "inventory_image_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.inventory_images": { + "name": "inventory_images", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "inventory_item_id": { + "name": "inventory_item_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "storage_key": { + "name": "storage_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "original_filename": { + "name": "original_filename", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_size": { + "name": "file_size", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "width": { + "name": "width", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "alt_text": { + "name": "alt_text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "is_primary": { + "name": "is_primary", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "processing_status": { + "name": "processing_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()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "inventory_images_inventory_item_id_index": { + "name": "inventory_images_inventory_item_id_index", + "columns": [ + { + "expression": "inventory_item_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "inventory_images_inventory_item_id_inventory_items_id_fk": { + "name": "inventory_images_inventory_item_id_inventory_items_id_fk", + "tableFrom": "inventory_images", + "tableTo": "inventory_items", + "columnsFrom": [ + "inventory_item_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.inventory_items": { + "name": "inventory_items", + "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 + }, + "condition_id": { + "name": "condition_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "inventory_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'ACTIVE'" + }, + "notes": { + "name": "notes", + "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": { + "inventory_items_product_id_products_id_fk": { + "name": "inventory_items_product_id_products_id_fk", + "tableFrom": "inventory_items", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inventory_items_condition_id_conditions_id_fk": { + "name": "inventory_items_condition_id_conditions_id_fk", + "tableFrom": "inventory_items", + "tableTo": "conditions", + "columnsFrom": [ + "condition_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.listings": { + "name": "listings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "inventory_item_id": { + "name": "inventory_item_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "product_name": { + "name": "product_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "category_id": { + "name": "category_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "condition_id": { + "name": "condition_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "asking_price_minor": { + "name": "asking_price_minor", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "listing_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'DRAFT'" + }, + "notes": { + "name": "notes", + "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": { + "listings_inventory_item_id_index": { + "name": "listings_inventory_item_id_index", + "columns": [ + { + "expression": "inventory_item_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "listings_status_index": { + "name": "listings_status_index", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "listings_inventory_item_id_inventory_items_id_fk": { + "name": "listings_inventory_item_id_inventory_items_id_fk", + "tableFrom": "listings", + "tableTo": "inventory_items", + "columnsFrom": [ + "inventory_item_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "listings_condition_id_conditions_id_fk": { + "name": "listings_condition_id_conditions_id_fk", + "tableFrom": "listings", + "tableTo": "conditions", + "columnsFrom": [ + "condition_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mfa_methods": { + "name": "mfa_methods", + "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 + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret_ciphertext": { + "name": "secret_ciphertext", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "disabled_at": { + "name": "disabled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "mfa_methods_user_id_index": { + "name": "mfa_methods_user_id_index", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "mfa_methods_user_id_users_id_fk": { + "name": "mfa_methods_user_id_users_id_fk", + "tableFrom": "mfa_methods", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.password_reset_tokens": { + "name": "password_reset_tokens", + "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 + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "used_at": { + "name": "used_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": { + "password_reset_tokens_token_hash_unique": { + "name": "password_reset_tokens_token_hash_unique", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "password_reset_tokens_user_id_index": { + "name": "password_reset_tokens_user_id_index", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "password_reset_tokens_user_id_users_id_fk": { + "name": "password_reset_tokens_user_id_users_id_fk", + "tableFrom": "password_reset_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.products": { + "name": "products", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "seller_id": { + "name": "seller_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "category_id": { + "name": "category_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "brand": { + "name": "brand", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "set_name": { + "name": "set_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "card_number": { + "name": "card_number", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "event_name": { + "name": "event_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "release_year": { + "name": "release_year", + "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": { + "products_seller_slug_unique": { + "name": "products_seller_slug_unique", + "columns": [ + { + "expression": "seller_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "products_seller_id_sellers_id_fk": { + "name": "products_seller_id_sellers_id_fk", + "tableFrom": "products", + "tableTo": "sellers", + "columnsFrom": [ + "seller_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "products_category_id_categories_id_fk": { + "name": "products_category_id_categories_id_fk", + "tableFrom": "products", + "tableTo": "categories", + "columnsFrom": [ + "category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "products_release_year_valid": { + "name": "products_release_year_valid", + "value": "\"products\".\"release_year\" IS NULL OR \"products\".\"release_year\" BETWEEN 1996 AND 9999" + } + }, + "isRLSEnabled": false + }, + "public.security_audit_events": { + "name": "security_audit_events", + "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": false + }, + "event": { + "name": "event", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "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": { + "security_audit_events_user_id_index": { + "name": "security_audit_events_user_id_index", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "security_audit_events_event_created_at_index": { + "name": "security_audit_events_event_created_at_index", + "columns": [ + { + "expression": "event", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "security_audit_events_user_id_users_id_fk": { + "name": "security_audit_events_user_id_users_id_fk", + "tableFrom": "security_audit_events", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sellers": { + "name": "sellers", + "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 + }, + "display_name": { + "name": "display_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": { + "sellers_user_id_unique": { + "name": "sellers_user_id_unique", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "sellers_user_id_users_id_fk": { + "name": "sellers_user_id_users_id_fk", + "tableFrom": "sellers", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sessions": { + "name": "sessions", + "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 + }, + "session_token_hash": { + "name": "session_token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "csrf_token_hash": { + "name": "csrf_token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_activity_at": { + "name": "last_activity_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_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": { + "sessions_token_hash_unique": { + "name": "sessions_token_hash_unique", + "columns": [ + { + "expression": "session_token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sessions_user_id_index": { + "name": "sessions_user_id_index", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sessions_expires_at_index": { + "name": "sessions_expires_at_index", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password_hash": { + "name": "password_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "user_role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'ADMIN'" + }, + "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": { + "users_email_unique": { + "name": "users_email_unique", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.inventory_status": { + "name": "inventory_status", + "schema": "public", + "values": [ + "ACTIVE", + "SOLD", + "ARCHIVED" + ] + }, + "public.listing_status": { + "name": "listing_status", + "schema": "public", + "values": [ + "DRAFT", + "AVAILABLE", + "PENDING", + "CLOSED", + "ARCHIVED" + ] + }, + "public.user_role": { + "name": "user_role", + "schema": "public", + "values": [ + "ADMIN" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/database/migrations/meta/_journal.json b/database/migrations/meta/_journal.json index 6fed042..49d5f25 100644 --- a/database/migrations/meta/_journal.json +++ b/database/migrations/meta/_journal.json @@ -71,6 +71,13 @@ "when": 1789459200000, "tag": "0009_remove_listing_image_count", "breakpoints": true + }, + { + "idx": 10, + "version": "7", + "when": 1788919152853, + "tag": "0010_plain_charles_xavier", + "breakpoints": true } ] } \ No newline at end of file