diff --git a/.frontmatter/database/taxonomyDb.json b/.frontmatter/database/taxonomyDb.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/.frontmatter/database/taxonomyDb.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/drizzle.config.ts b/drizzle.config.ts index f570da9..5c46e1a 100644 --- a/drizzle.config.ts +++ b/drizzle.config.ts @@ -3,8 +3,13 @@ import type { Config } from "drizzle-kit"; export default { schema: "./src/db/schema.ts", out: "./drizzle/migrations", - driver: "pg", + dialect: "postgresql", dbCredentials: { - connectionString: process.env.DATABASE_URL!, + host: process.env.DB_HOST!, + port: Number(process.env.DB_PORT!), + user: process.env.DB_USER!, + password: process.env.DB_PASSWORD!, + database: process.env.DB_NAME!, + ssl: false, }, } satisfies Config; \ No newline at end of file diff --git a/drizzle/migrations/0000_luxuriant_albert_cleary.sql b/drizzle/migrations/0000_luxuriant_albert_cleary.sql new file mode 100644 index 0000000..fd8c8e5 --- /dev/null +++ b/drizzle/migrations/0000_luxuriant_albert_cleary.sql @@ -0,0 +1,77 @@ +-- Current sql file was generated after introspecting the database +-- If you want to run this migration please uncomment this code before executing migrations +/* +CREATE TABLE "product_category_mappings" ( + "id" serial PRIMARY KEY NOT NULL, + "feed_name" varchar(255), + "feed_category_value" varchar(255), + "canonical_category_id" integer, + "confidence_score" double precision, + "last_reviewed_by" varchar(255), + "last_reviewed_at" timestamp +); +--> statement-breakpoint +CREATE TABLE "categories" ( + "id" serial PRIMARY KEY NOT NULL, + "name" varchar(255) NOT NULL, + "parent_id" integer, + "slug" varchar(255) NOT NULL, + CONSTRAINT "categories_slug_key" UNIQUE("slug") +); +--> statement-breakpoint +CREATE TABLE "products" ( + "id" serial PRIMARY KEY NOT NULL, + "name" varchar(255) NOT NULL, + "brand" varchar(255), + "description" text, + "upc" varchar(32), + "mpn" varchar(64), + "canonical_category_id" integer, + "created_at" timestamp DEFAULT now(), + "updated_at" timestamp DEFAULT now() +); +--> statement-breakpoint +CREATE TABLE "offers" ( + "id" serial PRIMARY KEY NOT NULL, + "product_id" integer, + "feed_name" varchar(255) NOT NULL, + "feed_sku" varchar(255), + "price" numeric(10, 2), + "url" text, + "in_stock" boolean, + "vendor" varchar(255), + "last_seen_at" timestamp DEFAULT now(), + "raw_data" jsonb, + CONSTRAINT "offers_product_id_feed_name_feed_sku_key" UNIQUE("product_id","feed_name","feed_sku"), + CONSTRAINT "offers_feed_unique" UNIQUE("feed_name","feed_sku") +); +--> statement-breakpoint +CREATE TABLE "offer_price_history" ( + "id" serial PRIMARY KEY NOT NULL, + "offer_id" integer, + "price" numeric(10, 2) NOT NULL, + "seen_at" timestamp DEFAULT now() +); +--> statement-breakpoint +CREATE TABLE "feeds" ( + "id" serial PRIMARY KEY NOT NULL, + "name" varchar(255) NOT NULL, + "url" text, + "last_imported_at" timestamp, + CONSTRAINT "feeds_name_key" UNIQUE("name") +); +--> statement-breakpoint +CREATE TABLE "product_attributes" ( + "id" serial PRIMARY KEY NOT NULL, + "product_id" integer, + "name" varchar(255) NOT NULL, + "value" varchar(255) NOT NULL +); +--> statement-breakpoint +ALTER TABLE "product_category_mappings" ADD CONSTRAINT "product_category_mappings_canonical_category_id_fkey" FOREIGN KEY ("canonical_category_id") REFERENCES "public"."categories"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "categories" ADD CONSTRAINT "categories_parent_id_fkey" FOREIGN KEY ("parent_id") REFERENCES "public"."categories"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "products" ADD CONSTRAINT "products_canonical_category_id_fkey" FOREIGN KEY ("canonical_category_id") REFERENCES "public"."categories"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "offers" ADD CONSTRAINT "offers_product_id_fkey" FOREIGN KEY ("product_id") REFERENCES "public"."products"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "offer_price_history" ADD CONSTRAINT "offer_price_history_offer_id_fkey" FOREIGN KEY ("offer_id") REFERENCES "public"."offers"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "product_attributes" ADD CONSTRAINT "product_attributes_product_id_fkey" FOREIGN KEY ("product_id") REFERENCES "public"."products"("id") ON DELETE cascade ON UPDATE no action; +*/ \ No newline at end of file diff --git a/drizzle/migrations/0001_superb_umar.sql b/drizzle/migrations/0001_superb_umar.sql new file mode 100644 index 0000000..8332688 --- /dev/null +++ b/drizzle/migrations/0001_superb_umar.sql @@ -0,0 +1,7 @@ +DROP TABLE "product_category_mappings" CASCADE;--> statement-breakpoint +DROP TABLE "categories" CASCADE;--> statement-breakpoint +DROP TABLE "products" CASCADE;--> statement-breakpoint +DROP TABLE "offers" CASCADE;--> statement-breakpoint +DROP TABLE "offer_price_history" CASCADE;--> statement-breakpoint +DROP TABLE "feeds" CASCADE;--> statement-breakpoint +DROP TABLE "product_attributes" CASCADE; \ No newline at end of file diff --git a/drizzle/migrations/meta/0000_snapshot.json b/drizzle/migrations/meta/0000_snapshot.json new file mode 100644 index 0000000..a055cd2 --- /dev/null +++ b/drizzle/migrations/meta/0000_snapshot.json @@ -0,0 +1,493 @@ +{ + "id": "00000000-0000-0000-0000-000000000000", + "prevId": "", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.product_category_mappings": { + "name": "product_category_mappings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "feed_name": { + "name": "feed_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "feed_category_value": { + "name": "feed_category_value", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "canonical_category_id": { + "name": "canonical_category_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "confidence_score": { + "name": "confidence_score", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "last_reviewed_by": { + "name": "last_reviewed_by", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "last_reviewed_at": { + "name": "last_reviewed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "product_category_mappings_canonical_category_id_fkey": { + "name": "product_category_mappings_canonical_category_id_fkey", + "tableFrom": "product_category_mappings", + "tableTo": "categories", + "schemaTo": "public", + "columnsFrom": [ + "canonical_category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {}, + "policies": {}, + "isRLSEnabled": false + }, + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "slug": { + "name": "slug", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "categories_parent_id_fkey": { + "name": "categories_parent_id_fkey", + "tableFrom": "categories", + "tableTo": "categories", + "schemaTo": "public", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "categories_slug_key": { + "columns": [ + "slug" + ], + "nullsNotDistinct": false, + "name": "categories_slug_key" + } + }, + "checkConstraints": {}, + "policies": {}, + "isRLSEnabled": false + }, + "public.products": { + "name": "products", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "brand": { + "name": "brand", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "upc": { + "name": "upc", + "type": "varchar(32)", + "primaryKey": false, + "notNull": false + }, + "mpn": { + "name": "mpn", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "canonical_category_id": { + "name": "canonical_category_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "products_canonical_category_id_fkey": { + "name": "products_canonical_category_id_fkey", + "tableFrom": "products", + "tableTo": "categories", + "schemaTo": "public", + "columnsFrom": [ + "canonical_category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {}, + "policies": {}, + "isRLSEnabled": false + }, + "public.offers": { + "name": "offers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "product_id": { + "name": "product_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "feed_name": { + "name": "feed_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "feed_sku": { + "name": "feed_sku", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "in_stock": { + "name": "in_stock", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "vendor": { + "name": "vendor", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "raw_data": { + "name": "raw_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "offers_product_id_fkey": { + "name": "offers_product_id_fkey", + "tableFrom": "offers", + "tableTo": "products", + "schemaTo": "public", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "offers_product_id_feed_name_feed_sku_key": { + "columns": [ + "product_id", + "feed_name", + "feed_sku" + ], + "nullsNotDistinct": false, + "name": "offers_product_id_feed_name_feed_sku_key" + }, + "offers_feed_unique": { + "columns": [ + "feed_name", + "feed_sku" + ], + "nullsNotDistinct": false, + "name": "offers_feed_unique" + } + }, + "checkConstraints": {}, + "policies": {}, + "isRLSEnabled": false + }, + "public.offer_price_history": { + "name": "offer_price_history", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "offer_id": { + "name": "offer_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true + }, + "seen_at": { + "name": "seen_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "offer_price_history_offer_id_fkey": { + "name": "offer_price_history_offer_id_fkey", + "tableFrom": "offer_price_history", + "tableTo": "offers", + "schemaTo": "public", + "columnsFrom": [ + "offer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {}, + "policies": {}, + "isRLSEnabled": false + }, + "public.feeds": { + "name": "feeds", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_imported_at": { + "name": "last_imported_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "feeds_name_key": { + "columns": [ + "name" + ], + "nullsNotDistinct": false, + "name": "feeds_name_key" + } + }, + "checkConstraints": {}, + "policies": {}, + "isRLSEnabled": false + }, + "public.product_attributes": { + "name": "product_attributes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "product_id": { + "name": "product_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "product_attributes_product_id_fkey": { + "name": "product_attributes_product_id_fkey", + "tableFrom": "product_attributes", + "tableTo": "products", + "schemaTo": "public", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {}, + "policies": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/migrations/meta/0001_snapshot.json b/drizzle/migrations/meta/0001_snapshot.json new file mode 100644 index 0000000..7628b12 --- /dev/null +++ b/drizzle/migrations/meta/0001_snapshot.json @@ -0,0 +1,18 @@ +{ + "id": "919e511e-3cff-4bd0-b4ec-20865db2d1d3", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "7", + "dialect": "postgresql", + "tables": {}, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/migrations/meta/_journal.json b/drizzle/migrations/meta/_journal.json new file mode 100644 index 0000000..73582fd --- /dev/null +++ b/drizzle/migrations/meta/_journal.json @@ -0,0 +1,20 @@ +{ + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1751488452074, + "tag": "0000_luxuriant_albert_cleary", + "breakpoints": true + }, + { + "idx": 1, + "version": "7", + "when": 1751488491929, + "tag": "0001_superb_umar", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/drizzle/migrations/relations.ts b/drizzle/migrations/relations.ts new file mode 100644 index 0000000..2f800c4 --- /dev/null +++ b/drizzle/migrations/relations.ts @@ -0,0 +1,53 @@ +import { relations } from "drizzle-orm/relations"; +import { categories, productCategoryMappings, products, offers, offerPriceHistory, productAttributes } from "./schema"; + +export const productCategoryMappingsRelations = relations(productCategoryMappings, ({one}) => ({ + category: one(categories, { + fields: [productCategoryMappings.canonicalCategoryId], + references: [categories.id] + }), +})); + +export const categoriesRelations = relations(categories, ({one, many}) => ({ + productCategoryMappings: many(productCategoryMappings), + category: one(categories, { + fields: [categories.parentId], + references: [categories.id], + relationName: "categories_parentId_categories_id" + }), + categories: many(categories, { + relationName: "categories_parentId_categories_id" + }), + products: many(products), +})); + +export const productsRelations = relations(products, ({one, many}) => ({ + category: one(categories, { + fields: [products.canonicalCategoryId], + references: [categories.id] + }), + offers: many(offers), + productAttributes: many(productAttributes), +})); + +export const offersRelations = relations(offers, ({one, many}) => ({ + product: one(products, { + fields: [offers.productId], + references: [products.id] + }), + offerPriceHistories: many(offerPriceHistory), +})); + +export const offerPriceHistoryRelations = relations(offerPriceHistory, ({one}) => ({ + offer: one(offers, { + fields: [offerPriceHistory.offerId], + references: [offers.id] + }), +})); + +export const productAttributesRelations = relations(productAttributes, ({one}) => ({ + product: one(products, { + fields: [productAttributes.productId], + references: [products.id] + }), +})); \ No newline at end of file diff --git a/drizzle/migrations/schema.ts b/drizzle/migrations/schema.ts new file mode 100644 index 0000000..7bf1ae8 --- /dev/null +++ b/drizzle/migrations/schema.ts @@ -0,0 +1,108 @@ +import { pgTable, foreignKey, serial, varchar, integer, doublePrecision, timestamp, unique, text, numeric, boolean, jsonb } from "drizzle-orm/pg-core" +import { sql } from "drizzle-orm" + + + +export const productCategoryMappings = pgTable("product_category_mappings", { + id: serial().primaryKey().notNull(), + feedName: varchar("feed_name", { length: 255 }), + feedCategoryValue: varchar("feed_category_value", { length: 255 }), + canonicalCategoryId: integer("canonical_category_id"), + confidenceScore: doublePrecision("confidence_score"), + lastReviewedBy: varchar("last_reviewed_by", { length: 255 }), + lastReviewedAt: timestamp("last_reviewed_at", { mode: 'string' }), +}, (table) => [ + foreignKey({ + columns: [table.canonicalCategoryId], + foreignColumns: [categories.id], + name: "product_category_mappings_canonical_category_id_fkey" + }), +]); + +export const categories = pgTable("categories", { + id: serial().primaryKey().notNull(), + name: varchar({ length: 255 }).notNull(), + parentId: integer("parent_id"), + slug: varchar({ length: 255 }).notNull(), +}, (table) => [ + foreignKey({ + columns: [table.parentId], + foreignColumns: [table.id], + name: "categories_parent_id_fkey" + }), + unique("categories_slug_key").on(table.slug), +]); + +export const products = pgTable("products", { + id: serial().primaryKey().notNull(), + name: varchar({ length: 255 }).notNull(), + brand: varchar({ length: 255 }), + description: text(), + upc: varchar({ length: 32 }), + mpn: varchar({ length: 64 }), + canonicalCategoryId: integer("canonical_category_id"), + createdAt: timestamp("created_at", { mode: 'string' }).defaultNow(), + updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow(), +}, (table) => [ + foreignKey({ + columns: [table.canonicalCategoryId], + foreignColumns: [categories.id], + name: "products_canonical_category_id_fkey" + }), +]); + +export const offers = pgTable("offers", { + id: serial().primaryKey().notNull(), + productId: integer("product_id"), + feedName: varchar("feed_name", { length: 255 }).notNull(), + feedSku: varchar("feed_sku", { length: 255 }), + price: numeric({ precision: 10, scale: 2 }), + url: text(), + inStock: boolean("in_stock"), + vendor: varchar({ length: 255 }), + lastSeenAt: timestamp("last_seen_at", { mode: 'string' }).defaultNow(), + rawData: jsonb("raw_data"), +}, (table) => [ + foreignKey({ + columns: [table.productId], + foreignColumns: [products.id], + name: "offers_product_id_fkey" + }).onDelete("cascade"), + unique("offers_product_id_feed_name_feed_sku_key").on(table.productId, table.feedName, table.feedSku), + unique("offers_feed_unique").on(table.feedName, table.feedSku), +]); + +export const offerPriceHistory = pgTable("offer_price_history", { + id: serial().primaryKey().notNull(), + offerId: integer("offer_id"), + price: numeric({ precision: 10, scale: 2 }).notNull(), + seenAt: timestamp("seen_at", { mode: 'string' }).defaultNow(), +}, (table) => [ + foreignKey({ + columns: [table.offerId], + foreignColumns: [offers.id], + name: "offer_price_history_offer_id_fkey" + }).onDelete("cascade"), +]); + +export const feeds = pgTable("feeds", { + id: serial().primaryKey().notNull(), + name: varchar({ length: 255 }).notNull(), + url: text(), + lastImportedAt: timestamp("last_imported_at", { mode: 'string' }), +}, (table) => [ + unique("feeds_name_key").on(table.name), +]); + +export const productAttributes = pgTable("product_attributes", { + id: serial().primaryKey().notNull(), + productId: integer("product_id"), + name: varchar({ length: 255 }).notNull(), + value: varchar({ length: 255 }).notNull(), +}, (table) => [ + foreignKey({ + columns: [table.productId], + foreignColumns: [products.id], + name: "product_attributes_product_id_fkey" + }).onDelete("cascade"), +]); diff --git a/frontmatter.json b/frontmatter.json new file mode 100644 index 0000000..a88f78b --- /dev/null +++ b/frontmatter.json @@ -0,0 +1,52 @@ +{ + "$schema": "https://frontmatter.codes/frontmatter.schema.json", + "frontMatter.taxonomy.contentTypes": [ + { + "name": "default", + "pageBundle": false, + "previewPath": null, + "fields": [ + { + "title": "Title", + "name": "title", + "type": "string" + }, + { + "title": "Description", + "name": "description", + "type": "string" + }, + { + "title": "Publishing date", + "name": "date", + "type": "datetime", + "default": "{{now}}", + "isPublishDate": true + }, + { + "title": "Content preview", + "name": "preview", + "type": "image" + }, + { + "title": "Is in draft", + "name": "draft", + "type": "draft" + }, + { + "title": "Tags", + "name": "tags", + "type": "tags" + }, + { + "title": "Categories", + "name": "categories", + "type": "categories" + } + ] + } + ], + "frontMatter.framework.id": "next", + "frontMatter.content.publicFolder": "public", + "frontMatter.preview.host": "http://localhost:3000" +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index dad7efc..21e657b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,6 @@ "bcryptjs": "^3.0.2", "daisyui": "^4.7.3", "date-fns": "^4.1.0", - "drizzle-kit": "^0.31.4", "drizzle-orm": "^0.44.2", "lucide-react": "^0.525.0", "next": "^14.2.30", @@ -33,6 +32,7 @@ "@types/pg": "^8.15.4", "@types/react": "^18.2.0", "@types/react-dom": "^18.2.0", + "drizzle-kit": "^0.31.4", "eslint": "^9", "eslint-config-next": "15.3.4", "typescript": "^5" @@ -159,7 +159,8 @@ "node_modules/@drizzle-team/brocli": { "version": "0.10.2", "resolved": "https://registry.npmjs.org/@drizzle-team/brocli/-/brocli-0.10.2.tgz", - "integrity": "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==" + "integrity": "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==", + "dev": true }, "node_modules/@emnapi/core": { "version": "1.4.3", @@ -197,6 +198,7 @@ "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz", "integrity": "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==", "deprecated": "Merged into tsx: https://tsx.is", + "dev": true, "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" @@ -209,6 +211,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "android" @@ -224,6 +227,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "android" @@ -239,6 +243,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "android" @@ -254,6 +259,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -269,6 +275,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -284,6 +291,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "freebsd" @@ -299,6 +307,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "freebsd" @@ -314,6 +323,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -329,6 +339,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -344,6 +355,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "linux" @@ -359,6 +371,7 @@ "cpu": [ "loong64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -374,6 +387,7 @@ "cpu": [ "mips64el" ], + "dev": true, "optional": true, "os": [ "linux" @@ -389,6 +403,7 @@ "cpu": [ "ppc64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -404,6 +419,7 @@ "cpu": [ "riscv64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -419,6 +435,7 @@ "cpu": [ "s390x" ], + "dev": true, "optional": true, "os": [ "linux" @@ -434,6 +451,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -449,6 +467,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "netbsd" @@ -464,6 +483,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "openbsd" @@ -479,6 +499,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "sunos" @@ -494,6 +515,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -509,6 +531,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "win32" @@ -524,6 +547,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -536,6 +560,7 @@ "version": "0.18.20", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -573,6 +598,7 @@ "resolved": "https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.6.5.tgz", "integrity": "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==", "deprecated": "Merged into tsx: https://tsx.is", + "dev": true, "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", "get-tsconfig": "^4.7.0" @@ -585,6 +611,7 @@ "cpu": [ "ppc64" ], + "dev": true, "optional": true, "os": [ "aix" @@ -600,6 +627,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "android" @@ -615,6 +643,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "android" @@ -630,6 +659,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "android" @@ -645,6 +675,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -660,6 +691,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -675,6 +707,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "freebsd" @@ -690,6 +723,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "freebsd" @@ -705,6 +739,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -720,6 +755,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -735,6 +771,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "linux" @@ -750,6 +787,7 @@ "cpu": [ "loong64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -765,6 +803,7 @@ "cpu": [ "mips64el" ], + "dev": true, "optional": true, "os": [ "linux" @@ -780,6 +819,7 @@ "cpu": [ "ppc64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -795,6 +835,7 @@ "cpu": [ "riscv64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -810,6 +851,7 @@ "cpu": [ "s390x" ], + "dev": true, "optional": true, "os": [ "linux" @@ -825,6 +867,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -840,6 +883,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "netbsd" @@ -855,6 +899,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "netbsd" @@ -870,6 +915,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "openbsd" @@ -885,6 +931,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "openbsd" @@ -900,6 +947,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "sunos" @@ -915,6 +963,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -930,6 +979,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "win32" @@ -945,6 +995,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -2652,7 +2703,8 @@ "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true }, "node_modules/busboy": { "version": "1.6.0", @@ -2985,6 +3037,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, "dependencies": { "ms": "^2.1.3" }, @@ -3063,6 +3116,8 @@ "version": "0.31.4", "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.4.tgz", "integrity": "sha512-tCPWVZWZqWVx2XUsVpJRnH9Mx0ClVOf5YUHerZ5so1OKSlqww4zy1R5ksEdGRcO3tM3zj0PYN6V48TbQCL1RfA==", + "dev": true, + "license": "MIT", "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", @@ -3399,6 +3454,7 @@ "version": "0.25.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", + "dev": true, "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -3438,6 +3494,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz", "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==", + "dev": true, "dependencies": { "debug": "^4.3.4" }, @@ -4136,6 +4193,7 @@ "version": "4.10.1", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "dev": true, "dependencies": { "resolve-pkg-maps": "^1.0.0" }, @@ -5059,7 +5117,8 @@ "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true }, "node_modules/mz": { "version": "2.7.0", @@ -6038,6 +6097,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, "funding": { "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } @@ -6297,6 +6357,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -6313,6 +6374,7 @@ "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" diff --git a/package.json b/package.json index 7f968d2..61684a8 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,6 @@ "bcryptjs": "^3.0.2", "daisyui": "^4.7.3", "date-fns": "^4.1.0", - "drizzle-kit": "^0.31.4", "drizzle-orm": "^0.44.2", "lucide-react": "^0.525.0", "next": "^14.2.30", @@ -34,6 +33,7 @@ "@types/pg": "^8.15.4", "@types/react": "^18.2.0", "@types/react-dom": "^18.2.0", + "drizzle-kit": "^0.31.4", "eslint": "^9", "eslint-config-next": "15.3.4", "typescript": "^5" diff --git a/src/app/(main)/parts/[category]/page.tsx b/src/app/(main)/parts/[category]/page.tsx index d5eb557..0920fad 100644 --- a/src/app/(main)/parts/[category]/page.tsx +++ b/src/app/(main)/parts/[category]/page.tsx @@ -3,14 +3,12 @@ import { useEffect, useState, useMemo } from 'react'; import { useParams } from 'next/navigation'; const columns = [ - 'brandName', - 'productName', - 'department', - 'category', - 'subcategory', - 'retailPrice', - 'salePrice', - 'imageUrl', + 'name', + 'brand', + 'description', + 'slug', + 'createdAt', + 'updatedAt', ]; export default function PartsCategoryPage() { @@ -61,14 +59,14 @@ export default function PartsCategoryPage() { }); }, [products, categoryParam]); - const brandOptions = useMemo(() => Array.from(new Set(filteredByCategory.map(p => p.brandName).filter(Boolean))).sort(), [filteredByCategory]); + const brandOptions = useMemo(() => Array.from(new Set(filteredByCategory.map(p => p.brand).filter(Boolean))).sort(), [filteredByCategory]); const departmentOptions = useMemo(() => Array.from(new Set(filteredByCategory.map(p => p.department).filter(Boolean))).sort(), [filteredByCategory]); const subcategoryOptions = useMemo(() => Array.from(new Set(filteredByCategory.map(p => p.subcategory).filter(Boolean))).sort(), [filteredByCategory]); // Further filter by sidebar filters const filteredProducts = useMemo(() => { return filteredByCategory.filter(p => - (!brand || p.brandName === brand) && + (!brand || p.brand === brand) && (!department || p.department === department) && (!subcategory || p.subcategory === subcategory) ); @@ -138,19 +136,19 @@ export default function PartsCategoryPage() { ) : paginatedProducts.length === 0 ? ( No products found. ) : ( - paginatedProducts.map((product, i) => ( - - {columns.map(col => ( - - {col === 'imageUrl' && product[col] ? ( - thumb - ) : ( - product[col] ?? '' - )} - - ))} - - )) + paginatedProducts + .filter(product => columns.some(col => product[col] && String(product[col]).trim() !== '')) + .map((product, i) => ( + + {columns.map(col => ( + + {typeof product[col] === 'object' && product[col] !== null + ? JSON.stringify(product[col]) + : product[col] ?? ''} + + ))} + + )) )} diff --git a/src/app/(main)/parts/page.tsx b/src/app/(main)/parts/page.tsx index cd3da06..7b56f4d 100644 --- a/src/app/(main)/parts/page.tsx +++ b/src/app/(main)/parts/page.tsx @@ -343,7 +343,7 @@ const useCanonicalCategories = () => { const [categories, setCategories] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { - fetch('/api/product-categories') + fetch('/api/categories') .then(res => res.json()) .then(data => { setCategories(data.data); @@ -390,14 +390,12 @@ function getDescendantCategoryIds(categories: any[], selectedId: string): string } const columns = [ - 'brandName', - 'productName', - 'department', - 'category', - 'subcategory', - 'retailPrice', - 'salePrice', - 'imageUrl', + 'name', + 'brand', + 'description', + 'slug', + 'createdAt', + 'updatedAt', ]; export default function PartsPage() { @@ -413,6 +411,20 @@ export default function PartsPage() { const [category, setCategory] = useState(''); const [subcategory, setSubcategory] = useState(''); + // Category data from canonical API + const { categories, loading: categoriesLoading } = useCanonicalCategories(); + const flatCategories = useMemo(() => flattenCategories(categories), [categories]); + + // Updated filter options + const categoryOptions = useMemo( + () => flatCategories.filter(cat => cat.parentId === null).map(cat => ({ value: String(cat.id), label: cat.name })), + [flatCategories] + ); + const subcategoryOptions = useMemo( + () => flatCategories.filter(cat => cat.parentId === parseInt(category)).map(cat => ({ value: String(cat.id), label: cat.name })), + [flatCategories, category] + ); + useEffect(() => { setLoading(true); setError(null); @@ -431,16 +443,14 @@ export default function PartsPage() { // Get unique filter options from all products const brandOptions = useMemo(() => Array.from(new Set(products.map(p => p.brandName).filter(Boolean))).sort(), [products]); const departmentOptions = useMemo(() => Array.from(new Set(products.map(p => p.department).filter(Boolean))).sort(), [products]); - const categoryOptions = useMemo(() => Array.from(new Set(products.map(p => p.category).filter(Boolean))).sort(), [products]); - const subcategoryOptions = useMemo(() => Array.from(new Set(products.map(p => p.subcategory).filter(Boolean))).sort(), [products]); - // Filter products before rendering + // Filter products before rendering (match by category/subcategory ID if available) const filteredProducts = useMemo(() => { return products.filter(p => (!brand || p.brandName === brand) && (!department || p.department === department) && - (!category || p.category === category) && - (!subcategory || p.subcategory === subcategory) + (!category || String(p.categoryId) === category) && + (!subcategory || String(p.subcategoryId) === subcategory) ); }, [products, brand, department, category, subcategory]); @@ -465,25 +475,18 @@ export default function PartsPage() { {brandOptions.map(opt => )} -
- - -
- { setCategory(e.target.value); setSubcategory(''); }}> - {categoryOptions.map(opt => )} + {categoryOptions.map(opt => )}
@@ -505,19 +508,19 @@ export default function PartsPage() { ) : paginatedProducts.length === 0 ? ( No products found. ) : ( - paginatedProducts.map((product, i) => ( - - {columns.map(col => ( - - {col === 'imageUrl' && product[col] ? ( - thumb - ) : ( - product[col] ?? '' - )} - - ))} - - )) + paginatedProducts + .filter(product => columns.some(col => product[col] && String(product[col]).trim() !== '')) + .map((product, i) => ( + + {columns.map(col => ( + + {typeof product[col] === 'object' && product[col] !== null + ? JSON.stringify(product[col]) + : product[col] ?? ''} + + ))} + + )) )} diff --git a/src/app/api/products/[slug]/route.ts b/src/app/api/products/[slug]/route.ts index 5cfdf07..e51c1b4 100644 --- a/src/app/api/products/[slug]/route.ts +++ b/src/app/api/products/[slug]/route.ts @@ -1,5 +1,5 @@ import { db } from '@/db'; -import { bb_products } from '@/db/schema'; +import { products } from '@/db/schema'; function slugify(name: string): string { return name @@ -13,7 +13,7 @@ export async function GET( { params }: { params: { slug: string } } ) { try { - const allProducts = await db.select().from(bb_products); + const allProducts = await db.select().from(products); const mapped = allProducts.map((item: any) => ({ id: item.uuid, name: item.productName, diff --git a/src/app/api/products/route.ts b/src/app/api/products/route.ts index cc25942..fb4b6f1 100644 --- a/src/app/api/products/route.ts +++ b/src/app/api/products/route.ts @@ -1,5 +1,5 @@ import { db } from '@/db'; -import { bb_products } from '@/db/schema'; +import { products } from '@/db/schema'; import { NextResponse } from 'next/server'; import { sql } from 'drizzle-orm'; @@ -18,11 +18,11 @@ export async function GET(req: Request) { const offset = (page - 1) * limit; // Get total count using raw SQL - const totalResult = await db.execute(sql`SELECT COUNT(*)::int AS count FROM bb_products`); + const totalResult = await db.execute(sql`SELECT COUNT(*)::int AS count FROM products`); const total = Number(totalResult.rows?.[0]?.count || 0); // Get paginated products - const allProducts = await db.select().from(bb_products).limit(limit).offset(offset); + const allProducts = await db.select().from(products).limit(limit).offset(offset); const mapped = allProducts.map((item: any) => ({ ...item, slug: slugify(item.productName || item.product_name || item.name || ''), diff --git a/src/app/api/test-products/route.ts b/src/app/api/test-products/route.ts index 8072201..7ccad9d 100644 --- a/src/app/api/test-products/route.ts +++ b/src/app/api/test-products/route.ts @@ -1,9 +1,9 @@ import { db } from "@/db"; -import { bb_products } from "@/db/schema"; +import { products } from "@/db/schema"; export async function GET() { try { - const allProducts = await db.select().from(bb_products).limit(50); + const allProducts = await db.select().from(products).limit(50); const mapped = allProducts.map((item: any) => ({ id: item.uuid, name: item.productName, diff --git a/src/db/schema-org.ts b/src/db/schema-org.ts new file mode 100644 index 0000000..54bce38 --- /dev/null +++ b/src/db/schema-org.ts @@ -0,0 +1,571 @@ +import { pgTableCreator, integer, varchar, text, numeric, timestamp, unique, check, date, boolean, uuid, bigint, real, doublePrecision, primaryKey, pgView, index, serial } from "drizzle-orm/pg-core"; +import { relations, sql } from "drizzle-orm"; +import { DATABASE_PREFIX as prefix } from "@/lib/constants"; + +export const pgTable = pgTableCreator((name) => (prefix == "" || prefix == null) ? name: `${prefix}_${name}`); +/// +export const products = pgTable("products", { + id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "products_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), + name: varchar({ length: 255 }).notNull(), + description: text().notNull(), + price: numeric().notNull(), + resellerId: integer("reseller_id").notNull(), + categoryId: integer("category_id").notNull(), + stockQty: integer("stock_qty").default(0), + updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), + createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), + deletedAt: timestamp("deleted_at", { mode: 'string' }), +}); + +export const categories = pgTable("categories", { + id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "categories_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), + name: varchar({ length: 100 }).notNull(), + parentCategoryId: integer("parent_category_id"), + updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), + createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), + deletedAt: timestamp("deleted_at", { mode: 'string' }), + uuid: uuid().defaultRandom(), +}); + +export const productFeeds = pgTable("product_feeds", { + id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "productfeeds_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), + resellerId: integer("reseller_id").notNull(), + feedUrl: varchar("feed_url", { length: 255 }).notNull(), + lastUpdate: timestamp("last_update", { mode: 'string' }), + updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), + createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), + deletedAt: timestamp("deleted_at", { mode: 'string' }), + uuid: uuid().defaultRandom(), +}, (table) => { + return { + productFeedsUuidUnique: unique("product_feeds_uuid_unique").on(table.uuid), + } +}); + +export const userActivityLog = pgTable("user_activity_log", { + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + id: bigint({ mode: "number" }).primaryKey().generatedAlwaysAsIdentity({ name: "user_activity_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + userId: bigint("user_id", { mode: "number" }).notNull(), + activity: text().notNull(), + timestamp: timestamp({ mode: 'string' }).default(sql`CURRENT_TIMESTAMP`), +}); + +export const brands = pgTable("brands", { + id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "brands_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), + name: varchar({ length: 100 }).notNull(), + updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), + createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), + deletedAt: timestamp("deleted_at", { mode: 'string' }), + uuid: uuid().defaultRandom(), +}, (table) => { + return { + brandsUuidUnique: unique("brands_uuid_unique").on(table.uuid), + } +}); + + +export const manufacturer = pgTable("manufacturer", { + id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "manufacturer_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), + name: varchar({ length: 100 }).notNull(), + updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), + createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), + deletedAt: timestamp("deleted_at", { mode: 'string' }), + uuid: uuid().defaultRandom(), +}, (table) => { + return { + manufacturerUuidUnique: unique("manufacturer_uuid_unique").on(table.uuid), + } +}); + +export const states = pgTable("states", { + id: integer().primaryKey().generatedByDefaultAsIdentity({ name: "states_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), + state: varchar({ length: 50 }), + abbreviation: varchar({ length: 50 }), +}); + +export const componentType = pgTable("component_type", { + id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "component_type_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), + name: varchar({ length: 100 }).notNull(), + updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), + createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), + deletedAt: timestamp("deleted_at", { mode: 'string' }), + uuid: uuid().defaultRandom(), +}, (table) => { + return { + componentTypeUuidUnique: unique("component_type_uuid_unique").on(table.uuid), + } +}); + +export const aeroPrecision = pgTable("aero_precision", { + sku: text().primaryKey().notNull(), + manufacturerId: text("manufacturer_id"), + brandName: text("brand_name"), + productName: text("product_name"), + longDescription: text("long_description"), + shortDescription: text("short_description"), + department: text(), + category: text(), + subcategory: text(), + thumbUrl: text("thumb_url"), + imageUrl: text("image_url"), + buyLink: text("buy_link"), + keywords: text(), + reviews: text(), + retailPrice: numeric("retail_price"), + salePrice: numeric("sale_price"), + brandPageLink: text("brand_page_link"), + brandLogoImage: text("brand_logo_image"), + productPageViewTracking: text("product_page_view_tracking"), + variantsXml: text("variants_xml"), + mediumImageUrl: text("medium_image_url"), + productContentWidget: text("product_content_widget"), + googleCategorization: text("google_categorization"), + itemBasedCommission: text("item_based_commission"), + uuid: uuid().defaultRandom(), +}); + +export const compartment = pgTable("compartment", { + id: uuid().defaultRandom().primaryKey().notNull(), + name: varchar({ length: 100 }).notNull(), + description: varchar({ length: 300 }), + updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), + createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), + deletedAt: timestamp("deleted_at", { mode: 'string' }), +}); + +export const builds = pgTable("builds", { + id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "build_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), + accountId: integer("account_id").notNull(), + name: varchar({ length: 255 }).notNull(), + description: text(), + updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), + createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), + deletedAt: timestamp("deleted_at", { mode: 'string' }), + uuid: uuid().defaultRandom(), +}, (table) => { + return { + buildsUuidUnique: unique("builds_uuid_unique").on(table.uuid), + } +}); + +export const bb_products = pgTable("bb_products", { + uuid: uuid().defaultRandom().primaryKey().notNull(), + upc: varchar("UPC", { length: 100 }), + sku: varchar("SKU", { length: 50 }), + manufacturerId: varchar("MANUFACTURER_ID", { length: 50 }), + brandName: varchar("BRAND_NAME", { length: 50 }), + productName: varchar("PRODUCT_NAME", { length: 255 }), + longDescription: text("LONG_DESCRIPTION"), + shortDescription: varchar("SHORT_DESCRIPTION", { length: 500 }), + department: varchar("DEPARTMENT", { length: 100 }), + category: varchar("CATEGORY", { length: 100 }), + subcategory: varchar("SUBCATEGORY", { length: 100 }), + thumbUrl: varchar("THUMB_URL", { length: 500 }), + imageUrl: varchar("IMAGE_URL", { length: 500 }), + buyLink: varchar("BUY_LINK", { length: 500 }), + keywords: varchar("KEYWORDS", { length: 500 }), + reviews: varchar("REVIEWS", { length: 500 }), + retailPrice: varchar("RETAIL_PRICE", { length: 50 }), + salePrice: varchar("SALE_PRICE", { length: 50 }), + brandPageLink: varchar("BRAND_PAGE_LINK", { length: 500 }), + brandLogoImage: varchar("BRAND_LOGO_IMAGE", { length: 500 }), + productPageViewTracking: varchar("PRODUCT_PAGE_VIEW_TRACKING", { length: 500 }), + parentGroupId: varchar("PARENT_GROUP_ID", { length: 200 }), + fineline: varchar("FINELINE", { length: 200 }), + superfineline: varchar("SUPERFINELINE", { length: 200 }), + modelnumber: varchar("MODELNUMBER", { length: 100 }), + caliber: varchar("CALIBER", { length: 200 }), + mediumImageUrl: varchar("MEDIUM_IMAGE_URL", { length: 500 }), + productContentWidget: varchar("PRODUCT_CONTENT_WIDGET", { length: 500 }), + googleCategorization: varchar("GOOGLE_CATEGORIZATION", { length: 500 }), + itemBasedCommission: varchar("ITEM_BASED_COMMISSION", { length: 500 }), + itemBasedCommissionRate: varchar("ITEM_BASED_COMMISSION RATE", { length: 50 }), + updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), + createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), + deletedAt: timestamp("deleted_at", { mode: 'string' }), +}); + +export const psa_old = pgTable("psa_old", { + sku: varchar("SKU", { length: 50 }), + manufacturerId: varchar("MANUFACTURER_ID", { length: 50 }), + brandName: varchar("BRAND_NAME", { length: 50 }), + productName: varchar("PRODUCT_NAME", { length: 255 }), + longDescription: text("LONG_DESCRIPTION"), + shortDescription: varchar("SHORT_DESCRIPTION", { length: 50 }), + department: varchar("DEPARTMENT", { length: 50 }), + category: varchar("CATEGORY", { length: 50 }), + subcategory: varchar("SUBCATEGORY", { length: 50 }), + thumbUrl: varchar("THUMB_URL", { length: 50 }), + imageUrl: varchar("IMAGE_URL", { length: 50 }), + buyLink: varchar("BUY_LINK", { length: 128 }), + keywords: varchar("KEYWORDS", { length: 50 }), + reviews: varchar("REVIEWS", { length: 50 }), + retailPrice: real("RETAIL_PRICE"), + salePrice: real("SALE_PRICE"), + brandPageLink: varchar("BRAND_PAGE_LINK", { length: 50 }), + brandLogoImage: varchar("BRAND_LOGO_IMAGE", { length: 50 }), + productPageViewTracking: varchar("PRODUCT_PAGE_VIEW_TRACKING", { length: 256 }), + parentGroupId: varchar("PARENT_GROUP_ID", { length: 50 }), + fineline: varchar("FINELINE", { length: 50 }), + superfineline: varchar("SUPERFINELINE", { length: 200 }), + modelnumber: varchar("MODELNUMBER", { length: 50 }), + caliber: varchar("CALIBER", { length: 200 }), + upc: varchar("UPC", { length: 100 }), + mediumImageUrl: varchar("MEDIUM_IMAGE_URL", { length: 50 }), + productContentWidget: varchar("PRODUCT_CONTENT_WIDGET", { length: 256 }), + googleCategorization: varchar("GOOGLE_CATEGORIZATION", { length: 50 }), + itemBasedCommission: varchar("ITEM_BASED_COMMISSION", { length: 50 }), + uuid: uuid().defaultRandom(), +}); +export const psa = pgTable("psa", { + id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "psa_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), + sku: varchar("SKU", { length: 50 }), + manufacturerId: varchar("MANUFACTURER_ID", { length: 50 }), + brandName: varchar("BRAND_NAME", { length: 50 }), + productName: varchar("PRODUCT_NAME", { length: 255 }), + longDescription: text("LONG_DESCRIPTION"), + shortDescription: varchar("SHORT_DESCRIPTION", { length: 50 }), + department: varchar("DEPARTMENT", { length: 50 }), + category: varchar("CATEGORY", { length: 50 }), + subcategory: varchar("SUBCATEGORY", { length: 50 }), + thumbUrl: varchar("THUMB_URL", { length: 50 }), + imageUrl: varchar("IMAGE_URL", { length: 50 }), + buyLink: varchar("BUY_LINK", { length: 128 }), + keywords: varchar("KEYWORDS", { length: 50 }), + reviews: varchar("REVIEWS", { length: 50 }), + retailPrice: real("RETAIL_PRICE"), + salePrice: real("SALE_PRICE"), + brandPageLink: varchar("BRAND_PAGE_LINK", { length: 50 }), + brandLogoImage: varchar("BRAND_LOGO_IMAGE", { length: 50 }), + productPageViewTracking: varchar("PRODUCT_PAGE_VIEW_TRACKING", { length: 256 }), + parentGroupId: varchar("PARENT_GROUP_ID", { length: 50 }), + fineline: varchar("FINELINE", { length: 50 }), + superfineline: varchar("SUPERFINELINE", { length: 200 }), + modelnumber: varchar("MODELNUMBER", { length: 50 }), + caliber: varchar("CALIBER", { length: 200 }), + upc: varchar("UPC", { length: 100 }), + mediumImageUrl: varchar("MEDIUM_IMAGE_URL", { length: 50 }), + productContentWidget: varchar("PRODUCT_CONTENT_WIDGET", { length: 256 }), + googleCategorization: varchar("GOOGLE_CATEGORIZATION", { length: 50 }), + itemBasedCommission: varchar("ITEM_BASED_COMMISSION", { length: 50 }), + uuid: uuid().defaultRandom(), +}); + +export const lipseycatalog = pgTable("lipseycatalog", { + id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "lipseycatalog_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), + itemno: varchar({ length: 20 }).notNull(), + description1: text(), + description2: text(), + upc: varchar({ length: 20 }), + manufacturermodelno: varchar({ length: 30 }), + msrp: doublePrecision(), + model: text(), + calibergauge: text(), + manufacturer: text(), + type: text(), + action: text(), + barrellength: text(), + capacity: text(), + finish: text(), + overalllength: text(), + receiver: text(), + safety: text(), + sights: text(), + stockframegrips: text(), + magazine: text(), + weight: text(), + imagename: text(), + chamber: text(), + drilledandtapped: text(), + rateoftwist: text(), + itemtype: text(), + additionalfeature1: text(), + additionalfeature2: text(), + additionalfeature3: text(), + shippingweight: text(), + boundbookmanufacturer: text(), + boundbookmodel: text(), + boundbooktype: text(), + nfathreadpattern: text(), + nfaattachmentmethod: text(), + nfabaffletype: text(), + silencercanbedisassembled: text(), + silencerconstructionmaterial: text(), + nfadbreduction: text(), + silenceroutsidediameter: text(), + nfaform3Caliber: text(), + opticmagnification: text(), + maintubesize: text(), + adjustableobjective: text(), + objectivesize: text(), + opticadjustments: text(), + illuminatedreticle: text(), + reticle: text(), + exclusive: text(), + quantity: varchar({ length: 10 }).default(sql`NULL`), + allocated: text(), + onsale: text(), + price: doublePrecision(), + currentprice: doublePrecision(), + retailmap: doublePrecision(), + fflrequired: text(), + sotrequired: text(), + exclusivetype: text(), + scopecoverincluded: text(), + special: text(), + sightstype: text(), + case: text(), + choke: text(), + dbreduction: text(), + family: text(), + finishtype: text(), + frame: text(), + griptype: varchar({ length: 30 }), + handgunslidematerial: text(), + countryoforigin: varchar({ length: 4 }), + itemlength: text(), + itemwidth: text(), + itemheight: text(), + packagelength: doublePrecision(), + packagewidth: doublePrecision(), + packageheight: doublePrecision(), + itemgroup: varchar({ length: 40 }), + updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), + createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), + deletedAt: timestamp("deleted_at", { mode: 'string' }), +}); + +export const buildsComponents = pgTable("builds_components", { + id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "build_components_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), + buildId: integer("build_id").notNull(), + productId: integer("product_id").notNull(), + updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), + createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), + deletedAt: timestamp("deleted_at", { mode: 'string' }), + uuid: uuid().defaultRandom(), +}, (table) => { + return { + buildsComponentsUuidUnique: unique("builds_components_uuid_unique").on(table.uuid), + } +}); + +export const balResellers = pgTable("bal_resellers", { + id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "resellers_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), + name: varchar({ length: 100 }).notNull(), + websiteUrl: varchar("website_url", { length: 255 }), + contactEmail: varchar("contact_email", { length: 100 }), + updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), + createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), + deletedAt: timestamp("deleted_at", { mode: 'string' }), + uuid: uuid().defaultRandom(), +}, (table) => { + return { + balResellersUuidUnique: unique("bal_resellers_uuid_unique").on(table.uuid), + } +}); + +export const verificationTokens = pgTable("verificationTokens", { + identifier: varchar("identifier").notNull(), + token: varchar("token").notNull(), + expires: timestamp("expires").notNull(), +}); + +export const authenticator = pgTable("authenticator", { + credentialId: text().notNull(), + userId: text().notNull(), + providerAccountId: text().notNull(), + credentialPublicKey: text().notNull(), + counter: integer().notNull(), + credentialDeviceType: text().notNull(), + credentialBackedUp: boolean().notNull(), + transports: text(), +}, (table) => { + return { + authenticatorUserIdCredentialIdPk: primaryKey({ columns: [table.credentialId, table.userId], name: "authenticator_userId_credentialID_pk"}), + authenticatorCredentialIdUnique: unique("authenticator_credentialID_unique").on(table.credentialId), + } +}); + +export const accounts = pgTable("accounts", { + id: uuid("id").primaryKey().defaultRandom(), + uuid: uuid("uuid").defaultRandom(), + userId: uuid("user_id").notNull(), + type: varchar("type").notNull(), + provider: text().notNull(), + providerAccountId: varchar("provider_account_id").notNull(), + refreshToken: text("refresh_token"), + accessToken: text("access_token"), + expiresAt: integer("expires_at"), + tokenType: varchar("token_type"), + idToken: text("id_token"), + sessionState: varchar("session_state"), + scope: text(), +} +); + +/* export const vw_accounts = pgView("vw_accounts", { + uuid: uuid().defaultRandom(), + userId: text("user_id").notNull(), + type: text().notNull(), + provider: text().notNull(), + providerAccountId: text("provider_account_id").notNull(), + refreshToken: text("refresh_token"), + accessToken: text("access_token"), + expiresAt: integer("expires_at"), + tokenType: text("token_type"), + scope: text(), + idToken: text("id_token"), + sessionState: text("session_state"), + first_name: text("first_name"), + last_name: text("last_name"), + +},) */ + +/* From here down is the authentication library Lusia tables */ + +export const users = pgTable("user", + { + id: varchar("id", { length: 21 }).primaryKey(), + name: varchar("name"), + username: varchar({ length: 50 }), + discordId: varchar("discord_id", { length: 255 }).unique(), + email: varchar("email", { length: 255 }).unique().notNull(), + emailVerified: boolean("email_verified").default(false).notNull(), + hashedPassword: varchar("hashed_password", { length: 255 }), + first_name: varchar("first_name", { length: 50 }), + last_name: varchar("last_name", { length: 50 }), + full_name: varchar("full_name", { length: 50 }), + profilePicture: varchar("profile_picture", { length: 255 }), + image: text("image"), + dateOfBirth: date("date_of_birth"), + phoneNumber: varchar("phone_number", { length: 20 }), + createdAt: timestamp("created_at", { mode: 'string' }).default(sql`CURRENT_TIMESTAMP`), + updatedAt: timestamp("updated_at", { mode: 'string' }).default(sql`CURRENT_TIMESTAMP`), + isAdmin: boolean("is_admin").default(false), + lastLogin: timestamp("last_login", { mode: 'string' }), + buildPrivacySetting: text("build_privacy_setting").default('public'), + uuid: uuid().defaultRandom(), + avatar: varchar("avatar", { length: 255 }), + stripeSubscriptionId: varchar("stripe_subscription_id", { length: 191 }), + stripePriceId: varchar("stripe_price_id", { length: 191 }), + stripeCustomerId: varchar("stripe_customer_id", { length: 191 }), + stripeCurrentPeriodEnd: timestamp("stripe_current_period_end"), +}, (table) => ({ + usersUsernameKey: unique("users_username_key").on(table.username), + usersEmailKey: unique("users_email_key").on(table.email), + usersBuildPrivacySettingCheck: check("users_build_privacy_setting_check", sql`build_privacy_setting = ANY (ARRAY['private'::text, 'public'::text])`), + emailIdx: index("user_email_idx").on(table.email), + discordIdx: index("user_discord_idx").on(table.discordId), + }), +); +export type User = typeof users.$inferSelect; +export type NewUser = typeof users.$inferInsert; + +export const session = pgTable( + "session", + { + sessionToken: varchar("sessionToken", { length: 255 }).primaryKey(), + userId: varchar("userId", { length: 21 }).notNull(), + expires: timestamp("expires", { withTimezone: true, mode: "date" }).notNull(), + } +); + +export const emailVerificationCodes = pgTable( + "email_verification_codes", + { + id: serial("id").primaryKey(), + userId: varchar("user_id", { length: 21 }).unique().notNull(), + email: varchar("email", { length: 255 }).notNull(), + code: varchar("code", { length: 8 }).notNull(), + expiresAt: timestamp("expires_at", { withTimezone: true, mode: "date" }).notNull(), + }, + (t) => ({ + userIdx: index("verification_code_user_idx").on(t.userId), + emailIdx: index("verification_code_email_idx").on(t.email), + }), + ); + + export const passwordResetTokens = pgTable( + "password_reset_tokens", + { + id: varchar("id", { length: 40 }).primaryKey(), + userId: varchar("user_id", { length: 21 }).notNull(), + expiresAt: timestamp("expires_at", { withTimezone: true, mode: "date" }).notNull(), + }, + (t) => ({ + userIdx: index("password_token_user_idx").on(t.userId), + }), + ); + + export const posts = pgTable( + "posts", + { + id: varchar("id", { length: 15 }).primaryKey(), + userId: varchar("user_id", { length: 255 }).notNull(), + title: varchar("title", { length: 255 }).notNull(), + excerpt: varchar("excerpt", { length: 255 }).notNull(), + content: text("content").notNull(), + status: varchar("status", { length: 10, enum: ["draft", "published"] }) + .default("draft") + .notNull(), + tags: varchar("tags", { length: 255 }), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at", { mode: "date" }).$onUpdate(() => new Date()), + }, + (t) => ({ + userIdx: index("post_user_idx").on(t.userId), + createdAtIdx: index("post_created_at_idx").on(t.createdAt), + }), + ); + + export const postRelations = relations(posts, ({ one }) => ({ + user: one(users, { + fields: [posts.userId], + references: [users.id], + }), + })); + + export type Post = typeof posts.$inferSelect; + export type NewPost = typeof posts.$inferInsert; + + export const vwUserSessions = pgView("vw_user_sessions", { id: varchar({ length: 255 }), + userId: varchar("user_id", { length: 21 }), + uId: varchar("u_id", { length: 21 }), + uEmail: varchar("u_email", { length: 255 }), + expiresAt: timestamp("expires_at", { withTimezone: true, mode: 'string' }), + createdAt: timestamp("created_at", { mode: 'string' }), + updatedAt: timestamp("updated_at", { mode: 'string' }), +}).existing(); +//as(sql`SELECT s.id, s.user_id, u.id AS u_id, u.email AS u_email, s.expires_at, s.created_at, s.updated_at FROM sessions s, users u WHERE s.user_id::text = u.id::text`); + +// Default Drizzle File + +// import { pgTable, serial, text, integer, timestamp } from "drizzle-orm/pg-core"; + +// export const products = pgTable("products", { +// id: serial("id").primaryKey(), +// name: text("name").notNull(), +// description: text("description"), +// price: integer("price"), +// createdAt: timestamp("created_at").defaultNow(), +// // Add more fields as needed +// }); + +export const affiliateCategoryMap = pgTable("affiliate_category_map", { + id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "affiliate_category_map_id_seq", startWith: 1, increment: 1 }), + feedname: varchar("feedname", { length: 100 }).notNull(), + affiliatecategory: varchar("affiliatecategory", { length: 255 }).notNull(), + buildercategoryid: integer("buildercategoryid").notNull(), + notes: varchar("notes", { length: 255 }), +}); + +export const product_categories = pgTable("product_categories", { + id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "product_categories_id_seq", startWith: 1, increment: 1 }), + name: varchar({ length: 100 }).notNull(), + parent_category_id: integer("parent_category_id"), + type: varchar({ length: 50 }), + sort_order: integer("sort_order"), + created_at: timestamp("created_at", { mode: 'string' }).defaultNow(), + updated_at: timestamp("updated_at", { mode: 'string' }).defaultNow(), +}); \ No newline at end of file diff --git a/src/db/schema.ts b/src/db/schema.ts index 54bce38..7bf1ae8 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -1,571 +1,108 @@ -import { pgTableCreator, integer, varchar, text, numeric, timestamp, unique, check, date, boolean, uuid, bigint, real, doublePrecision, primaryKey, pgView, index, serial } from "drizzle-orm/pg-core"; -import { relations, sql } from "drizzle-orm"; -import { DATABASE_PREFIX as prefix } from "@/lib/constants"; +import { pgTable, foreignKey, serial, varchar, integer, doublePrecision, timestamp, unique, text, numeric, boolean, jsonb } from "drizzle-orm/pg-core" +import { sql } from "drizzle-orm" -export const pgTable = pgTableCreator((name) => (prefix == "" || prefix == null) ? name: `${prefix}_${name}`); -/// -export const products = pgTable("products", { - id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "products_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), - name: varchar({ length: 255 }).notNull(), - description: text().notNull(), - price: numeric().notNull(), - resellerId: integer("reseller_id").notNull(), - categoryId: integer("category_id").notNull(), - stockQty: integer("stock_qty").default(0), - updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), - createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), - deletedAt: timestamp("deleted_at", { mode: 'string' }), -}); + + +export const productCategoryMappings = pgTable("product_category_mappings", { + id: serial().primaryKey().notNull(), + feedName: varchar("feed_name", { length: 255 }), + feedCategoryValue: varchar("feed_category_value", { length: 255 }), + canonicalCategoryId: integer("canonical_category_id"), + confidenceScore: doublePrecision("confidence_score"), + lastReviewedBy: varchar("last_reviewed_by", { length: 255 }), + lastReviewedAt: timestamp("last_reviewed_at", { mode: 'string' }), +}, (table) => [ + foreignKey({ + columns: [table.canonicalCategoryId], + foreignColumns: [categories.id], + name: "product_category_mappings_canonical_category_id_fkey" + }), +]); export const categories = pgTable("categories", { - id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "categories_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), - name: varchar({ length: 100 }).notNull(), - parentCategoryId: integer("parent_category_id"), - updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), - createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), - deletedAt: timestamp("deleted_at", { mode: 'string' }), - uuid: uuid().defaultRandom(), -}); - -export const productFeeds = pgTable("product_feeds", { - id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "productfeeds_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), - resellerId: integer("reseller_id").notNull(), - feedUrl: varchar("feed_url", { length: 255 }).notNull(), - lastUpdate: timestamp("last_update", { mode: 'string' }), - updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), - createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), - deletedAt: timestamp("deleted_at", { mode: 'string' }), - uuid: uuid().defaultRandom(), -}, (table) => { - return { - productFeedsUuidUnique: unique("product_feeds_uuid_unique").on(table.uuid), - } -}); - -export const userActivityLog = pgTable("user_activity_log", { - // You can use { mode: "bigint" } if numbers are exceeding js number limitations - id: bigint({ mode: "number" }).primaryKey().generatedAlwaysAsIdentity({ name: "user_activity_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), - // You can use { mode: "bigint" } if numbers are exceeding js number limitations - userId: bigint("user_id", { mode: "number" }).notNull(), - activity: text().notNull(), - timestamp: timestamp({ mode: 'string' }).default(sql`CURRENT_TIMESTAMP`), -}); - -export const brands = pgTable("brands", { - id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "brands_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), - name: varchar({ length: 100 }).notNull(), - updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), - createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), - deletedAt: timestamp("deleted_at", { mode: 'string' }), - uuid: uuid().defaultRandom(), -}, (table) => { - return { - brandsUuidUnique: unique("brands_uuid_unique").on(table.uuid), - } -}); - - -export const manufacturer = pgTable("manufacturer", { - id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "manufacturer_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), - name: varchar({ length: 100 }).notNull(), - updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), - createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), - deletedAt: timestamp("deleted_at", { mode: 'string' }), - uuid: uuid().defaultRandom(), -}, (table) => { - return { - manufacturerUuidUnique: unique("manufacturer_uuid_unique").on(table.uuid), - } -}); - -export const states = pgTable("states", { - id: integer().primaryKey().generatedByDefaultAsIdentity({ name: "states_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), - state: varchar({ length: 50 }), - abbreviation: varchar({ length: 50 }), -}); - -export const componentType = pgTable("component_type", { - id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "component_type_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), - name: varchar({ length: 100 }).notNull(), - updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), - createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), - deletedAt: timestamp("deleted_at", { mode: 'string' }), - uuid: uuid().defaultRandom(), -}, (table) => { - return { - componentTypeUuidUnique: unique("component_type_uuid_unique").on(table.uuid), - } -}); - -export const aeroPrecision = pgTable("aero_precision", { - sku: text().primaryKey().notNull(), - manufacturerId: text("manufacturer_id"), - brandName: text("brand_name"), - productName: text("product_name"), - longDescription: text("long_description"), - shortDescription: text("short_description"), - department: text(), - category: text(), - subcategory: text(), - thumbUrl: text("thumb_url"), - imageUrl: text("image_url"), - buyLink: text("buy_link"), - keywords: text(), - reviews: text(), - retailPrice: numeric("retail_price"), - salePrice: numeric("sale_price"), - brandPageLink: text("brand_page_link"), - brandLogoImage: text("brand_logo_image"), - productPageViewTracking: text("product_page_view_tracking"), - variantsXml: text("variants_xml"), - mediumImageUrl: text("medium_image_url"), - productContentWidget: text("product_content_widget"), - googleCategorization: text("google_categorization"), - itemBasedCommission: text("item_based_commission"), - uuid: uuid().defaultRandom(), -}); - -export const compartment = pgTable("compartment", { - id: uuid().defaultRandom().primaryKey().notNull(), - name: varchar({ length: 100 }).notNull(), - description: varchar({ length: 300 }), - updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), - createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), - deletedAt: timestamp("deleted_at", { mode: 'string' }), -}); - -export const builds = pgTable("builds", { - id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "build_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), - accountId: integer("account_id").notNull(), + id: serial().primaryKey().notNull(), name: varchar({ length: 255 }).notNull(), + parentId: integer("parent_id"), + slug: varchar({ length: 255 }).notNull(), +}, (table) => [ + foreignKey({ + columns: [table.parentId], + foreignColumns: [table.id], + name: "categories_parent_id_fkey" + }), + unique("categories_slug_key").on(table.slug), +]); + +export const products = pgTable("products", { + id: serial().primaryKey().notNull(), + name: varchar({ length: 255 }).notNull(), + brand: varchar({ length: 255 }), description: text(), - updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), - createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), - deletedAt: timestamp("deleted_at", { mode: 'string' }), - uuid: uuid().defaultRandom(), -}, (table) => { - return { - buildsUuidUnique: unique("builds_uuid_unique").on(table.uuid), - } -}); + upc: varchar({ length: 32 }), + mpn: varchar({ length: 64 }), + canonicalCategoryId: integer("canonical_category_id"), + createdAt: timestamp("created_at", { mode: 'string' }).defaultNow(), + updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow(), +}, (table) => [ + foreignKey({ + columns: [table.canonicalCategoryId], + foreignColumns: [categories.id], + name: "products_canonical_category_id_fkey" + }), +]); -export const bb_products = pgTable("bb_products", { - uuid: uuid().defaultRandom().primaryKey().notNull(), - upc: varchar("UPC", { length: 100 }), - sku: varchar("SKU", { length: 50 }), - manufacturerId: varchar("MANUFACTURER_ID", { length: 50 }), - brandName: varchar("BRAND_NAME", { length: 50 }), - productName: varchar("PRODUCT_NAME", { length: 255 }), - longDescription: text("LONG_DESCRIPTION"), - shortDescription: varchar("SHORT_DESCRIPTION", { length: 500 }), - department: varchar("DEPARTMENT", { length: 100 }), - category: varchar("CATEGORY", { length: 100 }), - subcategory: varchar("SUBCATEGORY", { length: 100 }), - thumbUrl: varchar("THUMB_URL", { length: 500 }), - imageUrl: varchar("IMAGE_URL", { length: 500 }), - buyLink: varchar("BUY_LINK", { length: 500 }), - keywords: varchar("KEYWORDS", { length: 500 }), - reviews: varchar("REVIEWS", { length: 500 }), - retailPrice: varchar("RETAIL_PRICE", { length: 50 }), - salePrice: varchar("SALE_PRICE", { length: 50 }), - brandPageLink: varchar("BRAND_PAGE_LINK", { length: 500 }), - brandLogoImage: varchar("BRAND_LOGO_IMAGE", { length: 500 }), - productPageViewTracking: varchar("PRODUCT_PAGE_VIEW_TRACKING", { length: 500 }), - parentGroupId: varchar("PARENT_GROUP_ID", { length: 200 }), - fineline: varchar("FINELINE", { length: 200 }), - superfineline: varchar("SUPERFINELINE", { length: 200 }), - modelnumber: varchar("MODELNUMBER", { length: 100 }), - caliber: varchar("CALIBER", { length: 200 }), - mediumImageUrl: varchar("MEDIUM_IMAGE_URL", { length: 500 }), - productContentWidget: varchar("PRODUCT_CONTENT_WIDGET", { length: 500 }), - googleCategorization: varchar("GOOGLE_CATEGORIZATION", { length: 500 }), - itemBasedCommission: varchar("ITEM_BASED_COMMISSION", { length: 500 }), - itemBasedCommissionRate: varchar("ITEM_BASED_COMMISSION RATE", { length: 50 }), - updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), - createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), - deletedAt: timestamp("deleted_at", { mode: 'string' }), -}); +export const offers = pgTable("offers", { + id: serial().primaryKey().notNull(), + productId: integer("product_id"), + feedName: varchar("feed_name", { length: 255 }).notNull(), + feedSku: varchar("feed_sku", { length: 255 }), + price: numeric({ precision: 10, scale: 2 }), + url: text(), + inStock: boolean("in_stock"), + vendor: varchar({ length: 255 }), + lastSeenAt: timestamp("last_seen_at", { mode: 'string' }).defaultNow(), + rawData: jsonb("raw_data"), +}, (table) => [ + foreignKey({ + columns: [table.productId], + foreignColumns: [products.id], + name: "offers_product_id_fkey" + }).onDelete("cascade"), + unique("offers_product_id_feed_name_feed_sku_key").on(table.productId, table.feedName, table.feedSku), + unique("offers_feed_unique").on(table.feedName, table.feedSku), +]); -export const psa_old = pgTable("psa_old", { - sku: varchar("SKU", { length: 50 }), - manufacturerId: varchar("MANUFACTURER_ID", { length: 50 }), - brandName: varchar("BRAND_NAME", { length: 50 }), - productName: varchar("PRODUCT_NAME", { length: 255 }), - longDescription: text("LONG_DESCRIPTION"), - shortDescription: varchar("SHORT_DESCRIPTION", { length: 50 }), - department: varchar("DEPARTMENT", { length: 50 }), - category: varchar("CATEGORY", { length: 50 }), - subcategory: varchar("SUBCATEGORY", { length: 50 }), - thumbUrl: varchar("THUMB_URL", { length: 50 }), - imageUrl: varchar("IMAGE_URL", { length: 50 }), - buyLink: varchar("BUY_LINK", { length: 128 }), - keywords: varchar("KEYWORDS", { length: 50 }), - reviews: varchar("REVIEWS", { length: 50 }), - retailPrice: real("RETAIL_PRICE"), - salePrice: real("SALE_PRICE"), - brandPageLink: varchar("BRAND_PAGE_LINK", { length: 50 }), - brandLogoImage: varchar("BRAND_LOGO_IMAGE", { length: 50 }), - productPageViewTracking: varchar("PRODUCT_PAGE_VIEW_TRACKING", { length: 256 }), - parentGroupId: varchar("PARENT_GROUP_ID", { length: 50 }), - fineline: varchar("FINELINE", { length: 50 }), - superfineline: varchar("SUPERFINELINE", { length: 200 }), - modelnumber: varchar("MODELNUMBER", { length: 50 }), - caliber: varchar("CALIBER", { length: 200 }), - upc: varchar("UPC", { length: 100 }), - mediumImageUrl: varchar("MEDIUM_IMAGE_URL", { length: 50 }), - productContentWidget: varchar("PRODUCT_CONTENT_WIDGET", { length: 256 }), - googleCategorization: varchar("GOOGLE_CATEGORIZATION", { length: 50 }), - itemBasedCommission: varchar("ITEM_BASED_COMMISSION", { length: 50 }), - uuid: uuid().defaultRandom(), -}); -export const psa = pgTable("psa", { - id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "psa_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), - sku: varchar("SKU", { length: 50 }), - manufacturerId: varchar("MANUFACTURER_ID", { length: 50 }), - brandName: varchar("BRAND_NAME", { length: 50 }), - productName: varchar("PRODUCT_NAME", { length: 255 }), - longDescription: text("LONG_DESCRIPTION"), - shortDescription: varchar("SHORT_DESCRIPTION", { length: 50 }), - department: varchar("DEPARTMENT", { length: 50 }), - category: varchar("CATEGORY", { length: 50 }), - subcategory: varchar("SUBCATEGORY", { length: 50 }), - thumbUrl: varchar("THUMB_URL", { length: 50 }), - imageUrl: varchar("IMAGE_URL", { length: 50 }), - buyLink: varchar("BUY_LINK", { length: 128 }), - keywords: varchar("KEYWORDS", { length: 50 }), - reviews: varchar("REVIEWS", { length: 50 }), - retailPrice: real("RETAIL_PRICE"), - salePrice: real("SALE_PRICE"), - brandPageLink: varchar("BRAND_PAGE_LINK", { length: 50 }), - brandLogoImage: varchar("BRAND_LOGO_IMAGE", { length: 50 }), - productPageViewTracking: varchar("PRODUCT_PAGE_VIEW_TRACKING", { length: 256 }), - parentGroupId: varchar("PARENT_GROUP_ID", { length: 50 }), - fineline: varchar("FINELINE", { length: 50 }), - superfineline: varchar("SUPERFINELINE", { length: 200 }), - modelnumber: varchar("MODELNUMBER", { length: 50 }), - caliber: varchar("CALIBER", { length: 200 }), - upc: varchar("UPC", { length: 100 }), - mediumImageUrl: varchar("MEDIUM_IMAGE_URL", { length: 50 }), - productContentWidget: varchar("PRODUCT_CONTENT_WIDGET", { length: 256 }), - googleCategorization: varchar("GOOGLE_CATEGORIZATION", { length: 50 }), - itemBasedCommission: varchar("ITEM_BASED_COMMISSION", { length: 50 }), - uuid: uuid().defaultRandom(), -}); +export const offerPriceHistory = pgTable("offer_price_history", { + id: serial().primaryKey().notNull(), + offerId: integer("offer_id"), + price: numeric({ precision: 10, scale: 2 }).notNull(), + seenAt: timestamp("seen_at", { mode: 'string' }).defaultNow(), +}, (table) => [ + foreignKey({ + columns: [table.offerId], + foreignColumns: [offers.id], + name: "offer_price_history_offer_id_fkey" + }).onDelete("cascade"), +]); -export const lipseycatalog = pgTable("lipseycatalog", { - id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "lipseycatalog_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), - itemno: varchar({ length: 20 }).notNull(), - description1: text(), - description2: text(), - upc: varchar({ length: 20 }), - manufacturermodelno: varchar({ length: 30 }), - msrp: doublePrecision(), - model: text(), - calibergauge: text(), - manufacturer: text(), - type: text(), - action: text(), - barrellength: text(), - capacity: text(), - finish: text(), - overalllength: text(), - receiver: text(), - safety: text(), - sights: text(), - stockframegrips: text(), - magazine: text(), - weight: text(), - imagename: text(), - chamber: text(), - drilledandtapped: text(), - rateoftwist: text(), - itemtype: text(), - additionalfeature1: text(), - additionalfeature2: text(), - additionalfeature3: text(), - shippingweight: text(), - boundbookmanufacturer: text(), - boundbookmodel: text(), - boundbooktype: text(), - nfathreadpattern: text(), - nfaattachmentmethod: text(), - nfabaffletype: text(), - silencercanbedisassembled: text(), - silencerconstructionmaterial: text(), - nfadbreduction: text(), - silenceroutsidediameter: text(), - nfaform3Caliber: text(), - opticmagnification: text(), - maintubesize: text(), - adjustableobjective: text(), - objectivesize: text(), - opticadjustments: text(), - illuminatedreticle: text(), - reticle: text(), - exclusive: text(), - quantity: varchar({ length: 10 }).default(sql`NULL`), - allocated: text(), - onsale: text(), - price: doublePrecision(), - currentprice: doublePrecision(), - retailmap: doublePrecision(), - fflrequired: text(), - sotrequired: text(), - exclusivetype: text(), - scopecoverincluded: text(), - special: text(), - sightstype: text(), - case: text(), - choke: text(), - dbreduction: text(), - family: text(), - finishtype: text(), - frame: text(), - griptype: varchar({ length: 30 }), - handgunslidematerial: text(), - countryoforigin: varchar({ length: 4 }), - itemlength: text(), - itemwidth: text(), - itemheight: text(), - packagelength: doublePrecision(), - packagewidth: doublePrecision(), - packageheight: doublePrecision(), - itemgroup: varchar({ length: 40 }), - updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), - createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), - deletedAt: timestamp("deleted_at", { mode: 'string' }), -}); +export const feeds = pgTable("feeds", { + id: serial().primaryKey().notNull(), + name: varchar({ length: 255 }).notNull(), + url: text(), + lastImportedAt: timestamp("last_imported_at", { mode: 'string' }), +}, (table) => [ + unique("feeds_name_key").on(table.name), +]); -export const buildsComponents = pgTable("builds_components", { - id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "build_components_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), - buildId: integer("build_id").notNull(), - productId: integer("product_id").notNull(), - updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), - createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), - deletedAt: timestamp("deleted_at", { mode: 'string' }), - uuid: uuid().defaultRandom(), -}, (table) => { - return { - buildsComponentsUuidUnique: unique("builds_components_uuid_unique").on(table.uuid), - } -}); - -export const balResellers = pgTable("bal_resellers", { - id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "resellers_id_seq", startWith: 1, increment: 1, minValue: 1, maxValue: 2147483647, cache: 1 }), - name: varchar({ length: 100 }).notNull(), - websiteUrl: varchar("website_url", { length: 255 }), - contactEmail: varchar("contact_email", { length: 100 }), - updatedAt: timestamp("updated_at", { mode: 'string' }).defaultNow().notNull(), - createdAt: timestamp("created_at", { mode: 'string' }).defaultNow().notNull(), - deletedAt: timestamp("deleted_at", { mode: 'string' }), - uuid: uuid().defaultRandom(), -}, (table) => { - return { - balResellersUuidUnique: unique("bal_resellers_uuid_unique").on(table.uuid), - } -}); - -export const verificationTokens = pgTable("verificationTokens", { - identifier: varchar("identifier").notNull(), - token: varchar("token").notNull(), - expires: timestamp("expires").notNull(), -}); - -export const authenticator = pgTable("authenticator", { - credentialId: text().notNull(), - userId: text().notNull(), - providerAccountId: text().notNull(), - credentialPublicKey: text().notNull(), - counter: integer().notNull(), - credentialDeviceType: text().notNull(), - credentialBackedUp: boolean().notNull(), - transports: text(), -}, (table) => { - return { - authenticatorUserIdCredentialIdPk: primaryKey({ columns: [table.credentialId, table.userId], name: "authenticator_userId_credentialID_pk"}), - authenticatorCredentialIdUnique: unique("authenticator_credentialID_unique").on(table.credentialId), - } -}); - -export const accounts = pgTable("accounts", { - id: uuid("id").primaryKey().defaultRandom(), - uuid: uuid("uuid").defaultRandom(), - userId: uuid("user_id").notNull(), - type: varchar("type").notNull(), - provider: text().notNull(), - providerAccountId: varchar("provider_account_id").notNull(), - refreshToken: text("refresh_token"), - accessToken: text("access_token"), - expiresAt: integer("expires_at"), - tokenType: varchar("token_type"), - idToken: text("id_token"), - sessionState: varchar("session_state"), - scope: text(), -} -); - -/* export const vw_accounts = pgView("vw_accounts", { - uuid: uuid().defaultRandom(), - userId: text("user_id").notNull(), - type: text().notNull(), - provider: text().notNull(), - providerAccountId: text("provider_account_id").notNull(), - refreshToken: text("refresh_token"), - accessToken: text("access_token"), - expiresAt: integer("expires_at"), - tokenType: text("token_type"), - scope: text(), - idToken: text("id_token"), - sessionState: text("session_state"), - first_name: text("first_name"), - last_name: text("last_name"), - -},) */ - -/* From here down is the authentication library Lusia tables */ - -export const users = pgTable("user", - { - id: varchar("id", { length: 21 }).primaryKey(), - name: varchar("name"), - username: varchar({ length: 50 }), - discordId: varchar("discord_id", { length: 255 }).unique(), - email: varchar("email", { length: 255 }).unique().notNull(), - emailVerified: boolean("email_verified").default(false).notNull(), - hashedPassword: varchar("hashed_password", { length: 255 }), - first_name: varchar("first_name", { length: 50 }), - last_name: varchar("last_name", { length: 50 }), - full_name: varchar("full_name", { length: 50 }), - profilePicture: varchar("profile_picture", { length: 255 }), - image: text("image"), - dateOfBirth: date("date_of_birth"), - phoneNumber: varchar("phone_number", { length: 20 }), - createdAt: timestamp("created_at", { mode: 'string' }).default(sql`CURRENT_TIMESTAMP`), - updatedAt: timestamp("updated_at", { mode: 'string' }).default(sql`CURRENT_TIMESTAMP`), - isAdmin: boolean("is_admin").default(false), - lastLogin: timestamp("last_login", { mode: 'string' }), - buildPrivacySetting: text("build_privacy_setting").default('public'), - uuid: uuid().defaultRandom(), - avatar: varchar("avatar", { length: 255 }), - stripeSubscriptionId: varchar("stripe_subscription_id", { length: 191 }), - stripePriceId: varchar("stripe_price_id", { length: 191 }), - stripeCustomerId: varchar("stripe_customer_id", { length: 191 }), - stripeCurrentPeriodEnd: timestamp("stripe_current_period_end"), -}, (table) => ({ - usersUsernameKey: unique("users_username_key").on(table.username), - usersEmailKey: unique("users_email_key").on(table.email), - usersBuildPrivacySettingCheck: check("users_build_privacy_setting_check", sql`build_privacy_setting = ANY (ARRAY['private'::text, 'public'::text])`), - emailIdx: index("user_email_idx").on(table.email), - discordIdx: index("user_discord_idx").on(table.discordId), - }), -); -export type User = typeof users.$inferSelect; -export type NewUser = typeof users.$inferInsert; - -export const session = pgTable( - "session", - { - sessionToken: varchar("sessionToken", { length: 255 }).primaryKey(), - userId: varchar("userId", { length: 21 }).notNull(), - expires: timestamp("expires", { withTimezone: true, mode: "date" }).notNull(), - } -); - -export const emailVerificationCodes = pgTable( - "email_verification_codes", - { - id: serial("id").primaryKey(), - userId: varchar("user_id", { length: 21 }).unique().notNull(), - email: varchar("email", { length: 255 }).notNull(), - code: varchar("code", { length: 8 }).notNull(), - expiresAt: timestamp("expires_at", { withTimezone: true, mode: "date" }).notNull(), - }, - (t) => ({ - userIdx: index("verification_code_user_idx").on(t.userId), - emailIdx: index("verification_code_email_idx").on(t.email), - }), - ); - - export const passwordResetTokens = pgTable( - "password_reset_tokens", - { - id: varchar("id", { length: 40 }).primaryKey(), - userId: varchar("user_id", { length: 21 }).notNull(), - expiresAt: timestamp("expires_at", { withTimezone: true, mode: "date" }).notNull(), - }, - (t) => ({ - userIdx: index("password_token_user_idx").on(t.userId), - }), - ); - - export const posts = pgTable( - "posts", - { - id: varchar("id", { length: 15 }).primaryKey(), - userId: varchar("user_id", { length: 255 }).notNull(), - title: varchar("title", { length: 255 }).notNull(), - excerpt: varchar("excerpt", { length: 255 }).notNull(), - content: text("content").notNull(), - status: varchar("status", { length: 10, enum: ["draft", "published"] }) - .default("draft") - .notNull(), - tags: varchar("tags", { length: 255 }), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at", { mode: "date" }).$onUpdate(() => new Date()), - }, - (t) => ({ - userIdx: index("post_user_idx").on(t.userId), - createdAtIdx: index("post_created_at_idx").on(t.createdAt), - }), - ); - - export const postRelations = relations(posts, ({ one }) => ({ - user: one(users, { - fields: [posts.userId], - references: [users.id], - }), - })); - - export type Post = typeof posts.$inferSelect; - export type NewPost = typeof posts.$inferInsert; - - export const vwUserSessions = pgView("vw_user_sessions", { id: varchar({ length: 255 }), - userId: varchar("user_id", { length: 21 }), - uId: varchar("u_id", { length: 21 }), - uEmail: varchar("u_email", { length: 255 }), - expiresAt: timestamp("expires_at", { withTimezone: true, mode: 'string' }), - createdAt: timestamp("created_at", { mode: 'string' }), - updatedAt: timestamp("updated_at", { mode: 'string' }), -}).existing(); -//as(sql`SELECT s.id, s.user_id, u.id AS u_id, u.email AS u_email, s.expires_at, s.created_at, s.updated_at FROM sessions s, users u WHERE s.user_id::text = u.id::text`); - -// Default Drizzle File - -// import { pgTable, serial, text, integer, timestamp } from "drizzle-orm/pg-core"; - -// export const products = pgTable("products", { -// id: serial("id").primaryKey(), -// name: text("name").notNull(), -// description: text("description"), -// price: integer("price"), -// createdAt: timestamp("created_at").defaultNow(), -// // Add more fields as needed -// }); - -export const affiliateCategoryMap = pgTable("affiliate_category_map", { - id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "affiliate_category_map_id_seq", startWith: 1, increment: 1 }), - feedname: varchar("feedname", { length: 100 }).notNull(), - affiliatecategory: varchar("affiliatecategory", { length: 255 }).notNull(), - buildercategoryid: integer("buildercategoryid").notNull(), - notes: varchar("notes", { length: 255 }), -}); - -export const product_categories = pgTable("product_categories", { - id: integer().primaryKey().generatedAlwaysAsIdentity({ name: "product_categories_id_seq", startWith: 1, increment: 1 }), - name: varchar({ length: 100 }).notNull(), - parent_category_id: integer("parent_category_id"), - type: varchar({ length: 50 }), - sort_order: integer("sort_order"), - created_at: timestamp("created_at", { mode: 'string' }).defaultNow(), - updated_at: timestamp("updated_at", { mode: 'string' }).defaultNow(), -}); \ No newline at end of file +export const productAttributes = pgTable("product_attributes", { + id: serial().primaryKey().notNull(), + productId: integer("product_id"), + name: varchar({ length: 255 }).notNull(), + value: varchar({ length: 255 }).notNull(), +}, (table) => [ + foreignKey({ + columns: [table.productId], + foreignColumns: [products.id], + name: "product_attributes_product_id_fkey" + }).onDelete("cascade"), +]);