feat: introduce singleton webhook URL
This commit is contained in:
@@ -1,12 +1,16 @@
|
||||
import { QueryContext, ModelOptions } from 'objection';
|
||||
import type { RelationMappings } from 'objection';
|
||||
import { AES, enc } from 'crypto-js';
|
||||
import { IRequest } from '@automatisch/types';
|
||||
import App from './app';
|
||||
import Base from './base';
|
||||
import User from './user';
|
||||
import Step from './step';
|
||||
import ExtendedQueryBuilder from './query-builder';
|
||||
import appConfig from '../config/app';
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import Telemetry from '../helpers/telemetry';
|
||||
import globalVariable from '../helpers/global-variable';
|
||||
|
||||
class Connection extends Base {
|
||||
id!: string;
|
||||
@@ -18,6 +22,9 @@ class Connection extends Base {
|
||||
draft: boolean;
|
||||
count?: number;
|
||||
flowCount?: number;
|
||||
user?: User;
|
||||
steps?: Step[];
|
||||
triggerSteps?: Step[];
|
||||
|
||||
static tableName = 'connections';
|
||||
|
||||
@@ -53,6 +60,17 @@ class Connection extends Base {
|
||||
to: 'steps.connection_id',
|
||||
},
|
||||
},
|
||||
triggerSteps: {
|
||||
relation: Base.HasManyRelation,
|
||||
modelClass: Step,
|
||||
join: {
|
||||
from: 'connections.id',
|
||||
to: 'steps.connection_id',
|
||||
},
|
||||
filter(builder: ExtendedQueryBuilder<Step>) {
|
||||
builder.where('type', '=', 'trigger');
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
encryptData(): void {
|
||||
@@ -110,6 +128,27 @@ class Connection extends Base {
|
||||
await super.$afterUpdate(opt, queryContext);
|
||||
Telemetry.connectionUpdated(this);
|
||||
}
|
||||
|
||||
async getApp() {
|
||||
if (!this.key) return null;
|
||||
|
||||
return await App.findOneByKey(this.key);
|
||||
}
|
||||
|
||||
async verifyWebhook(request: IRequest) {
|
||||
if (!this.key) return true;
|
||||
|
||||
const app = await this.getApp();
|
||||
|
||||
const $ = await globalVariable({
|
||||
connection: this,
|
||||
request,
|
||||
});
|
||||
|
||||
if (!app.auth?.verifyWebhook) return true;
|
||||
|
||||
return app.auth.verifyWebhook($);
|
||||
}
|
||||
}
|
||||
|
||||
export default Connection;
|
||||
|
@@ -18,6 +18,7 @@ class Flow extends Base {
|
||||
active: boolean;
|
||||
status: 'paused' | 'published' | 'draft';
|
||||
steps: Step[];
|
||||
triggerStep: Step;
|
||||
published_at: string;
|
||||
remoteWebhookId: string;
|
||||
executions?: Execution[];
|
||||
@@ -51,6 +52,20 @@ class Flow extends Base {
|
||||
builder.orderBy('position', 'asc');
|
||||
},
|
||||
},
|
||||
triggerStep: {
|
||||
relation: Base.HasOneRelation,
|
||||
modelClass: Step,
|
||||
join: {
|
||||
from: 'flows.id',
|
||||
to: 'steps.flow_id',
|
||||
},
|
||||
filter(builder: ExtendedQueryBuilder<Step>) {
|
||||
builder
|
||||
.where('type', 'trigger')
|
||||
.limit(1)
|
||||
.first();
|
||||
},
|
||||
},
|
||||
executions: {
|
||||
relation: Base.HasManyRelation,
|
||||
modelClass: Execution,
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { URL } from 'node:url';
|
||||
import { QueryContext, ModelOptions } from 'objection';
|
||||
import get from 'lodash.get';
|
||||
import type { IJSONObject, IStep } from '@automatisch/types';
|
||||
import Base from './base';
|
||||
import App from './app';
|
||||
@@ -22,6 +23,7 @@ class Step extends Base {
|
||||
connection?: Connection;
|
||||
flow: Flow;
|
||||
executionSteps: ExecutionStep[];
|
||||
webhookPath?: string;
|
||||
|
||||
static tableName = 'steps';
|
||||
|
||||
@@ -43,6 +45,7 @@ class Step extends Base {
|
||||
},
|
||||
position: { type: 'integer' },
|
||||
parameters: { type: 'object' },
|
||||
webhookPath: { type: ['string', 'null'] },
|
||||
},
|
||||
};
|
||||
|
||||
@@ -77,17 +80,52 @@ class Step extends Base {
|
||||
},
|
||||
});
|
||||
|
||||
get webhookUrl() {
|
||||
return new URL(this.webhookPath, appConfig.webhookUrl).toString();
|
||||
}
|
||||
|
||||
get iconUrl() {
|
||||
if (!this.appKey) return null;
|
||||
|
||||
return `${appConfig.baseUrl}/apps/${this.appKey}/assets/favicon.svg`;
|
||||
}
|
||||
|
||||
get webhookUrl() {
|
||||
if (this.appKey !== 'webhook') return null;
|
||||
async computeWebhookPath() {
|
||||
if (this.type === 'action') return null;
|
||||
|
||||
const url = new URL(`/webhooks/${this.flowId}`, appConfig.webhookUrl);
|
||||
return url.toString();
|
||||
const triggerCommand = await this.getTriggerCommand();
|
||||
|
||||
if (!triggerCommand) return null;
|
||||
|
||||
const {
|
||||
useSingletonWebhook,
|
||||
singletonWebhookRefValueParameter,
|
||||
type,
|
||||
} = triggerCommand;
|
||||
|
||||
const isWebhook = type === 'webhook';
|
||||
|
||||
if (!isWebhook) return null;
|
||||
|
||||
if (singletonWebhookRefValueParameter) {
|
||||
const parameterValue = get(this.parameters, singletonWebhookRefValueParameter);
|
||||
return `/webhooks/connections/${this.connectionId}/${parameterValue}`;
|
||||
}
|
||||
|
||||
if (useSingletonWebhook) {
|
||||
return `/webhooks/connections/${this.connectionId}`;
|
||||
}
|
||||
|
||||
return `/webhooks/flows/${this.flowId}`;
|
||||
}
|
||||
|
||||
async getWebhookUrl() {
|
||||
if (this.type === 'action') return;
|
||||
|
||||
const path = await this.computeWebhookPath();
|
||||
const webhookUrl = new URL(path, appConfig.webhookUrl).toString();
|
||||
|
||||
return webhookUrl;
|
||||
}
|
||||
|
||||
async $afterInsert(queryContext: QueryContext) {
|
||||
@@ -166,6 +204,18 @@ class Step extends Base {
|
||||
|
||||
return existingArguments;
|
||||
}
|
||||
|
||||
async updateWebhookUrl() {
|
||||
if (this.isAction) return this;
|
||||
|
||||
const payload = {
|
||||
webhookPath: await this.computeWebhookPath(),
|
||||
};
|
||||
|
||||
await this.$query().patchAndFetch(payload);
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export default Step;
|
||||
|
Reference in New Issue
Block a user