From cf7e3bf8c4c98d2986777d6b8cf5c1b1fbe10592 Mon Sep 17 00:00:00 2001 From: JensForstmann Date: Sun, 1 Sep 2024 15:03:20 +0200 Subject: [PATCH] Add headers to webhook requests --- backend/src/events.ts | 1 + backend/src/match.ts | 5 ++ backend/src/matchesController.ts | 2 +- backend/src/routes.ts | 40 +++++++++ backend/swagger.json | 47 ++++++++-- common/types/match.ts | 8 +- frontend/src/components/CreateUpdateMatch.tsx | 86 ++++++++++++++++++- frontend/src/pages/matchEdit.tsx | 1 + 8 files changed, 181 insertions(+), 9 deletions(-) diff --git a/backend/src/events.ts b/backend/src/events.ts index 1fab971..6685402 100644 --- a/backend/src/events.ts +++ b/backend/src/events.ts @@ -43,6 +43,7 @@ const send = (match: Match.Match, data: Event, isSystemEvent?: boolean) => { fetch(url, { method: 'POST', headers: { + ...match.data.webhookHeaders, 'Content-Type': 'application/json', }, body: JSON.stringify(data), diff --git a/backend/src/match.ts b/backend/src/match.ts index 56ce571..b805afe 100644 --- a/backend/src/match.ts +++ b/backend/src/match.ts @@ -112,6 +112,7 @@ export const createFromCreateDto = async (dto: IMatchCreateDto, id: string, logS createdAt: Date.now(), lastSavedAt: 0, webhookUrl: dto.webhookUrl ?? null, + webhookHeaders: dto.webhookHeaders ?? null, mode: dto.mode ?? 'SINGLE', }; try { @@ -1036,6 +1037,10 @@ export const update = async (match: Match, dto: IMatchUpdateDto) => { match.data.webhookUrl = dto.webhookUrl; } + if (dto.webhookHeaders !== undefined) { + match.data.webhookHeaders = dto.webhookHeaders; + } + if (dto.logSecret) { const previous = match.data.logSecret; match.data.logSecret = dto.logSecret; diff --git a/backend/src/matchesController.ts b/backend/src/matchesController.ts index 1b2b757..1694d26 100644 --- a/backend/src/matchesController.ts +++ b/backend/src/matchesController.ts @@ -204,7 +204,7 @@ export class MatchesController extends Controller { } /** - * Stop supervising a specific match. TMT will no longer listen to the game server and will not execute any rcon commands. + * Stop supervising a specific match. TMT will execute the "end rcon commands" and afterwards no longer listen to the game server. */ @Delete('{id}') async deleteMatch(id: string, @Request() req: ExpressRequest): Promise { diff --git a/backend/src/routes.ts b/backend/src/routes.ts index 4a4e3b2..d770b5b 100644 --- a/backend/src/routes.ts +++ b/backend/src/routes.ts @@ -494,6 +494,18 @@ const models: TsoaRoute.Models = { subSchemas: [{ dataType: 'string' }, { dataType: 'enum', enums: [null] }], required: true, }, + webhookHeaders: { + dataType: 'union', + subSchemas: [ + { + dataType: 'nestedObjectLiteral', + nestedProperties: {}, + additionalProperties: { dataType: 'string' }, + }, + { dataType: 'enum', enums: [null] }, + ], + required: true, + }, rconCommands: { dataType: 'nestedObjectLiteral', nestedProperties: { @@ -561,6 +573,17 @@ const models: TsoaRoute.Models = { dataType: 'union', subSchemas: [{ dataType: 'string' }, { dataType: 'enum', enums: [null] }], }, + webhookHeaders: { + dataType: 'union', + subSchemas: [ + { + dataType: 'nestedObjectLiteral', + nestedProperties: {}, + additionalProperties: { dataType: 'string' }, + }, + { dataType: 'enum', enums: [null] }, + ], + }, rconCommands: { dataType: 'nestedObjectLiteral', nestedProperties: { @@ -607,6 +630,18 @@ const models: TsoaRoute.Models = { subSchemas: [{ dataType: 'string' }, { dataType: 'enum', enums: [null] }], required: true, }, + webhookHeaders: { + dataType: 'union', + subSchemas: [ + { + dataType: 'nestedObjectLiteral', + nestedProperties: {}, + additionalProperties: { dataType: 'string' }, + }, + { dataType: 'enum', enums: [null] }, + ], + required: true, + }, rconCommands: { dataType: 'nestedObjectLiteral', nestedProperties: { @@ -1007,6 +1042,11 @@ const models: TsoaRoute.Models = { }, gameServer: { ref: 'IGameServer' }, webhookUrl: { dataType: 'string' }, + webhookHeaders: { + dataType: 'nestedObjectLiteral', + nestedProperties: {}, + additionalProperties: { dataType: 'string' }, + }, rconCommands: { dataType: 'nestedObjectLiteral', nestedProperties: { diff --git a/backend/swagger.json b/backend/swagger.json index 1de512e..824e1e4 100644 --- a/backend/swagger.json +++ b/backend/swagger.json @@ -735,7 +735,16 @@ "webhookUrl": { "type": "string", "nullable": true, - "description": "if set various events will be posted to this url" + "description": "Send various events to this url (HTTP POST)" + }, + "webhookHeaders": { + "properties": {}, + "additionalProperties": { + "type": "string" + }, + "type": "object", + "nullable": true, + "description": "Additional headers that will be added to each webhook request" }, "rconCommands": { "properties": { @@ -841,6 +850,7 @@ "matchMaps", "currentMap", "webhookUrl", + "webhookHeaders", "rconCommands", "canClinch", "matchEndAction", @@ -920,7 +930,16 @@ "webhookUrl": { "type": "string", "nullable": true, - "description": "if set various events will be posted to this url" + "description": "Send various events to this url (HTTP POST)" + }, + "webhookHeaders": { + "properties": {}, + "additionalProperties": { + "type": "string" + }, + "type": "object", + "nullable": true, + "description": "Additional headers that will be added to each webhook request" }, "rconCommands": { "properties": { @@ -1047,7 +1066,16 @@ "webhookUrl": { "type": "string", "nullable": true, - "description": "if set various events will be posted to this url" + "description": "Send various events to this url (HTTP POST)" + }, + "webhookHeaders": { + "properties": {}, + "additionalProperties": { + "type": "string" + }, + "type": "object", + "nullable": true, + "description": "Additional headers that will be added to each webhook request" }, "rconCommands": { "properties": { @@ -1156,6 +1184,7 @@ "matchMaps", "currentMap", "webhookUrl", + "webhookHeaders", "rconCommands", "canClinch", "matchEndAction", @@ -1910,7 +1939,15 @@ }, "webhookUrl": { "type": "string", - "description": "if set various events will be posted to this url" + "description": "Send various events to this url (HTTP POST)" + }, + "webhookHeaders": { + "properties": {}, + "additionalProperties": { + "type": "string" + }, + "type": "object", + "description": "Additional headers that will be added to each webhook request" }, "rconCommands": { "properties": { @@ -2499,7 +2536,7 @@ "description": "No content" } }, - "description": "Stop supervising a specific match. TMT will no longer listen to the game server and will not execute any rcon commands.", + "description": "Stop supervising a specific match. TMT will execute the \"end rcon commands\" and afterwards no longer listen to the game server.", "security": [ { "bearer_token": [] diff --git a/common/types/match.ts b/common/types/match.ts index 7bb5a6e..a0db5bb 100644 --- a/common/types/match.ts +++ b/common/types/match.ts @@ -78,8 +78,10 @@ export interface IMatch { matchMaps: IMatchMap[]; /** Index of the matchMaps array indicating the current map. */ currentMap: number; - /** if set various events will be posted to this url */ + /** Send various events to this url (HTTP POST) */ webhookUrl: string | null; + /** Additional headers that will be added to each webhook request */ + webhookHeaders: { [key: string]: string } | null; rconCommands: { /** executed exactly once on match init */ init: string[]; @@ -145,8 +147,10 @@ export interface IMatchCreateDto { teamB: ITeamCreateDto; electionSteps: Array; gameServer: IGameServer | null; - /** if set various events will be posted to this url */ + /** Send various events to this url (HTTP POST) */ webhookUrl?: string | null; + /** Additional headers that will be added to each webhook request */ + webhookHeaders?: { [key: string]: string } | null; rconCommands?: { /** executed exactly once on match init */ init?: string[]; diff --git a/frontend/src/components/CreateUpdateMatch.tsx b/frontend/src/components/CreateUpdateMatch.tsx index 22d8216..c3cfa2e 100644 --- a/frontend/src/components/CreateUpdateMatch.tsx +++ b/frontend/src/components/CreateUpdateMatch.tsx @@ -1,5 +1,14 @@ import autoAnimate from '@formkit/auto-animate'; -import { Component, For, Match, Show, Switch, createEffect, createSignal, onMount } from 'solid-js'; +import { + Component, + For, + Match, + Show, + Switch, + createEffect, + createSignal, + onMount +} from 'solid-js'; import { createStore } from 'solid-js/store'; import { IConfig, @@ -240,6 +249,7 @@ export const CreateUpdateMatch: Component< const [dto, setDto] = createStore(copyObject(props.match)); const [showAdvanced, setShowAdvanced] = createSignal(false); const [json, setJson] = createSignal(''); + const [webhookHeadersErrorMessage, setWebhookHeadersErrorMessage] = createSignal(''); createEffect(() => { try { @@ -686,6 +696,80 @@ export const CreateUpdateMatch: Component< )} onInput={(e) => setDto('webhookUrl', e.currentTarget.value)} /> +