mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
Add configurable headers for webhooks (#8855)
* Add configurable headers for webhooks * Update api/src/database/migrations/20211016A-add-webhook-headers.ts Co-authored-by: Rijk van Zanten <rijkvanzanten@me.com>
This commit is contained in:
13
api/src/database/migrations/20211016A-add-webhook-headers.ts
Normal file
13
api/src/database/migrations/20211016A-add-webhook-headers.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Knex } from 'knex';
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
await knex.schema.alterTable('directus_webhooks', (table) => {
|
||||
table.json('headers');
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await knex.schema.alterTable('directus_webhooks', (table) => {
|
||||
table.dropColumn('headers');
|
||||
});
|
||||
}
|
||||
@@ -68,6 +68,27 @@ fields:
|
||||
width: half
|
||||
display: boolean
|
||||
|
||||
- field: headers
|
||||
special: json
|
||||
interface: list
|
||||
options:
|
||||
template: '{{ header }}: {{ value }}'
|
||||
addLabel: $t:field_options.directus_webhooks.headers.add
|
||||
fields:
|
||||
- field: header
|
||||
name: $t:field_options.directus_webhooks.headers.header
|
||||
type: string
|
||||
meta:
|
||||
interface: input
|
||||
width: half
|
||||
- field: value
|
||||
name: $t:field_options.directus_webhooks.headers.value
|
||||
type: string
|
||||
meta:
|
||||
interface: input
|
||||
width: half
|
||||
width: full
|
||||
|
||||
- field: triggers_divider
|
||||
interface: presentation-divider
|
||||
options:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { AbstractServiceOptions, Item, PrimaryKey } from '../types';
|
||||
import { AbstractServiceOptions, Item, PrimaryKey, Webhook } from '../types';
|
||||
import { register } from '../webhooks';
|
||||
import { ItemsService, MutationOptions } from './items';
|
||||
|
||||
export class WebhooksService extends ItemsService {
|
||||
export class WebhooksService extends ItemsService<Webhook> {
|
||||
constructor(options: AbstractServiceOptions) {
|
||||
super('directus_webhooks', options);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ export type Webhook = {
|
||||
url: string;
|
||||
status: 'active' | 'inactive';
|
||||
data: boolean;
|
||||
actions: string;
|
||||
collections: string;
|
||||
actions: string[];
|
||||
collections: string[];
|
||||
headers: WebhookHeader[];
|
||||
};
|
||||
|
||||
export type WebhookHeader = { header: string; value: string };
|
||||
|
||||
@@ -3,26 +3,27 @@ import { ListenerFn } from 'eventemitter2';
|
||||
import getDatabase from './database';
|
||||
import emitter from './emitter';
|
||||
import logger from './logger';
|
||||
import { Webhook } from './types';
|
||||
import { Webhook, WebhookHeader } from './types';
|
||||
import { pick } from 'lodash';
|
||||
import { WebhooksService } from './services';
|
||||
import { getSchema } from './utils/get-schema';
|
||||
|
||||
let registered: { event: string; handler: ListenerFn }[] = [];
|
||||
|
||||
export async function register(): Promise<void> {
|
||||
unregister();
|
||||
|
||||
const database = getDatabase();
|
||||
|
||||
const webhooks = await database.select<Webhook[]>('*').from('directus_webhooks').where({ status: 'active' });
|
||||
const webhookService = new WebhooksService({ knex: getDatabase(), schema: await getSchema() });
|
||||
|
||||
const webhooks = await webhookService.readByQuery({ filter: { status: { _eq: 'active' } } });
|
||||
for (const webhook of webhooks) {
|
||||
if (webhook.actions === '*') {
|
||||
if (webhook.actions.includes('*')) {
|
||||
const event = 'items.*';
|
||||
const handler = createHandler(webhook);
|
||||
emitter.on(event, handler);
|
||||
registered.push({ event, handler });
|
||||
} else {
|
||||
for (const action of webhook.actions.split(',')) {
|
||||
for (const action of webhook.actions) {
|
||||
const event = `items.${action}`;
|
||||
const handler = createHandler(webhook);
|
||||
emitter.on(event, handler);
|
||||
@@ -42,8 +43,7 @@ export function unregister(): void {
|
||||
|
||||
function createHandler(webhook: Webhook): ListenerFn {
|
||||
return async (data) => {
|
||||
const collectionAllowList = webhook.collections.split(',');
|
||||
if (collectionAllowList.includes('*') === false && collectionAllowList.includes(data.collection) === false) return;
|
||||
if (webhook.collections.includes('*') === false && webhook.collections.includes(data.collection) === false) return;
|
||||
|
||||
const webhookPayload = pick(data, [
|
||||
'event',
|
||||
@@ -60,6 +60,7 @@ function createHandler(webhook: Webhook): ListenerFn {
|
||||
url: webhook.url,
|
||||
method: webhook.method,
|
||||
data: webhook.data ? webhookPayload : null,
|
||||
headers: mergeHeaders(webhook.headers),
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.warn(`Webhook "${webhook.name}" (id: ${webhook.id}) failed`);
|
||||
@@ -67,3 +68,13 @@ function createHandler(webhook: Webhook): ListenerFn {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function mergeHeaders(headerArray: WebhookHeader[]) {
|
||||
const headers: Record<string, string> = {};
|
||||
|
||||
for (const { header, value } of headerArray ?? []) {
|
||||
headers[header] = value;
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user