Add support for webhook route parameters (#1343)

* 🚧 add webhookId to URL

* 🚧 add webhookId to webhook entity, 🔧 refactor migrations

* 🚧 🐘 postgres migration

* 🚧 add mySQL migration

* 🚧 refactor mongoDB

* 🚧 add webhookId to IWebhookDb

* 🚧 starting workflow with dynamic route works

*  production dynamic webhooks complete

* 🎨 fix lint issues

* 🔧 dynamic path for webhook-test complete

* 🎨 fix lint issues

* 🎨 fix typescript issue

*  add error message for dynamic webhook-test

* 🔨 improve handling of leading `/`

* 🚧 add webhookId to URL

* 🚧 add webhookId to webhook entity, 🔧 refactor migrations

* 🚧 🐘 postgres migration

* 🚧 add mySQL migration

* 🚧 refactor mongoDB

* 🚧 add webhookId to IWebhookDb

* 🚧 starting workflow with dynamic route works

*  production dynamic webhooks complete

* 🎨 fix lint issues

* 🔧 dynamic path for webhook-test complete

* 🎨 fix lint issues

* 🎨 fix typescript issue

*  add error message for dynamic webhook-test

* 🔨 improve handling of leading `/`

*  Fix issue that tab-title did not get reset on new workflow

* Revert " Fix issue that tab-title did not get reset on new workflow"

This reverts commit 699d0a8946e08339558c72b2714601329fbf5f2c.

* 🔧 reset params before extraction

* 🐘 removing unique constraint for webhookId

* 🚧 handle multiple webhooks per id

* 🔧 enable webhook-test for multiple WH with same id

* 🐘 add migration for postgres

*  add mysql migration

* 🎨 fix lint issue

Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
Ben Hesseldieck
2021-01-23 20:00:32 +01:00
committed by GitHub
parent 1a68303319
commit d395498882
26 changed files with 327 additions and 88 deletions

View File

@@ -116,12 +116,49 @@ export class ActiveWorkflowRunner {
throw new ResponseHelper.ResponseError('The "activeWorkflows" instance did not get initialized yet.', 404, 404);
}
const webhook = await Db.collections.Webhook?.findOne({ webhookPath: path, method: httpMethod }) as IWebhookDb;
let webhook = await Db.collections.Webhook?.findOne({ webhookPath: path, method: httpMethod }) as IWebhookDb;
let webhookId: string | undefined;
// check if something exist
// check if path is dynamic
if (webhook === undefined) {
// The requested webhook is not registered
throw new ResponseHelper.ResponseError(`The requested webhook "${httpMethod} ${path}" is not registered.`, 404, 404);
// check if a dynamic webhook path exists
const pathElements = path.split('/');
webhookId = pathElements.shift();
const dynamicWebhooks = await Db.collections.Webhook?.find({ webhookId, method: httpMethod, pathLength: pathElements.length });
if (dynamicWebhooks === undefined) {
// The requested webhook is not registered
throw new ResponseHelper.ResponseError(`The requested webhook "${httpMethod} ${path}" is not registered.`, 404, 404);
}
// set webhook to the first webhook result
// if more results have been returned choose the one with the most route-matches
webhook = dynamicWebhooks[0];
if (dynamicWebhooks.length > 1) {
let maxMatches = 0;
const pathElementsSet = new Set(pathElements);
dynamicWebhooks.forEach(dynamicWebhook => {
const intersection =
dynamicWebhook.webhookPath
.split('/')
.reduce((acc, element) => pathElementsSet.has(element) ? acc += 1 : acc, 0);
if (intersection > maxMatches) {
maxMatches = intersection;
webhook = dynamicWebhook;
}
});
if (maxMatches === 0) {
throw new ResponseHelper.ResponseError(`The requested webhook "${httpMethod} ${path}" is not registered.`, 404, 404);
}
}
path = webhook.webhookPath;
// extracting params from path
webhook.webhookPath.split('/').forEach((ele, index) => {
if (ele.startsWith(':')) {
// write params to req.params
req.params[ele.slice(1)] = pathElements[index];
}
});
}
const workflowData = await Db.collections.Workflow!.findOne(webhook.workflowId);
@@ -253,6 +290,15 @@ export class ActiveWorkflowRunner {
method: webhookData.httpMethod,
} as IWebhookDb;
if (webhook.webhookPath.startsWith('/')) {
webhook.webhookPath = webhook.webhookPath.slice(1);
}
if ((path.startsWith(':') || path.includes('/:')) && node.webhookId) {
webhook.webhookId = node.webhookId;
webhook.pathLength = webhook.webhookPath.split('/').length;
}
try {
await Db.collections.Webhook?.insert(webhook);
@@ -273,10 +319,9 @@ export class ActiveWorkflowRunner {
let errorMessage = '';
// if it's a workflow from the the insert
// TODO check if there is standard error code for deplicate key violation that works
// TODO check if there is standard error code for duplicate key violation that works
// with all databases
if (error.name === 'MongoError' || error.name === 'QueryFailedError') {
errorMessage = `The webhook path [${webhook.webhookPath}] and method [${webhook.method}] already exist.`;
} else if (error.detail) {

View File

@@ -32,29 +32,10 @@ export let collections: IDatabaseCollections = {
Webhook: null,
};
import {
CreateIndexStoppedAt1594828256133,
InitialMigration1587669153312,
WebhookModel1589476000887,
} from './databases/postgresdb/migrations';
import {
CreateIndexStoppedAt1594910478695,
InitialMigration1587563438936,
WebhookModel1592679094242,
} from './databases/mongodb/migrations';
import {
CreateIndexStoppedAt1594902918301,
InitialMigration1588157391238,
WebhookModel1592447867632,
} from './databases/mysqldb/migrations';
import {
CreateIndexStoppedAt1594825041918,
InitialMigration1588102412422,
WebhookModel1592445003908,
} from './databases/sqlite/migrations';
import { postgresMigrations } from './databases/postgresdb/migrations';
import { mongodbMigrations } from './databases/mongodb/migrations';
import { mysqlMigrations } from './databases/mysqldb/migrations';
import { sqliteMigrations } from './databases/sqlite/migrations';
import * as path from 'path';
@@ -75,11 +56,7 @@ export async function init(): Promise<IDatabaseCollections> {
entityPrefix,
url: await GenericHelpers.getConfigValue('database.mongodb.connectionUrl') as string,
useNewUrlParser: true,
migrations: [
InitialMigration1587563438936,
WebhookModel1592679094242,
CreateIndexStoppedAt1594910478695,
],
migrations: mongodbMigrations,
migrationsRun: true,
migrationsTableName: `${entityPrefix}migrations`,
};
@@ -112,11 +89,7 @@ export async function init(): Promise<IDatabaseCollections> {
port: await GenericHelpers.getConfigValue('database.postgresdb.port') as number,
username: await GenericHelpers.getConfigValue('database.postgresdb.user') as string,
schema: config.get('database.postgresdb.schema'),
migrations: [
InitialMigration1587669153312,
WebhookModel1589476000887,
CreateIndexStoppedAt1594828256133,
],
migrations: postgresMigrations,
migrationsRun: true,
migrationsTableName: `${entityPrefix}migrations`,
ssl,
@@ -135,11 +108,7 @@ export async function init(): Promise<IDatabaseCollections> {
password: await GenericHelpers.getConfigValue('database.mysqldb.password') as string,
port: await GenericHelpers.getConfigValue('database.mysqldb.port') as number,
username: await GenericHelpers.getConfigValue('database.mysqldb.user') as string,
migrations: [
InitialMigration1588157391238,
WebhookModel1592447867632,
CreateIndexStoppedAt1594902918301,
],
migrations: mysqlMigrations,
migrationsRun: true,
migrationsTableName: `${entityPrefix}migrations`,
};
@@ -151,11 +120,7 @@ export async function init(): Promise<IDatabaseCollections> {
type: 'sqlite',
database: path.join(n8nFolder, 'database.sqlite'),
entityPrefix,
migrations: [
InitialMigration1588102412422,
WebhookModel1592445003908,
CreateIndexStoppedAt1594825041918,
],
migrations: sqliteMigrations,
migrationsRun: true,
migrationsTableName: `${entityPrefix}migrations`,
};

View File

@@ -57,6 +57,8 @@ export interface IWebhookDb {
webhookPath: string;
method: string;
node: string;
webhookId?: string;
pathLength?: number;
}
export interface IWorkflowBase extends IWorkflowBaseWorkflow {

View File

@@ -1693,6 +1693,7 @@ class App {
let response;
try {
delete req.params[0];
response = await this.activeWorkflowRunner.executeWebhook('HEAD', requestUrl, req, res);
} catch (error) {
ResponseHelper.sendErrorResponse(res, error);
@@ -1734,6 +1735,7 @@ class App {
let response;
try {
delete req.params[0];
response = await this.activeWorkflowRunner.executeWebhook('GET', requestUrl, req, res);
} catch (error) {
ResponseHelper.sendErrorResponse(res, error);
@@ -1755,6 +1757,7 @@ class App {
let response;
try {
delete req.params[0];
response = await this.activeWorkflowRunner.executeWebhook('POST', requestUrl, req, res);
} catch (error) {
ResponseHelper.sendErrorResponse(res, error);
@@ -1776,6 +1779,7 @@ class App {
let response;
try {
delete req.params[0];
response = await this.testWebhooks.callTestWebhook('HEAD', requestUrl, req, res);
} catch (error) {
ResponseHelper.sendErrorResponse(res, error);
@@ -1817,6 +1821,7 @@ class App {
let response;
try {
delete req.params[0];
response = await this.testWebhooks.callTestWebhook('GET', requestUrl, req, res);
} catch (error) {
ResponseHelper.sendErrorResponse(res, error);
@@ -1838,6 +1843,7 @@ class App {
let response;
try {
delete req.params[0];
response = await this.testWebhooks.callTestWebhook('POST', requestUrl, req, res);
} catch (error) {
ResponseHelper.sendErrorResponse(res, error);

View File

@@ -54,14 +54,28 @@ export class TestWebhooks {
* @memberof TestWebhooks
*/
async callTestWebhook(httpMethod: WebhookHttpMethod, path: string, request: express.Request, response: express.Response): Promise<IResponseCallbackData> {
const webhookData: IWebhookData | undefined = this.activeWebhooks!.get(httpMethod, path);
let webhookData: IWebhookData | undefined = this.activeWebhooks!.get(httpMethod, path);
// check if path is dynamic
if (webhookData === undefined) {
// The requested webhook is not registered
throw new ResponseHelper.ResponseError(`The requested webhook "${httpMethod} ${path}" is not registered.`, 404, 404);
const pathElements = path.split('/');
const webhookId = pathElements.shift();
webhookData = this.activeWebhooks!.get(httpMethod, pathElements.join('/'), webhookId);
if (webhookData === undefined) {
// The requested webhook is not registered
throw new ResponseHelper.ResponseError(`The requested webhook "${httpMethod} ${path}" is not registered.`, 404, 404);
}
path = webhookData.path;
// extracting params from path
path.split('/').forEach((ele, index) => {
if (ele.startsWith(':')) {
// write params to req.params
request.params[ele.slice(1)] = pathElements[index];
}
});
}
const webhookKey = this.activeWebhooks!.getWebhookKey(webhookData.httpMethod, webhookData.path);
const webhookKey = this.activeWebhooks!.getWebhookKey(webhookData.httpMethod, webhookData.path, webhookData.webhookId) + `|${webhookData.workflowId}`;
// TODO: Clean that duplication up one day and improve code generally
if (this.testWebhookData[webhookKey] === undefined) {
@@ -81,7 +95,7 @@ export class TestWebhooks {
return new Promise(async (resolve, reject) => {
try {
const executionMode = 'manual';
const executionId = await WebhookHelpers.executeWebhook(workflow, webhookData, this.testWebhookData[webhookKey].workflowData, workflowStartNode, executionMode, this.testWebhookData[webhookKey].sessionId, request, response, (error: Error | null, data: IResponseCallbackData) => {
const executionId = await WebhookHelpers.executeWebhook(workflow, webhookData!, this.testWebhookData[webhookKey].workflowData, workflowStartNode, executionMode, this.testWebhookData[webhookKey].sessionId, request, response, (error: Error | null, data: IResponseCallbackData) => {
if (error !== null) {
return reject(error);
}
@@ -98,7 +112,7 @@ export class TestWebhooks {
// Inform editor-ui that webhook got received
if (this.testWebhookData[webhookKey].sessionId !== undefined) {
const pushInstance = Push.getInstance();
pushInstance.send('testWebhookReceived', { workflowId: webhookData.workflowId, executionId }, this.testWebhookData[webhookKey].sessionId!);
pushInstance.send('testWebhookReceived', { workflowId: webhookData!.workflowId, executionId }, this.testWebhookData[webhookKey].sessionId!);
}
} catch (error) {
@@ -158,7 +172,7 @@ export class TestWebhooks {
let key: string;
const activatedKey: string[] = [];
for (const webhookData of webhooks) {
key = this.activeWebhooks!.getWebhookKey(webhookData.httpMethod, webhookData.path);
key = this.activeWebhooks!.getWebhookKey(webhookData.httpMethod, webhookData.path, webhookData.webhookId) + `|${workflowData.id}`;
activatedKey.push(key);

View File

@@ -11,6 +11,8 @@ import {
} from '../../Interfaces';
@Entity()
@Index(["webhookPath", "method"], { unique: true })
@Index(["webhookId", "method"], { unique: true })
export class WebhookEntity implements IWebhookDb {
@ObjectIdColumn()
@@ -27,4 +29,10 @@ export class WebhookEntity implements IWebhookDb {
@Column()
node: string;
@Column()
webhookId: string;
@Column({ nullable: true })
pathLength: number;
}

View File

@@ -1,3 +1,9 @@
export * from './1587563438936-InitialMigration';
export * from './1592679094242-WebhookModel';
export * from './151594910478695-CreateIndexStoppedAt';
import { InitialMigration1587563438936 } from './1587563438936-InitialMigration';
import { WebhookModel1592679094242 } from './1592679094242-WebhookModel';
import { CreateIndexStoppedAt1594910478695 } from './151594910478695-CreateIndexStoppedAt';
export const mongodbMigrations = [
InitialMigration1587563438936,
WebhookModel1592679094242,
CreateIndexStoppedAt1594910478695,
];

View File

@@ -1,6 +1,7 @@
import {
Column,
Entity,
Index,
PrimaryColumn,
} from 'typeorm';
@@ -9,6 +10,7 @@ import {
} from '../../Interfaces';
@Entity()
@Index(['webhookId', 'method', 'pathLength'])
export class WebhookEntity implements IWebhookDb {
@Column()
@@ -22,4 +24,10 @@ export class WebhookEntity implements IWebhookDb {
@Column()
node: string;
@Column({ nullable: true })
webhookId: string;
@Column({ nullable: true })
pathLength: number;
}

View File

@@ -0,0 +1,24 @@
import {MigrationInterface, QueryRunner} from "typeorm";
import * as config from '../../../../config';
export class AddWebhookId1611149998770 implements MigrationInterface {
name = 'AddWebhookId1611149998770';
async up(queryRunner: QueryRunner): Promise<void> {
const tablePrefix = config.get('database.tablePrefix');
await queryRunner.query('ALTER TABLE `' + tablePrefix + 'webhook_entity` ADD `webhookId` varchar(255) NULL');
await queryRunner.query('ALTER TABLE `' + tablePrefix + 'webhook_entity` ADD `pathLength` int NULL');
await queryRunner.query('CREATE INDEX `IDX_' + tablePrefix + '742496f199721a057051acf4c2` ON `' + tablePrefix + 'webhook_entity` (`webhookId`, `method`, `pathLength`)');
}
async down(queryRunner: QueryRunner): Promise<void> {
const tablePrefix = config.get('database.tablePrefix');
await queryRunner.query(
'DROP INDEX `IDX_' + tablePrefix + '742496f199721a057051acf4c2` ON `' + tablePrefix + 'webhook_entity`'
);
await queryRunner.query('ALTER TABLE `' + tablePrefix + 'webhook_entity` DROP COLUMN `pathLength`');
await queryRunner.query('ALTER TABLE `' + tablePrefix + 'webhook_entity` DROP COLUMN `webhookId`');
}
}

View File

@@ -1,3 +1,11 @@
export * from './1588157391238-InitialMigration';
export * from './1592447867632-WebhookModel';
export * from './1594902918301-CreateIndexStoppedAt';
import { InitialMigration1588157391238 } from './1588157391238-InitialMigration';
import { WebhookModel1592447867632 } from './1592447867632-WebhookModel';
import { CreateIndexStoppedAt1594902918301 } from './1594902918301-CreateIndexStoppedAt';
import { AddWebhookId1611149998770 } from './1611149998770-AddWebhookId';
export const mysqlMigrations = [
InitialMigration1588157391238,
WebhookModel1592447867632,
CreateIndexStoppedAt1594902918301,
AddWebhookId1611149998770,
];

View File

@@ -1,6 +1,7 @@
import {
Column,
Entity,
Index,
PrimaryColumn,
} from 'typeorm';
@@ -9,6 +10,7 @@ import {
} from '../../';
@Entity()
@Index(['webhookId', 'method', 'pathLength'])
export class WebhookEntity implements IWebhookDb {
@Column()
@@ -22,4 +24,10 @@ export class WebhookEntity implements IWebhookDb {
@Column()
node: string;
@Column({ nullable: true })
webhookId: string;
@Column({ nullable: true })
pathLength: number;
}

View File

@@ -16,7 +16,7 @@ export class WebhookModel1589476000887 implements MigrationInterface {
tablePrefix = schema + '.' + tablePrefix;
}
await queryRunner.query(`CREATE TABLE ${tablePrefix}webhook_entity ("workflowId" integer NOT NULL, "webhookPath" character varying NOT NULL, "method" character varying NOT NULL, "node" character varying NOT NULL, CONSTRAINT "PK_${tablePrefixIndex}b21ace2e13596ccd87dc9bf4ea6" PRIMARY KEY ("webhookPath", "method"))`, undefined);
await queryRunner.query(`CREATE TABLE IF NOT EXISTS ${tablePrefix}webhook_entity ("workflowId" integer NOT NULL, "webhookPath" character varying NOT NULL, "method" character varying NOT NULL, "node" character varying NOT NULL, CONSTRAINT "PK_${tablePrefixIndex}b21ace2e13596ccd87dc9bf4ea6" PRIMARY KEY ("webhookPath", "method"))`, undefined);
}
async down(queryRunner: QueryRunner): Promise<void> {

View File

@@ -0,0 +1,33 @@
import {MigrationInterface, QueryRunner} from "typeorm";
import * as config from '../../../../config';
export class AddWebhookId1611144599516 implements MigrationInterface {
name = 'AddWebhookId1611144599516';
async up(queryRunner: QueryRunner): Promise<void> {
let tablePrefix = config.get('database.tablePrefix');
const tablePrefixPure = tablePrefix;
const schema = config.get('database.postgresdb.schema');
if (schema) {
tablePrefix = schema + '.' + tablePrefix;
}
await queryRunner.query(`ALTER TABLE ${tablePrefix}webhook_entity ADD "webhookId" character varying`);
await queryRunner.query(`ALTER TABLE ${tablePrefix}webhook_entity ADD "pathLength" integer`);
await queryRunner.query(`CREATE INDEX IF NOT EXISTS IDX_${tablePrefixPure}16f4436789e804e3e1c9eeb240 ON ${tablePrefix}webhook_entity ("webhookId", "method", "pathLength") `);
}
async down(queryRunner: QueryRunner): Promise<void> {
let tablePrefix = config.get('database.tablePrefix');
const tablePrefixPure = tablePrefix;
const schema = config.get('database.postgresdb.schema');
if (schema) {
tablePrefix = schema + '.' + tablePrefix;
}
await queryRunner.query(`DROP INDEX IDX_${tablePrefixPure}16f4436789e804e3e1c9eeb240`);
await queryRunner.query(`ALTER TABLE ${tablePrefix}webhook_entity DROP COLUMN "pathLength"`);
await queryRunner.query(`ALTER TABLE ${tablePrefix}webhook_entity DROP COLUMN "webhookId"`);
}
}

View File

@@ -1,4 +1,11 @@
export * from './1587669153312-InitialMigration';
export * from './1589476000887-WebhookModel';
export * from './1594828256133-CreateIndexStoppedAt';
import { InitialMigration1587669153312 } from './1587669153312-InitialMigration';
import { WebhookModel1589476000887 } from './1589476000887-WebhookModel';
import { CreateIndexStoppedAt1594828256133 } from './1594828256133-CreateIndexStoppedAt';
import { AddWebhookId1611144599516 } from './1611144599516-AddWebhookId';
export const postgresMigrations = [
InitialMigration1587669153312,
WebhookModel1589476000887,
CreateIndexStoppedAt1594828256133,
AddWebhookId1611144599516,
];

View File

@@ -1,6 +1,7 @@
import {
Column,
Entity,
Index,
PrimaryColumn,
} from 'typeorm';
@@ -9,6 +10,7 @@ import {
} from '../../Interfaces';
@Entity()
@Index(['webhookId', 'method', 'pathLength'])
export class WebhookEntity implements IWebhookDb {
@Column()
@@ -22,4 +24,10 @@ export class WebhookEntity implements IWebhookDb {
@Column()
node: string;
@Column({ nullable: true })
webhookId: string;
@Column({ nullable: true })
pathLength: number;
}

View File

@@ -8,7 +8,7 @@ export class CreateIndexStoppedAt1594825041918 implements MigrationInterface {
async up(queryRunner: QueryRunner): Promise<void> {
const tablePrefix = config.get('database.tablePrefix');
await queryRunner.query(`CREATE INDEX "IDX_${tablePrefix}cefb067df2402f6aed0638a6c1" ON "execution_entity" ("stoppedAt") `);
await queryRunner.query(`CREATE INDEX "IDX_${tablePrefix}cefb067df2402f6aed0638a6c1" ON "${tablePrefix}execution_entity" ("stoppedAt") `);
}
async down(queryRunner: QueryRunner): Promise<void> {

View File

@@ -0,0 +1,26 @@
import {MigrationInterface, QueryRunner} from "typeorm";
import * as config from '../../../../config';
export class AddWebhookId1611071044839 implements MigrationInterface {
name = 'AddWebhookId1611071044839';
async up(queryRunner: QueryRunner): Promise<void> {
const tablePrefix = config.get('database.tablePrefix');
await queryRunner.query(`CREATE TABLE "temporary_webhook_entity" ("workflowId" integer NOT NULL, "webhookPath" varchar NOT NULL, "method" varchar NOT NULL, "node" varchar NOT NULL, "webhookId" varchar, "pathLength" integer, PRIMARY KEY ("webhookPath", "method"))`);
await queryRunner.query(`INSERT INTO "temporary_webhook_entity"("workflowId", "webhookPath", "method", "node") SELECT "workflowId", "webhookPath", "method", "node" FROM "${tablePrefix}webhook_entity"`);
await queryRunner.query(`DROP TABLE "${tablePrefix}webhook_entity"`);
await queryRunner.query(`ALTER TABLE "temporary_webhook_entity" RENAME TO "${tablePrefix}webhook_entity"`);
await queryRunner.query(`CREATE INDEX "IDX_${tablePrefix}742496f199721a057051acf4c2" ON "${tablePrefix}webhook_entity" ("webhookId", "method", "pathLength") `);
}
async down(queryRunner: QueryRunner): Promise<void> {
const tablePrefix = config.get('database.tablePrefix');
await queryRunner.query(`DROP INDEX "IDX_${tablePrefix}742496f199721a057051acf4c2"`);
await queryRunner.query(`ALTER TABLE "${tablePrefix}webhook_entity" RENAME TO "temporary_webhook_entity"`);
await queryRunner.query(`CREATE TABLE "${tablePrefix}webhook_entity" ("workflowId" integer NOT NULL, "webhookPath" varchar NOT NULL, "method" varchar NOT NULL, "node" varchar NOT NULL, PRIMARY KEY ("webhookPath", "method"))`);
await queryRunner.query(`INSERT INTO "${tablePrefix}webhook_entity"("workflowId", "webhookPath", "method", "node") SELECT "workflowId", "webhookPath", "method", "node" FROM "temporary_webhook_entity"`);
await queryRunner.query(`DROP TABLE "temporary_webhook_entity"`);
}
}

View File

@@ -1,3 +1,11 @@
export * from './1588102412422-InitialMigration';
export * from './1592445003908-WebhookModel';
export * from './1594825041918-CreateIndexStoppedAt';
import { InitialMigration1588102412422 } from './1588102412422-InitialMigration';
import { WebhookModel1592445003908 } from './1592445003908-WebhookModel';
import { CreateIndexStoppedAt1594825041918 } from './1594825041918-CreateIndexStoppedAt';
import { AddWebhookId1611071044839 } from './1611071044839-AddWebhookId';
export const sqliteMigrations = [
InitialMigration1588102412422,
WebhookModel1592445003908,
CreateIndexStoppedAt1594825041918,
AddWebhookId1611071044839,
];