feat(Webhook Node): Overhaul (#8889)

Co-authored-by: Giulio Andreini <andreini@netseven.it>
This commit is contained in:
Michael Kret
2024-03-28 10:46:39 +02:00
committed by GitHub
parent 519f945547
commit e84c27c0ce
17 changed files with 780 additions and 43 deletions

View File

@@ -115,12 +115,12 @@ export abstract class AbstractServer {
private async setupHealthCheck() {
// health check should not care about DB connections
this.app.get('/healthz', async (req, res) => {
this.app.get('/healthz', async (_req, res) => {
res.send({ status: 'ok' });
});
const { connectionState } = Db;
this.app.use((req, res, next) => {
this.app.use((_req, res, next) => {
if (connectionState.connected) {
if (connectionState.migrated) next();
else res.send('n8n is starting up. Please wait');

View File

@@ -106,10 +106,22 @@ export const webhookRequestHandler =
const options = await webhookManager.findAccessControlOptions(path, requestedMethod);
const { allowedOrigins } = options ?? {};
res.header(
'Access-Control-Allow-Origin',
!allowedOrigins || allowedOrigins === '*' ? req.headers.origin : allowedOrigins,
);
if (allowedOrigins && allowedOrigins !== '*' && allowedOrigins !== req.headers.origin) {
const originsList = allowedOrigins.split(',');
const defaultOrigin = originsList[0];
if (originsList.length === 1) {
res.header('Access-Control-Allow-Origin', defaultOrigin);
}
if (originsList.includes(req.headers.origin as string)) {
res.header('Access-Control-Allow-Origin', req.headers.origin);
} else {
res.header('Access-Control-Allow-Origin', defaultOrigin);
}
} else {
res.header('Access-Control-Allow-Origin', req.headers.origin);
}
if (method === 'OPTIONS') {
res.header('Access-Control-Max-Age', '300');
@@ -262,14 +274,14 @@ export async function executeWebhook(
);
const responseCode = workflow.expression.getSimpleParameterValue(
workflowStartNode,
webhookData.webhookDescription.responseCode,
webhookData.webhookDescription.responseCode as string,
executionMode,
additionalKeys,
undefined,
200,
) as number;
const responseData = workflow.expression.getSimpleParameterValue(
const responseData = workflow.expression.getComplexParameterValue(
workflowStartNode,
webhookData.webhookDescription.responseData,
executionMode,
@@ -324,7 +336,7 @@ export async function executeWebhook(
// TODO: pass a custom `fileWriteStreamHandler` to create binary data files directly
});
req.body = await new Promise((resolve) => {
form.parse(req, async (err, data, files) => {
form.parse(req, async (_err, data, files) => {
normalizeFormData(data);
normalizeFormData(files);
resolve({ data, files });
@@ -455,6 +467,12 @@ export async function executeWebhook(
responseCallback(null, {
responseCode,
});
} else if (responseData) {
// Return the data specified in the response data option
responseCallback(null, {
data: responseData as IDataObject,
responseCode,
});
} else if (webhookResultData.webhookResponse !== undefined) {
// Data to respond with is given
responseCallback(null, {

View File

@@ -49,7 +49,7 @@ export class WebhookNotFoundError extends NotFoundError {
const hintMsg =
hint === 'default'
? "Click the 'Execute workflow' button on the canvas, then try again. (In test mode, the webhook only works for one call after you click this button)"
? "Click the 'Test workflow' button on the canvas, then try again. (In test mode, the webhook only works for one call after you click this button)"
: "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)";
super(errorMsg, hintMsg);

View File

@@ -1,6 +1,6 @@
import { Service } from 'typedi';
import { CacheService } from '@/services/cache/cache.service';
import { type IWebhookData } from 'n8n-workflow';
import type { IWebhookData } from 'n8n-workflow';
import type { IWorkflowDb } from '@/Interfaces';
import { TEST_WEBHOOK_TIMEOUT, TEST_WEBHOOK_TIMEOUT_BUFFER } from '@/constants';