feat(FN-190): move drizzle-kit to production dependencies (+8 more)
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged: - feat(FN-190): complete Step 9 — add Migration Workflow section to docs/INDEX.md - fix(FN-190): apply biome import sorting and formatting - feat(FN-190): complete Step 7 — write unit tests for migrate runner (5 tests) - feat(FN-190): complete Step 6 — replace db:push with db:migrate in deploy workflow - feat(FN-190): complete Step 5 — update Dockerfile with drizzle/ copy and start.sh CMD - feat(FN-190): complete Step 4 — add db:migrate and db:migrate:dist scripts - feat(FN-190): complete Step 3 — write migrate runner with bootstrap logic - feat(FN-190): complete Step 2 — generate baseline Drizzle migration (46 tables) - feat(FN-190): complete Step 1 — move drizzle-kit to production dependencies Files changed: .github/workflows/deploy.yml | 2 +- Dockerfile | 6 +- apps/api/drizzle/0000_brief_guardian.sql | 658 +++ apps/api/drizzle/meta/0000_snapshot.json | 5167 +++++++++++++++++++++++ apps/api/drizzle/meta/_journal.json | 13 + apps/api/package.json | 4 +- apps/api/src/database/__tests__/migrate.spec.ts | 179 + apps/api/src/database/migrate.ts | 160 + apps/api/start.sh | 8 + docs/INDEX.md | 24 +- pnpm-lock.yaml | 6 +- 11 files changed, 6220 insertions(+), 7 deletions(-) Fusion-Task-Id: FN-190
This commit is contained in:
658
apps/api/drizzle/0000_brief_guardian.sql
Normal file
658
apps/api/drizzle/0000_brief_guardian.sql
Normal file
@@ -0,0 +1,658 @@
|
||||
CREATE TABLE "accounts" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"account_id" text NOT NULL,
|
||||
"provider_id" varchar(50) NOT NULL,
|
||||
"access_token" text,
|
||||
"refresh_token" text,
|
||||
"access_token_expires_at" timestamp with time zone,
|
||||
"refresh_token_expires_at" timestamp with time zone,
|
||||
"scope" text,
|
||||
"id_token" text,
|
||||
"password" text,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "brands" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"name" varchar(100) NOT NULL,
|
||||
"slug" varchar(100) NOT NULL,
|
||||
"logo_url" text,
|
||||
"is_active" boolean DEFAULT true NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "catalog_vehicles" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"source" varchar(20) DEFAULT 'pl24' NOT NULL,
|
||||
"service_name" varchar(100) NOT NULL,
|
||||
"brand_name" varchar(100) NOT NULL,
|
||||
"brand_id" uuid,
|
||||
"model" varchar(255) NOT NULL,
|
||||
"year" varchar(50),
|
||||
"engine" varchar(255),
|
||||
"body_type" varchar(100),
|
||||
"transmission" varchar(100),
|
||||
"market" varchar(100),
|
||||
"service_vehicle_id" varchar(255) NOT NULL,
|
||||
"catalog_path" text,
|
||||
"architecture" varchar(30),
|
||||
"metadata" jsonb,
|
||||
"categories_fetched" boolean DEFAULT false NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "categories" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"vehicle_id" uuid,
|
||||
"catalog_vehicle_id" uuid,
|
||||
"name" varchar(500) NOT NULL,
|
||||
"name_original" varchar(500),
|
||||
"parent_id" uuid,
|
||||
"external_id" text,
|
||||
"link_path" text,
|
||||
"link_wid" varchar(100),
|
||||
"unavailable" boolean DEFAULT false NOT NULL,
|
||||
"source" varchar(20) DEFAULT 'pl24' NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "changelog_entries" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"stage" varchar(10) DEFAULT 'prod' NOT NULL,
|
||||
"title" varchar(255) NOT NULL,
|
||||
"description" text NOT NULL,
|
||||
"published_at" timestamp with time zone NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_category_translations" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"original_name" varchar(500) NOT NULL,
|
||||
"translated_name" varchar(500) NOT NULL,
|
||||
"is_manual" boolean DEFAULT false NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "oem_code_copies" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"oem_code" varchar(100) NOT NULL,
|
||||
"part_id" uuid,
|
||||
"vehicle_id" uuid,
|
||||
"category_id" uuid,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "parts" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"vehicle_id" uuid,
|
||||
"catalog_vehicle_id" uuid,
|
||||
"category_id" uuid NOT NULL,
|
||||
"oem_code" varchar(100) NOT NULL,
|
||||
"name" varchar(500) NOT NULL,
|
||||
"name_original" varchar(500),
|
||||
"description" text,
|
||||
"quantity" integer,
|
||||
"position" varchar(100),
|
||||
"hotspot_index" integer,
|
||||
"unavailable" boolean DEFAULT false NOT NULL,
|
||||
"remark" text,
|
||||
"model_codes" varchar(500),
|
||||
"presel" boolean DEFAULT false NOT NULL,
|
||||
"price" numeric(10, 2),
|
||||
"currency" varchar(3),
|
||||
"source" varchar(20) DEFAULT 'pl24' NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "password_reset_tokens" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"token" text NOT NULL,
|
||||
"expires_at" timestamp with time zone NOT NULL,
|
||||
"used_at" timestamp with time zone,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "payments" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"subscription_id" uuid NOT NULL,
|
||||
"amount" integer NOT NULL,
|
||||
"currency" varchar(3) DEFAULT 'TRY' NOT NULL,
|
||||
"method" varchar(20) NOT NULL,
|
||||
"status" varchar(20) DEFAULT 'pending' NOT NULL,
|
||||
"iyzico_payment_id" text,
|
||||
"eft_receipt_url" text,
|
||||
"admin_note" text,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "plans" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"name" varchar(100) NOT NULL,
|
||||
"brand_count" integer NOT NULL,
|
||||
"price_monthly" integer NOT NULL,
|
||||
"price_yearly" integer NOT NULL,
|
||||
"is_active" boolean DEFAULT true NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "query_logs" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"vin" varchar(17) NOT NULL,
|
||||
"brand_id" uuid,
|
||||
"source" varchar(20),
|
||||
"success" boolean DEFAULT true NOT NULL,
|
||||
"error_message" text,
|
||||
"response_time_ms" integer,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "referrals" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"referrer_id" uuid NOT NULL,
|
||||
"referred_id" uuid NOT NULL,
|
||||
"reward_applied" boolean DEFAULT false NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "schema_pics" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"category_id" uuid NOT NULL,
|
||||
"image_url" text NOT NULL,
|
||||
"hotspots" jsonb DEFAULT '[]' NOT NULL,
|
||||
"source" varchar(20) DEFAULT 'pl24' NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "sessions" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"token" text NOT NULL,
|
||||
"expires_at" timestamp with time zone NOT NULL,
|
||||
"ip_address" varchar(45),
|
||||
"user_agent" text,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "user_brands" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"subscription_id" uuid NOT NULL,
|
||||
"brand_id" uuid NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "user_subscriptions" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"plan_id" uuid NOT NULL,
|
||||
"status" varchar(20) DEFAULT 'pending' NOT NULL,
|
||||
"billing_period" varchar(10) DEFAULT 'monthly' NOT NULL,
|
||||
"start_date" timestamp with time zone,
|
||||
"end_date" timestamp with time zone,
|
||||
"cancelled_at" timestamp with time zone,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "user_vehicles" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"vehicle_id" uuid NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"last_accessed_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "users" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"email" varchar(255) NOT NULL,
|
||||
"email_verified" boolean DEFAULT false NOT NULL,
|
||||
"image" text,
|
||||
"role" varchar(20) DEFAULT 'user' NOT NULL,
|
||||
"referral_code" varchar(20),
|
||||
"referred_by" uuid,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "vehicles" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"vin" varchar(17) NOT NULL,
|
||||
"brand_id" uuid,
|
||||
"brand_name" varchar(100),
|
||||
"model" varchar(255),
|
||||
"year" integer,
|
||||
"engine" varchar(255),
|
||||
"transmission" varchar(100),
|
||||
"body_type" varchar(100),
|
||||
"market" varchar(100),
|
||||
"raw_data" jsonb,
|
||||
"source" varchar(20) DEFAULT 'pl24' NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "verifications" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"identifier" text NOT NULL,
|
||||
"value" text NOT NULL,
|
||||
"expires_at" timestamp with time zone NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_catalogs" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"catalog_id" varchar(100) NOT NULL,
|
||||
"code" varchar(50),
|
||||
"brand_name" varchar(100) NOT NULL,
|
||||
"description" text,
|
||||
"source_id" integer,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_part_groups" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"emex_catalog_id" uuid,
|
||||
"group_id" varchar(100) NOT NULL,
|
||||
"name" varchar(500) NOT NULL,
|
||||
"name_original" varchar(500),
|
||||
"parent_group_id" varchar(100),
|
||||
"sort_order" integer,
|
||||
"has_parts" boolean DEFAULT false,
|
||||
"has_children" boolean DEFAULT false,
|
||||
"source_id" integer,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_part_images" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"emex_part_id" uuid,
|
||||
"image_url" text NOT NULL,
|
||||
"original_url" text,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_part_numbers" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"emex_part_id" uuid,
|
||||
"oem_code" varchar(100) NOT NULL,
|
||||
"is_main" boolean DEFAULT true NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_parts" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"emex_catalog_id" uuid,
|
||||
"group_id" uuid,
|
||||
"part_id" varchar(100),
|
||||
"part_number" varchar(100),
|
||||
"name" varchar(500) NOT NULL,
|
||||
"name_original" varchar(500),
|
||||
"description" text,
|
||||
"oem_number" varchar(100),
|
||||
"hotspot_index" integer,
|
||||
"raw_data" jsonb,
|
||||
"source_id" integer,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_schema_pics" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"group_id" uuid,
|
||||
"image_url" text,
|
||||
"original_url" text,
|
||||
"local_path" varchar(500),
|
||||
"hotspots" jsonb DEFAULT '[]' NOT NULL,
|
||||
"width" integer,
|
||||
"height" integer,
|
||||
"sort_order" integer DEFAULT 0,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_scrape_sessions" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"vin" varchar(17) NOT NULL,
|
||||
"status" varchar(20) DEFAULT 'pending' NOT NULL,
|
||||
"job_id" varchar(100),
|
||||
"result" jsonb,
|
||||
"error_message" text,
|
||||
"started_at" timestamp with time zone,
|
||||
"completed_at" timestamp with time zone,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_vehicle_group_links" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"emex_vehicle_id" uuid NOT NULL,
|
||||
"emex_group_id" uuid NOT NULL,
|
||||
"ssd" text,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_vehicle_part_links" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"emex_vehicle_id" uuid NOT NULL,
|
||||
"emex_part_id" uuid NOT NULL,
|
||||
"emex_group_id" uuid,
|
||||
"quantity" integer,
|
||||
"position" varchar(100),
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_vehicle_vins" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"emex_vehicle_id" uuid,
|
||||
"vin" varchar(17) NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_vehicles" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"catalog_id" uuid,
|
||||
"vehicle_id" varchar(100) NOT NULL,
|
||||
"name" varchar(500),
|
||||
"model_code" varchar(100),
|
||||
"engine" varchar(255),
|
||||
"engine_code" varchar(100),
|
||||
"body_type" varchar(100),
|
||||
"transmission" varchar(100),
|
||||
"drive_type" varchar(100),
|
||||
"fuel_type" varchar(100),
|
||||
"year_from" integer,
|
||||
"year_to" integer,
|
||||
"ssd" text,
|
||||
"options_raw" text,
|
||||
"raw_data" jsonb,
|
||||
"source_id" integer,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pcat_part_groups" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"catalog_id" text NOT NULL,
|
||||
"parent_id" text,
|
||||
"name" text NOT NULL,
|
||||
"img_url" text,
|
||||
"has_subgroups" boolean DEFAULT false NOT NULL,
|
||||
"has_parts" boolean DEFAULT false NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pcat_parts" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
"notice" text,
|
||||
"description" text,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pcat_schema_pics" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"group_id" text NOT NULL,
|
||||
"car_id" text NOT NULL,
|
||||
"img_url" text NOT NULL,
|
||||
"img_description" text,
|
||||
"hotspots" jsonb,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pcat_vehicles" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"catalog_id" text NOT NULL,
|
||||
"vin" varchar(17),
|
||||
"name" text NOT NULL,
|
||||
"description" text,
|
||||
"parameters" jsonb,
|
||||
"raw_data" jsonb,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pl24_catalogs" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"catalog_id" varchar(100) NOT NULL,
|
||||
"brand_name" varchar(100) NOT NULL,
|
||||
"description" text,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pl24_part_groups" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"pl24_vehicle_id" uuid,
|
||||
"group_id" varchar(100) NOT NULL,
|
||||
"name" varchar(500) NOT NULL,
|
||||
"parent_group_id" varchar(100),
|
||||
"sort_order" integer,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pl24_part_images" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"pl24_part_id" uuid,
|
||||
"image_url" text NOT NULL,
|
||||
"original_url" text,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pl24_part_numbers" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"pl24_part_id" uuid,
|
||||
"oem_code" varchar(100) NOT NULL,
|
||||
"is_main" boolean DEFAULT true NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pl24_parts" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"pl24_vehicle_id" uuid,
|
||||
"group_id" uuid,
|
||||
"part_id" varchar(100),
|
||||
"name" varchar(500) NOT NULL,
|
||||
"description" text,
|
||||
"quantity" integer,
|
||||
"position" varchar(100),
|
||||
"hotspot_index" integer,
|
||||
"raw_data" jsonb,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pl24_schema_pics" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"group_id" uuid,
|
||||
"image_url" text NOT NULL,
|
||||
"original_url" text,
|
||||
"hotspots" jsonb DEFAULT '[]' NOT NULL,
|
||||
"width" integer,
|
||||
"height" integer,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pl24_vehicle_groups" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"catalog_id" uuid,
|
||||
"group_id" varchar(100) NOT NULL,
|
||||
"name" varchar(500) NOT NULL,
|
||||
"parent_group_id" varchar(100),
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pl24_vehicle_parts" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"pl24_vehicle_id" uuid,
|
||||
"pl24_part_id" uuid,
|
||||
"fitment_info" text,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pl24_vehicle_vins" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"pl24_vehicle_id" uuid,
|
||||
"vin" varchar(17) NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pl24_vehicles" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"catalog_id" uuid,
|
||||
"vehicle_id" varchar(100) NOT NULL,
|
||||
"name" varchar(500),
|
||||
"model_code" varchar(100),
|
||||
"engine" varchar(255),
|
||||
"transmission" varchar(100),
|
||||
"body_type" varchar(100),
|
||||
"market" varchar(100),
|
||||
"year_from" integer,
|
||||
"year_to" integer,
|
||||
"raw_data" jsonb,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "accounts" ADD CONSTRAINT "accounts_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "catalog_vehicles" ADD CONSTRAINT "catalog_vehicles_brand_id_brands_id_fk" FOREIGN KEY ("brand_id") REFERENCES "public"."brands"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "categories" ADD CONSTRAINT "categories_vehicle_id_vehicles_id_fk" FOREIGN KEY ("vehicle_id") REFERENCES "public"."vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "categories" ADD CONSTRAINT "categories_catalog_vehicle_id_catalog_vehicles_id_fk" FOREIGN KEY ("catalog_vehicle_id") REFERENCES "public"."catalog_vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "oem_code_copies" ADD CONSTRAINT "oem_code_copies_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "parts" ADD CONSTRAINT "parts_vehicle_id_vehicles_id_fk" FOREIGN KEY ("vehicle_id") REFERENCES "public"."vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "parts" ADD CONSTRAINT "parts_catalog_vehicle_id_catalog_vehicles_id_fk" FOREIGN KEY ("catalog_vehicle_id") REFERENCES "public"."catalog_vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "parts" ADD CONSTRAINT "parts_category_id_categories_id_fk" FOREIGN KEY ("category_id") REFERENCES "public"."categories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "password_reset_tokens" ADD CONSTRAINT "password_reset_tokens_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "payments" ADD CONSTRAINT "payments_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "payments" ADD CONSTRAINT "payments_subscription_id_user_subscriptions_id_fk" FOREIGN KEY ("subscription_id") REFERENCES "public"."user_subscriptions"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "query_logs" ADD CONSTRAINT "query_logs_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "query_logs" ADD CONSTRAINT "query_logs_brand_id_brands_id_fk" FOREIGN KEY ("brand_id") REFERENCES "public"."brands"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "referrals" ADD CONSTRAINT "referrals_referrer_id_users_id_fk" FOREIGN KEY ("referrer_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "referrals" ADD CONSTRAINT "referrals_referred_id_users_id_fk" FOREIGN KEY ("referred_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "schema_pics" ADD CONSTRAINT "schema_pics_category_id_categories_id_fk" FOREIGN KEY ("category_id") REFERENCES "public"."categories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "sessions" ADD CONSTRAINT "sessions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "user_brands" ADD CONSTRAINT "user_brands_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "user_brands" ADD CONSTRAINT "user_brands_subscription_id_user_subscriptions_id_fk" FOREIGN KEY ("subscription_id") REFERENCES "public"."user_subscriptions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "user_brands" ADD CONSTRAINT "user_brands_brand_id_brands_id_fk" FOREIGN KEY ("brand_id") REFERENCES "public"."brands"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "user_subscriptions" ADD CONSTRAINT "user_subscriptions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "user_subscriptions" ADD CONSTRAINT "user_subscriptions_plan_id_plans_id_fk" FOREIGN KEY ("plan_id") REFERENCES "public"."plans"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "user_vehicles" ADD CONSTRAINT "user_vehicles_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "user_vehicles" ADD CONSTRAINT "user_vehicles_vehicle_id_vehicles_id_fk" FOREIGN KEY ("vehicle_id") REFERENCES "public"."vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "vehicles" ADD CONSTRAINT "vehicles_brand_id_brands_id_fk" FOREIGN KEY ("brand_id") REFERENCES "public"."brands"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_part_groups" ADD CONSTRAINT "emex_part_groups_emex_catalog_id_emex_catalogs_id_fk" FOREIGN KEY ("emex_catalog_id") REFERENCES "public"."emex_catalogs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_part_images" ADD CONSTRAINT "emex_part_images_emex_part_id_emex_parts_id_fk" FOREIGN KEY ("emex_part_id") REFERENCES "public"."emex_parts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_part_numbers" ADD CONSTRAINT "emex_part_numbers_emex_part_id_emex_parts_id_fk" FOREIGN KEY ("emex_part_id") REFERENCES "public"."emex_parts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_parts" ADD CONSTRAINT "emex_parts_emex_catalog_id_emex_catalogs_id_fk" FOREIGN KEY ("emex_catalog_id") REFERENCES "public"."emex_catalogs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_parts" ADD CONSTRAINT "emex_parts_group_id_emex_part_groups_id_fk" FOREIGN KEY ("group_id") REFERENCES "public"."emex_part_groups"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_schema_pics" ADD CONSTRAINT "emex_schema_pics_group_id_emex_part_groups_id_fk" FOREIGN KEY ("group_id") REFERENCES "public"."emex_part_groups"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_vehicle_group_links" ADD CONSTRAINT "emex_vehicle_group_links_emex_vehicle_id_emex_vehicles_id_fk" FOREIGN KEY ("emex_vehicle_id") REFERENCES "public"."emex_vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_vehicle_group_links" ADD CONSTRAINT "emex_vehicle_group_links_emex_group_id_emex_part_groups_id_fk" FOREIGN KEY ("emex_group_id") REFERENCES "public"."emex_part_groups"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_vehicle_part_links" ADD CONSTRAINT "emex_vehicle_part_links_emex_vehicle_id_emex_vehicles_id_fk" FOREIGN KEY ("emex_vehicle_id") REFERENCES "public"."emex_vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_vehicle_part_links" ADD CONSTRAINT "emex_vehicle_part_links_emex_part_id_emex_parts_id_fk" FOREIGN KEY ("emex_part_id") REFERENCES "public"."emex_parts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_vehicle_part_links" ADD CONSTRAINT "emex_vehicle_part_links_emex_group_id_emex_part_groups_id_fk" FOREIGN KEY ("emex_group_id") REFERENCES "public"."emex_part_groups"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_vehicle_vins" ADD CONSTRAINT "emex_vehicle_vins_emex_vehicle_id_emex_vehicles_id_fk" FOREIGN KEY ("emex_vehicle_id") REFERENCES "public"."emex_vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_vehicles" ADD CONSTRAINT "emex_vehicles_catalog_id_emex_catalogs_id_fk" FOREIGN KEY ("catalog_id") REFERENCES "public"."emex_catalogs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "pl24_part_groups" ADD CONSTRAINT "pl24_part_groups_pl24_vehicle_id_pl24_vehicles_id_fk" FOREIGN KEY ("pl24_vehicle_id") REFERENCES "public"."pl24_vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "pl24_part_images" ADD CONSTRAINT "pl24_part_images_pl24_part_id_pl24_parts_id_fk" FOREIGN KEY ("pl24_part_id") REFERENCES "public"."pl24_parts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "pl24_part_numbers" ADD CONSTRAINT "pl24_part_numbers_pl24_part_id_pl24_parts_id_fk" FOREIGN KEY ("pl24_part_id") REFERENCES "public"."pl24_parts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "pl24_parts" ADD CONSTRAINT "pl24_parts_pl24_vehicle_id_pl24_vehicles_id_fk" FOREIGN KEY ("pl24_vehicle_id") REFERENCES "public"."pl24_vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "pl24_parts" ADD CONSTRAINT "pl24_parts_group_id_pl24_part_groups_id_fk" FOREIGN KEY ("group_id") REFERENCES "public"."pl24_part_groups"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "pl24_schema_pics" ADD CONSTRAINT "pl24_schema_pics_group_id_pl24_part_groups_id_fk" FOREIGN KEY ("group_id") REFERENCES "public"."pl24_part_groups"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "pl24_vehicle_groups" ADD CONSTRAINT "pl24_vehicle_groups_catalog_id_pl24_catalogs_id_fk" FOREIGN KEY ("catalog_id") REFERENCES "public"."pl24_catalogs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "pl24_vehicle_parts" ADD CONSTRAINT "pl24_vehicle_parts_pl24_vehicle_id_pl24_vehicles_id_fk" FOREIGN KEY ("pl24_vehicle_id") REFERENCES "public"."pl24_vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "pl24_vehicle_parts" ADD CONSTRAINT "pl24_vehicle_parts_pl24_part_id_pl24_parts_id_fk" FOREIGN KEY ("pl24_part_id") REFERENCES "public"."pl24_parts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "pl24_vehicle_vins" ADD CONSTRAINT "pl24_vehicle_vins_pl24_vehicle_id_pl24_vehicles_id_fk" FOREIGN KEY ("pl24_vehicle_id") REFERENCES "public"."pl24_vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "pl24_vehicles" ADD CONSTRAINT "pl24_vehicles_catalog_id_pl24_catalogs_id_fk" FOREIGN KEY ("catalog_id") REFERENCES "public"."pl24_catalogs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE INDEX "accounts_user_id_idx" ON "accounts" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "brands_slug_idx" ON "brands" USING btree ("slug");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "catalog_vehicles_source_svc_vid_idx" ON "catalog_vehicles" USING btree ("source","service_name","service_vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX "catalog_vehicles_brand_name_idx" ON "catalog_vehicles" USING btree ("brand_name");--> statement-breakpoint
|
||||
CREATE INDEX "catalog_vehicles_service_name_idx" ON "catalog_vehicles" USING btree ("service_name");--> statement-breakpoint
|
||||
CREATE INDEX "categories_vehicle_id_idx" ON "categories" USING btree ("vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX "categories_catalog_vehicle_id_idx" ON "categories" USING btree ("catalog_vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX "categories_parent_id_idx" ON "categories" USING btree ("parent_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "categories_vehicle_name_source_idx" ON "categories" USING btree ("vehicle_id","catalog_vehicle_id","name","source");--> statement-breakpoint
|
||||
CREATE INDEX "changelog_entries_published_at_idx" ON "changelog_entries" USING btree ("published_at");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "emex_translations_original_name_idx" ON "emex_category_translations" USING btree ("original_name");--> statement-breakpoint
|
||||
CREATE INDEX "oem_code_copies_user_id_created_at_idx" ON "oem_code_copies" USING btree ("user_id","created_at");--> statement-breakpoint
|
||||
CREATE INDEX "oem_code_copies_oem_code_idx" ON "oem_code_copies" USING btree ("oem_code");--> statement-breakpoint
|
||||
CREATE INDEX "oem_code_copies_created_at_idx" ON "oem_code_copies" USING btree ("created_at");--> statement-breakpoint
|
||||
CREATE INDEX "parts_vehicle_id_idx" ON "parts" USING btree ("vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX "parts_catalog_vehicle_id_idx" ON "parts" USING btree ("catalog_vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX "parts_category_id_idx" ON "parts" USING btree ("category_id");--> statement-breakpoint
|
||||
CREATE INDEX "parts_oem_code_idx" ON "parts" USING btree ("oem_code");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "password_reset_tokens_token_idx" ON "password_reset_tokens" USING btree ("token");--> statement-breakpoint
|
||||
CREATE INDEX "payments_user_id_idx" ON "payments" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE INDEX "payments_status_idx" ON "payments" USING btree ("status");--> statement-breakpoint
|
||||
CREATE INDEX "query_logs_user_id_created_at_idx" ON "query_logs" USING btree ("user_id","created_at");--> statement-breakpoint
|
||||
CREATE INDEX "query_logs_vin_idx" ON "query_logs" USING btree ("vin");--> statement-breakpoint
|
||||
CREATE INDEX "referrals_referrer_id_idx" ON "referrals" USING btree ("referrer_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "referrals_referred_id_idx" ON "referrals" USING btree ("referred_id");--> statement-breakpoint
|
||||
CREATE INDEX "schema_pics_category_id_idx" ON "schema_pics" USING btree ("category_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "sessions_token_idx" ON "sessions" USING btree ("token");--> statement-breakpoint
|
||||
CREATE INDEX "user_brands_user_id_idx" ON "user_brands" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "user_brands_unique_idx" ON "user_brands" USING btree ("user_id","subscription_id","brand_id");--> statement-breakpoint
|
||||
CREATE INDEX "user_subscriptions_user_id_idx" ON "user_subscriptions" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE INDEX "user_subscriptions_status_idx" ON "user_subscriptions" USING btree ("status");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "user_vehicles_user_vehicle_idx" ON "user_vehicles" USING btree ("user_id","vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX "user_vehicles_user_id_idx" ON "user_vehicles" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "users_email_idx" ON "users" USING btree ("email");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "users_referral_code_idx" ON "users" USING btree ("referral_code");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "vehicles_vin_unique_idx" ON "vehicles" USING btree ("vin");--> statement-breakpoint
|
||||
CREATE INDEX "verifications_identifier_idx" ON "verifications" USING btree ("identifier");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "emex_catalogs_catalog_id_idx" ON "emex_catalogs" USING btree ("catalog_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_catalogs_code_idx" ON "emex_catalogs" USING btree ("code");--> statement-breakpoint
|
||||
CREATE INDEX "emex_part_groups_catalog_id_idx" ON "emex_part_groups" USING btree ("emex_catalog_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_part_groups_group_id_idx" ON "emex_part_groups" USING btree ("group_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "emex_part_groups_catalog_group_idx" ON "emex_part_groups" USING btree ("emex_catalog_id","group_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_part_images_part_id_idx" ON "emex_part_images" USING btree ("emex_part_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_part_numbers_part_id_idx" ON "emex_part_numbers" USING btree ("emex_part_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_part_numbers_oem_code_idx" ON "emex_part_numbers" USING btree ("oem_code");--> statement-breakpoint
|
||||
CREATE INDEX "emex_parts_catalog_id_idx" ON "emex_parts" USING btree ("emex_catalog_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_parts_group_id_idx" ON "emex_parts" USING btree ("group_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_parts_part_number_idx" ON "emex_parts" USING btree ("part_number");--> statement-breakpoint
|
||||
CREATE INDEX "emex_parts_oem_number_idx" ON "emex_parts" USING btree ("oem_number");--> statement-breakpoint
|
||||
CREATE INDEX "emex_schema_pics_group_id_idx" ON "emex_schema_pics" USING btree ("group_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "emex_schema_pics_group_path_idx" ON "emex_schema_pics" USING btree ("group_id","local_path");--> statement-breakpoint
|
||||
CREATE INDEX "emex_scrape_sessions_vin_idx" ON "emex_scrape_sessions" USING btree ("vin");--> statement-breakpoint
|
||||
CREATE INDEX "emex_scrape_sessions_status_idx" ON "emex_scrape_sessions" USING btree ("status");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "emex_vgl_vehicle_group_idx" ON "emex_vehicle_group_links" USING btree ("emex_vehicle_id","emex_group_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_vgl_group_idx" ON "emex_vehicle_group_links" USING btree ("emex_group_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "emex_vpl_vehicle_part_group_idx" ON "emex_vehicle_part_links" USING btree ("emex_vehicle_id","emex_part_id","emex_group_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_vpl_part_idx" ON "emex_vehicle_part_links" USING btree ("emex_part_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_vpl_group_idx" ON "emex_vehicle_part_links" USING btree ("emex_group_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "emex_vehicle_vins_vin_idx" ON "emex_vehicle_vins" USING btree ("vin");--> statement-breakpoint
|
||||
CREATE INDEX "emex_vehicle_vins_vehicle_id_idx" ON "emex_vehicle_vins" USING btree ("emex_vehicle_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "emex_vehicles_vehicle_id_idx" ON "emex_vehicles" USING btree ("vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_vehicles_catalog_id_idx" ON "emex_vehicles" USING btree ("catalog_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_vehicles_name_idx" ON "emex_vehicles" USING btree ("name");--> statement-breakpoint
|
||||
CREATE INDEX "emex_vehicles_source_id_idx" ON "emex_vehicles" USING btree ("source_id");--> statement-breakpoint
|
||||
CREATE INDEX "pcat_part_groups_catalog_id_idx" ON "pcat_part_groups" USING btree ("catalog_id");--> statement-breakpoint
|
||||
CREATE INDEX "pcat_part_groups_parent_id_idx" ON "pcat_part_groups" USING btree ("parent_id");--> statement-breakpoint
|
||||
CREATE INDEX "pcat_parts_name_idx" ON "pcat_parts" USING btree ("name");--> statement-breakpoint
|
||||
CREATE INDEX "pcat_schema_pics_group_car_idx" ON "pcat_schema_pics" USING btree ("group_id","car_id");--> statement-breakpoint
|
||||
CREATE INDEX "pcat_vehicles_vin_idx" ON "pcat_vehicles" USING btree ("vin");--> statement-breakpoint
|
||||
CREATE INDEX "pcat_vehicles_catalog_id_idx" ON "pcat_vehicles" USING btree ("catalog_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "pl24_catalogs_catalog_id_idx" ON "pl24_catalogs" USING btree ("catalog_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_part_groups_vehicle_id_idx" ON "pl24_part_groups" USING btree ("pl24_vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_part_groups_group_id_idx" ON "pl24_part_groups" USING btree ("group_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_part_images_part_id_idx" ON "pl24_part_images" USING btree ("pl24_part_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_part_numbers_part_id_idx" ON "pl24_part_numbers" USING btree ("pl24_part_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_part_numbers_oem_code_idx" ON "pl24_part_numbers" USING btree ("oem_code");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_parts_vehicle_id_idx" ON "pl24_parts" USING btree ("pl24_vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_parts_group_id_idx" ON "pl24_parts" USING btree ("group_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_schema_pics_group_id_idx" ON "pl24_schema_pics" USING btree ("group_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_vehicle_groups_catalog_id_idx" ON "pl24_vehicle_groups" USING btree ("catalog_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_vehicle_groups_group_id_idx" ON "pl24_vehicle_groups" USING btree ("group_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_vehicle_parts_vehicle_id_idx" ON "pl24_vehicle_parts" USING btree ("pl24_vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_vehicle_parts_part_id_idx" ON "pl24_vehicle_parts" USING btree ("pl24_part_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_vehicle_vins_vin_idx" ON "pl24_vehicle_vins" USING btree ("vin");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_vehicle_vins_vehicle_id_idx" ON "pl24_vehicle_vins" USING btree ("pl24_vehicle_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "pl24_vehicles_vehicle_id_idx" ON "pl24_vehicles" USING btree ("vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_vehicles_catalog_id_idx" ON "pl24_vehicles" USING btree ("catalog_id");
|
||||
5167
apps/api/drizzle/meta/0000_snapshot.json
Normal file
5167
apps/api/drizzle/meta/0000_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
13
apps/api/drizzle/meta/_journal.json
Normal file
13
apps/api/drizzle/meta/_journal.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "7",
|
||||
"when": 1778535325959,
|
||||
"tag": "0000_brief_guardian",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -14,6 +14,8 @@
|
||||
"db:push": "drizzle-kit push",
|
||||
"db:studio": "drizzle-kit studio",
|
||||
"db:seed": "tsx src/database/seed.ts",
|
||||
"db:migrate": "tsx src/database/migrate.ts",
|
||||
"db:migrate:dist": "node dist/database/migrate.js",
|
||||
"worker": "node dist/worker.js",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"clean": "rm -rf dist"
|
||||
@@ -52,6 +54,7 @@
|
||||
"bullmq": "^5.30.0",
|
||||
"bullmq-otel": "^1.2.0",
|
||||
"dotenv": "^16.4.0",
|
||||
"drizzle-kit": "^0.31.4",
|
||||
"drizzle-orm": "^0.41.0",
|
||||
"helmet": "^8.1.0",
|
||||
"ioredis": "^5.4.0",
|
||||
@@ -70,7 +73,6 @@
|
||||
"@types/multer": "^1.4.0",
|
||||
"@types/node": "^22.0.0",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
"drizzle-kit": "^0.31.4",
|
||||
"playwright": "^1.50.0",
|
||||
"tsx": "^4.19.0",
|
||||
"typescript": "^5.7.0",
|
||||
|
||||
179
apps/api/src/database/__tests__/migrate.spec.ts
Normal file
179
apps/api/src/database/__tests__/migrate.spec.ts
Normal file
@@ -0,0 +1,179 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
/**
|
||||
* Unit tests for the migration runner's bootstrap logic.
|
||||
*
|
||||
* We test bootstrapExistingDb() directly with a mock postgres.Sql client
|
||||
* and mock the filesystem via vi.mock("node:fs").
|
||||
*/
|
||||
|
||||
vi.mock("node:fs", async () => {
|
||||
const actual = await vi.importActual<typeof import("node:fs")>("node:fs");
|
||||
return {
|
||||
...actual,
|
||||
readFileSync: vi.fn((path: string, _encoding: string) => {
|
||||
// Default: no file — will cause errors unless overridden in test setup
|
||||
throw new Error(`ENOENT: mock file not set up: ${path}`);
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
import { bootstrapExistingDb } from "../../database/migrate";
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
interface MockSqlClient {
|
||||
(queryParts: TemplateStringsArray, ...values: unknown[]): Promise<unknown[]>;
|
||||
unsafe: ReturnType<typeof vi.fn>;
|
||||
end: ReturnType<typeof vi.fn>;
|
||||
__inserts: string[];
|
||||
__unsafeCalls: string[];
|
||||
}
|
||||
|
||||
function createMockSqlClient(): MockSqlClient {
|
||||
let queryHandler: (template: string) => unknown[] = () => [];
|
||||
|
||||
const sql = vi.fn(async (queryParts: TemplateStringsArray, ..._values: unknown[]) => {
|
||||
const queryText = queryParts.join(" ").trim();
|
||||
// Capture INSERT queries directly
|
||||
if (queryText.includes("INSERT INTO")) {
|
||||
(sql as any).__inserts.push(queryText);
|
||||
return [];
|
||||
}
|
||||
return queryHandler(queryText);
|
||||
}) as unknown as MockSqlClient;
|
||||
|
||||
(sql as any).unsafe = vi.fn(async (q: string) => {
|
||||
((sql as any).__unsafeCalls as string[]).push(q);
|
||||
});
|
||||
(sql as any).end = vi.fn().mockResolvedValue(undefined);
|
||||
(sql as any).__inserts = [];
|
||||
(sql as any).__unsafeCalls = [];
|
||||
|
||||
// Allow tests to set queryHandler
|
||||
(sql as any)._setQueryHandler = (handler: typeof queryHandler) => {
|
||||
queryHandler = handler;
|
||||
};
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
// ── Tests ──
|
||||
|
||||
describe("bootstrapExistingDb", () => {
|
||||
let sqlClient: MockSqlClient;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
sqlClient = createMockSqlClient();
|
||||
});
|
||||
|
||||
describe("when __drizzle_migrations already exists", () => {
|
||||
it("should skip bootstrap (no CREATE, no INSERT)", async () => {
|
||||
(sqlClient as any)._setQueryHandler((queryText: string) => {
|
||||
if (
|
||||
queryText.includes("information_schema") &&
|
||||
queryText.includes("__drizzle_migrations")
|
||||
) {
|
||||
return [{ table_name: "__drizzle_migrations" }];
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
await bootstrapExistingDb(sqlClient as any);
|
||||
|
||||
// Should NOT create schema or table
|
||||
expect((sqlClient as any).__unsafeCalls).toEqual([]);
|
||||
|
||||
// Should NOT insert any migration records
|
||||
expect((sqlClient as any).__inserts).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when __drizzle_migrations is missing but users exists (existing DB)", () => {
|
||||
it("should create schema, create table, mark baseline entries as applied", async () => {
|
||||
(sqlClient as any)._setQueryHandler((queryText: string) => {
|
||||
if (
|
||||
queryText.includes("information_schema") &&
|
||||
queryText.includes("__drizzle_migrations")
|
||||
) {
|
||||
return []; // does not exist
|
||||
}
|
||||
if (queryText.includes("information_schema") && queryText.includes("'users'")) {
|
||||
return [{ table_name: "users" }]; // users exist
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
// Set up the filesystem mocks
|
||||
const mockRFS = readFileSync as ReturnType<typeof vi.fn>;
|
||||
mockRFS.mockImplementation((path: string, _encoding: string) => {
|
||||
if (path.includes("_journal.json")) {
|
||||
return JSON.stringify({
|
||||
version: "7",
|
||||
dialect: "postgresql",
|
||||
entries: [
|
||||
{
|
||||
idx: 0,
|
||||
version: "7",
|
||||
when: 1778535325959,
|
||||
tag: "0000_brief_guardian",
|
||||
breakpoints: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
if (path.includes("0000_brief_guardian.sql")) {
|
||||
return "CREATE TABLE foo";
|
||||
}
|
||||
throw new Error(`Unexpected readFileSync: ${path}`);
|
||||
});
|
||||
|
||||
await bootstrapExistingDb(sqlClient as any);
|
||||
|
||||
// Verify CREATE SCHEMA was called
|
||||
expect((sqlClient as any).__unsafeCalls).toContain('CREATE SCHEMA IF NOT EXISTS "drizzle"');
|
||||
|
||||
// Verify CREATE TABLE was called
|
||||
const createTableCall = (sqlClient as any).__unsafeCalls.find((c: string) =>
|
||||
c.includes("CREATE TABLE IF NOT EXISTS"),
|
||||
);
|
||||
expect(createTableCall).toBeDefined();
|
||||
expect(createTableCall).toContain("__drizzle_migrations");
|
||||
|
||||
// Verify INSERT was called with a hash
|
||||
expect((sqlClient as any).__inserts.length).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when both __drizzle_migrations and users are missing (fresh DB)", () => {
|
||||
it("should skip bootstrap entirely", async () => {
|
||||
(sqlClient as any)._setQueryHandler((_queryText: string) => {
|
||||
return []; // nothing exists
|
||||
});
|
||||
|
||||
await bootstrapExistingDb(sqlClient as any);
|
||||
|
||||
// No schema creation
|
||||
expect((sqlClient as any).__unsafeCalls).toEqual([]);
|
||||
// No inserts
|
||||
expect((sqlClient as any).__inserts).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("hash computation", () => {
|
||||
it("should produce the expected SHA256 hash for a known input", () => {
|
||||
const input = "SELECT 1";
|
||||
const hash = createHash("sha256").update(input).digest("hex");
|
||||
expect(hash).toBe("e004ebd5b5532a4b85984a62f8ad48a81aa3460c1ca07701f386135d72cdecf5");
|
||||
});
|
||||
|
||||
it("should produce a different hash for different input", () => {
|
||||
const hashA = createHash("sha256").update("SELECT 1").digest("hex");
|
||||
const hashB = createHash("sha256").update("SELECT 2").digest("hex");
|
||||
expect(hashA).not.toBe(hashB);
|
||||
});
|
||||
});
|
||||
160
apps/api/src/database/migrate.ts
Normal file
160
apps/api/src/database/migrate.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { sql } from "drizzle-orm";
|
||||
import { drizzle } from "drizzle-orm/postgres-js";
|
||||
import { migrate } from "drizzle-orm/postgres-js/migrator";
|
||||
import postgres from "postgres";
|
||||
|
||||
/**
|
||||
* Path to the drizzle migrations folder.
|
||||
*
|
||||
* In development (tsx), __dirname = <repo>/apps/api/src/database,
|
||||
* and the drizzle folder is at <repo>/apps/api/drizzle.
|
||||
*
|
||||
* In production (compiled), __dirname = <app>/apps/api/dist/database,
|
||||
* and the drizzle folder is COPIED to <app>/apps/api/drizzle.
|
||||
*
|
||||
* Both resolve correctly with: join(__dirname, "../../drizzle")
|
||||
*/
|
||||
const migrationsFolder = process.env.MIGRATIONS_FOLDER || join(__dirname, "../../drizzle");
|
||||
|
||||
interface JournalEntry {
|
||||
idx: number;
|
||||
version: string;
|
||||
when: number;
|
||||
tag: string;
|
||||
breakpoints: boolean;
|
||||
}
|
||||
|
||||
interface Journal {
|
||||
version: string;
|
||||
dialect: string;
|
||||
entries: JournalEntry[];
|
||||
}
|
||||
|
||||
export async function bootstrapExistingDb(sqlClient: postgres.Sql): Promise<void> {
|
||||
const tablesResult = await sqlClient`
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'drizzle'
|
||||
AND table_name = '__drizzle_migrations'
|
||||
`;
|
||||
|
||||
if (tablesResult.length > 0) {
|
||||
// __drizzle_migrations already exists — normal migration run
|
||||
console.log("[migrate] Migration tracking table exists — skipping bootstrap");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if users table exists (canary for existing DB)
|
||||
const usersResult = await sqlClient`
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = 'users'
|
||||
`;
|
||||
|
||||
if (usersResult.length === 0) {
|
||||
// Fresh DB — migrate() will handle everything
|
||||
console.log("[migrate] Fresh database detected — skipping bootstrap");
|
||||
return;
|
||||
}
|
||||
|
||||
// Existing DB with no migration tracking — bootstrap it
|
||||
console.log("[migrate] Bootstrapping existing database...");
|
||||
|
||||
// Create the drizzle schema if it doesn't exist
|
||||
await sqlClient.unsafe(`CREATE SCHEMA IF NOT EXISTS "drizzle"`);
|
||||
|
||||
// Create __drizzle_migrations table matching Drizzle's expected schema
|
||||
await sqlClient.unsafe(`
|
||||
CREATE TABLE IF NOT EXISTS "drizzle"."__drizzle_migrations" (
|
||||
id SERIAL PRIMARY KEY,
|
||||
hash text NOT NULL,
|
||||
created_at bigint
|
||||
)
|
||||
`);
|
||||
|
||||
// Read journal and mark baseline migrations as applied
|
||||
const journalPath = join(migrationsFolder, "meta", "_journal.json");
|
||||
let journal: Journal;
|
||||
|
||||
try {
|
||||
journal = JSON.parse(readFileSync(journalPath, "utf-8")) as Journal;
|
||||
} catch (err) {
|
||||
console.error("[migrate] Failed to read _journal.json:", (err as Error).message);
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (!journal.entries || journal.entries.length === 0) {
|
||||
console.warn("[migrate] _journal.json has no entries — no migrations to bootstrap");
|
||||
return;
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
for (const entry of journal.entries) {
|
||||
const sqlPath = join(migrationsFolder, `${entry.tag}.sql`);
|
||||
let sqlContent: string;
|
||||
|
||||
try {
|
||||
sqlContent = readFileSync(sqlPath, "utf-8");
|
||||
} catch (err) {
|
||||
console.error(`[migrate] Failed to read migration ${entry.tag}.sql:`, (err as Error).message);
|
||||
throw err;
|
||||
}
|
||||
|
||||
const hash = createHash("sha256").update(sqlContent).digest("hex");
|
||||
await sqlClient`
|
||||
INSERT INTO "drizzle"."__drizzle_migrations" (hash, created_at)
|
||||
VALUES (${hash}, ${now})
|
||||
`;
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[migrate] Bootstrapped existing DB: marked ${journal.entries.length} baseline migration(s) as applied (no SQL executed)`,
|
||||
);
|
||||
}
|
||||
|
||||
async function run(): Promise<void> {
|
||||
const databaseUrl = process.env.DATABASE_URL;
|
||||
if (!databaseUrl) {
|
||||
console.error("[migrate] DATABASE_URL environment variable is required");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`[migrate] Migrations folder: ${migrationsFolder}`);
|
||||
|
||||
// Single connection for migration
|
||||
const sqlClient = postgres(databaseUrl, { max: 1 });
|
||||
|
||||
try {
|
||||
// Bootstrap existing databases before running the migrator
|
||||
await bootstrapExistingDb(sqlClient);
|
||||
|
||||
// Run Drizzle migrator (idempotent — applies only unapplied migrations)
|
||||
const db = drizzle(sqlClient);
|
||||
await migrate(db, { migrationsFolder });
|
||||
console.log("[migrate] Migration complete");
|
||||
|
||||
await sqlClient.end();
|
||||
process.exit(0);
|
||||
} catch (err) {
|
||||
console.error("[migrate] Migration failed:", (err as Error).message);
|
||||
try {
|
||||
await sqlClient.end();
|
||||
} catch {
|
||||
// ignore close errors during failure
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Only auto-run when executed directly (not when imported in tests)
|
||||
const isMainModule =
|
||||
process.argv[1] &&
|
||||
(process.argv[1].endsWith("migrate.ts") || process.argv[1].endsWith("migrate.js"));
|
||||
|
||||
if (isMainModule) {
|
||||
run();
|
||||
}
|
||||
8
apps/api/start.sh
Executable file
8
apps/api/start.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
echo "[start.sh] Running database migrations..."
|
||||
node --enable-source-maps apps/api/dist/database/migrate.js
|
||||
|
||||
echo "[start.sh] Starting API server..."
|
||||
exec node --enable-source-maps apps/api/dist/main.js
|
||||
Reference in New Issue
Block a user