From 26f485acb67e71e613c92b7c2cc621d12637cddd Mon Sep 17 00:00:00 2001 From: silifatzoffun74-art Date: Wed, 29 Jul 2026 19:07:01 +0100 Subject: [PATCH] feat: add TypeORM migrations for webhook_subscriptions, soroban_event_logs and index Closes #168 Closes #167 Closes #166 --- ...0000000-CreateWebhookSubscriptionsTable.ts | 48 +++++++++++++++++++ ...13000000000-CreateSorobanEventLogsTable.ts | 36 ++++++++++++++ .../1714000000000-AddSorobanEventLogsIndex.ts | 20 ++++++++ src/models/SorobanEventLog.model.ts | 35 ++++++++++++++ src/models/WebhookSubscription.model.ts | 43 +++++++++++++++++ 5 files changed, 182 insertions(+) create mode 100644 src/migrations/1712000000000-CreateWebhookSubscriptionsTable.ts create mode 100644 src/migrations/1713000000000-CreateSorobanEventLogsTable.ts create mode 100644 src/migrations/1714000000000-AddSorobanEventLogsIndex.ts create mode 100644 src/models/SorobanEventLog.model.ts create mode 100644 src/models/WebhookSubscription.model.ts diff --git a/src/migrations/1712000000000-CreateWebhookSubscriptionsTable.ts b/src/migrations/1712000000000-CreateWebhookSubscriptionsTable.ts new file mode 100644 index 0000000..a4e7474 --- /dev/null +++ b/src/migrations/1712000000000-CreateWebhookSubscriptionsTable.ts @@ -0,0 +1,48 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class CreateWebhookSubscriptionsTable1712000000000 + implements MigrationInterface +{ + name = "CreateWebhookSubscriptionsTable1712000000000"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE TABLE "webhook_subscriptions" ( + "id" uuid NOT NULL DEFAULT gen_random_uuid(), + "user_id" uuid NOT NULL, + "url" character varying(255) NOT NULL, + "secret" character varying(64) NOT NULL, + "event_types" jsonb NOT NULL, + "active" boolean NOT NULL DEFAULT true, + "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), + "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), + CONSTRAINT "PK_webhook_subscriptions" PRIMARY KEY ("id") + ); + `); + + await queryRunner.query(` + CREATE INDEX "idx_webhook_subscriptions_user_id" + ON "webhook_subscriptions" ("user_id"); + `); + + await queryRunner.query(` + ALTER TABLE "webhook_subscriptions" + ADD CONSTRAINT "FK_webhook_subscriptions_user" + FOREIGN KEY ("user_id") REFERENCES "users"("id") + ON DELETE CASCADE; + `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE "webhook_subscriptions" + DROP CONSTRAINT "FK_webhook_subscriptions_user"; + `); + + await queryRunner.query(` + DROP INDEX "public"."idx_webhook_subscriptions_user_id"; + `); + + await queryRunner.query(`DROP TABLE "webhook_subscriptions"`); + } +} diff --git a/src/migrations/1713000000000-CreateSorobanEventLogsTable.ts b/src/migrations/1713000000000-CreateSorobanEventLogsTable.ts new file mode 100644 index 0000000..988a2e6 --- /dev/null +++ b/src/migrations/1713000000000-CreateSorobanEventLogsTable.ts @@ -0,0 +1,36 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class CreateSorobanEventLogsTable1713000000000 + implements MigrationInterface +{ + name = "CreateSorobanEventLogsTable1713000000000"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE TABLE "soroban_event_logs" ( + "id" uuid NOT NULL DEFAULT gen_random_uuid(), + "contract_id" character varying(56) NOT NULL, + "ledger_sequence" bigint NOT NULL, + "topic" character varying(64) NOT NULL, + "tx_hash" character varying(64) NOT NULL, + "payload" jsonb, + "processed" boolean NOT NULL DEFAULT false, + "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), + CONSTRAINT "PK_soroban_event_logs" PRIMARY KEY ("id") + ); + `); + + await queryRunner.query(` + CREATE INDEX "idx_soroban_event_logs_processed" + ON "soroban_event_logs" ("processed"); + `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + DROP INDEX "public"."idx_soroban_event_logs_processed"; + `); + + await queryRunner.query(`DROP TABLE "soroban_event_logs"`); + } +} diff --git a/src/migrations/1714000000000-AddSorobanEventLogsIndex.ts b/src/migrations/1714000000000-AddSorobanEventLogsIndex.ts new file mode 100644 index 0000000..077f91e --- /dev/null +++ b/src/migrations/1714000000000-AddSorobanEventLogsIndex.ts @@ -0,0 +1,20 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddSorobanEventLogsIndex1714000000000 + implements MigrationInterface +{ + name = "AddSorobanEventLogsIndex1714000000000"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE INDEX "IDX_SOROBAN_EVENTS_CONTRACT_LEDGER" + ON "soroban_event_logs" ("contract_id", "ledger_sequence" DESC); + `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + DROP INDEX "public"."IDX_SOROBAN_EVENTS_CONTRACT_LEDGER"; + `); + } +} diff --git a/src/models/SorobanEventLog.model.ts b/src/models/SorobanEventLog.model.ts new file mode 100644 index 0000000..d0de18c --- /dev/null +++ b/src/models/SorobanEventLog.model.ts @@ -0,0 +1,35 @@ +import { + Column, + CreateDateColumn, + Entity, + Index, + PrimaryGeneratedColumn, +} from "typeorm"; + +@Entity("soroban_event_logs") +export class SorobanEventLog { + @PrimaryGeneratedColumn("uuid") + id!: string; + + @Column({ name: "contract_id", type: "varchar", length: 56 }) + contractId!: string; + + @Column({ name: "ledger_sequence", type: "bigint" }) + ledgerSequence!: string; + + @Column({ type: "varchar", length: 64 }) + topic!: string; + + @Column({ name: "tx_hash", type: "varchar", length: 64 }) + txHash!: string; + + @Column({ type: "jsonb", nullable: true }) + payload!: Record | null; + + @Column({ type: "boolean", default: false }) + @Index("idx_soroban_event_logs_processed") + processed!: boolean; + + @CreateDateColumn({ name: "created_at", type: "timestamptz" }) + createdAt!: Date; +} diff --git a/src/models/WebhookSubscription.model.ts b/src/models/WebhookSubscription.model.ts new file mode 100644 index 0000000..788b7c9 --- /dev/null +++ b/src/models/WebhookSubscription.model.ts @@ -0,0 +1,43 @@ +import { + Column, + CreateDateColumn, + Entity, + Index, + JoinColumn, + ManyToOne, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from "typeorm"; +import type { User } from "./User.model"; + +@Entity("webhook_subscriptions") +export class WebhookSubscription { + @PrimaryGeneratedColumn("uuid") + id!: string; + + @Column({ name: "user_id", type: "uuid" }) + @Index("idx_webhook_subscriptions_user_id") + userId!: string; + + @Column({ type: "varchar", length: 255 }) + url!: string; + + @Column({ type: "varchar", length: 64 }) + secret!: string; + + @Column({ name: "event_types", type: "jsonb" }) + eventTypes!: string[]; + + @Column({ type: "boolean", default: true }) + active!: boolean; + + @CreateDateColumn({ name: "created_at", type: "timestamptz" }) + createdAt!: Date; + + @UpdateDateColumn({ name: "updated_at", type: "timestamptz" }) + updatedAt!: Date; + + @ManyToOne("User", { onDelete: "CASCADE" }) + @JoinColumn({ name: "user_id" }) + user!: User; +}