fix(n8n Form Node): Redirection update (no-changelog) (#13104)

Co-authored-by: Dana <152518854+dana-gill@users.noreply.github.com>
This commit is contained in:
Michael Kret
2025-02-19 14:59:38 +02:00
committed by GitHub
parent 60ff82f648
commit 755734d349
15 changed files with 288 additions and 131 deletions

View File

@@ -37,7 +37,9 @@ import {
createDeferredPromise,
ExecutionCancelledError,
FORM_NODE_TYPE,
FORM_TRIGGER_NODE_TYPE,
NodeOperationError,
WAIT_NODE_TYPE,
} from 'n8n-workflow';
import assert from 'node:assert';
import { finished } from 'stream/promises';
@@ -101,6 +103,62 @@ export function getWorkflowWebhooks(
return returnData;
}
export function autoDetectResponseMode(
workflowStartNode: INode,
workflow: Workflow,
method: string,
) {
if (workflowStartNode.type === WAIT_NODE_TYPE && workflowStartNode.parameters.resume !== 'form') {
return undefined;
}
if (
[FORM_NODE_TYPE, FORM_TRIGGER_NODE_TYPE, WAIT_NODE_TYPE].includes(workflowStartNode.type) &&
method === 'POST'
) {
const connectedNodes = workflow.getChildNodes(workflowStartNode.name);
for (const nodeName of connectedNodes) {
const node = workflow.nodes[nodeName];
if (node.type === WAIT_NODE_TYPE && node.parameters.resume !== 'form') {
continue;
}
if ([FORM_NODE_TYPE, WAIT_NODE_TYPE].includes(node.type) && !node.disabled) {
return 'responseNode';
}
}
}
return undefined;
}
/**
* for formTrigger and form nodes redirection has to be handled by sending redirectURL in response body
*/
export const handleFormRedirectionCase = (
data: IWebhookResponseCallbackData,
workflowStartNode: INode,
) => {
if (workflowStartNode.type === WAIT_NODE_TYPE && workflowStartNode.parameters.resume !== 'form') {
return data;
}
if (
[FORM_NODE_TYPE, FORM_TRIGGER_NODE_TYPE, WAIT_NODE_TYPE].includes(workflowStartNode.type) &&
(data?.headers as IDataObject)?.location &&
String(data?.responseCode).startsWith('3')
) {
data.responseCode = 200;
data.data = {
redirectURL: (data?.headers as IDataObject)?.location,
};
(data.headers as IDataObject).location = undefined;
}
return data;
};
const { formDataFileSizeMax } = Container.get(GlobalConfig).endpoints;
const parseFormData = createMultiFormDataParser(formDataFileSizeMax);
@@ -154,23 +212,8 @@ export async function executeWebhook(
// Get the responseMode
let responseMode;
// if this is n8n FormTrigger node, check if there is a Form node in child nodes,
// if so, set 'responseMode' to 'formPage' to redirect to URL of that Form later
if (nodeType.description.name === 'formTrigger') {
const connectedNodes = workflow.getChildNodes(workflowStartNode.name);
let hasNextPage = false;
for (const nodeName of connectedNodes) {
const node = workflow.nodes[nodeName];
if (node.type === FORM_NODE_TYPE && !node.disabled) {
hasNextPage = true;
break;
}
}
if (hasNextPage) {
responseMode = 'formPage';
}
}
//check if response mode should be set automatically, e.g. multipage form
responseMode = autoDetectResponseMode(workflowStartNode, workflow, req.method);
if (!responseMode) {
responseMode = workflow.expression.getSimpleParameterValue(
@@ -201,7 +244,7 @@ export async function executeWebhook(
'firstEntryJson',
);
if (!['onReceived', 'lastNode', 'responseNode', 'formPage'].includes(responseMode)) {
if (!['onReceived', 'lastNode', 'responseNode'].includes(responseMode)) {
// If the mode is not known we error. Is probably best like that instead of using
// the default that people know as early as possible (probably already testing phase)
// that something does not resolve properly.
@@ -497,28 +540,16 @@ export async function executeWebhook(
} else {
// TODO: This probably needs some more changes depending on the options on the
// Webhook Response node
const headers = response.headers;
let responseCode = response.statusCode;
let data = response.body as IDataObject;
// for formTrigger node redirection has to be handled by sending redirectURL in response body
if (
nodeType.description.name === 'formTrigger' &&
headers.location &&
String(responseCode).startsWith('3')
) {
responseCode = 200;
data = {
redirectURL: headers.location,
};
headers.location = undefined;
}
let data: IWebhookResponseCallbackData = {
data: response.body as IDataObject,
headers: response.headers,
responseCode: response.statusCode,
};
responseCallback(null, {
data,
headers,
responseCode,
});
data = handleFormRedirectionCase(data, workflowStartNode);
responseCallback(null, data);
}
process.nextTick(() => res.end());
@@ -552,12 +583,6 @@ export async function executeWebhook(
responsePromise,
);
if (responseMode === 'formPage' && !didSendResponse) {
res.redirect(`${additionalData.formWaitingBaseUrl}/${executionId}`);
process.nextTick(() => res.end());
didSendResponse = true;
}
Container.get(Logger).debug(
`Started execution of workflow "${workflow.name}" from webhook with execution ID ${executionId}`,
{ executionId },