diff --git a/packages/cli/templates/form-trigger-completion.handlebars b/packages/cli/templates/form-trigger-completion.handlebars
index a15855d371..d6447128a5 100644
--- a/packages/cli/templates/form-trigger-completion.handlebars
+++ b/packages/cli/templates/form-trigger-completion.handlebars
@@ -26,49 +26,53 @@
-
-
-
-
+ {{#if appendAttribution}}
+
+ {{/if}}
+
+
+ {{/if}}
diff --git a/packages/nodes-base/nodes/Form/Form.node.ts b/packages/nodes-base/nodes/Form/Form.node.ts
index 82d8d4c7e4..0e4af9d306 100644
--- a/packages/nodes-base/nodes/Form/Form.node.ts
+++ b/packages/nodes-base/nodes/Form/Form.node.ts
@@ -115,6 +115,16 @@ const completionProperties = updateDisplayOptions(
value: 'redirect',
description: 'Redirect the user to a URL',
},
+ {
+ name: 'Show Text',
+ value: 'showText',
+ description: 'Display simple text or HTML',
+ },
+ {
+ name: 'Return Binary File',
+ value: 'returnBinary',
+ description: 'Return incoming binary file',
+ },
],
},
{
@@ -138,7 +148,7 @@ const completionProperties = updateDisplayOptions(
required: true,
displayOptions: {
show: {
- respondWith: ['text'],
+ respondWith: ['text', 'returnBinary'],
},
},
},
@@ -152,10 +162,41 @@ const completionProperties = updateDisplayOptions(
},
displayOptions: {
show: {
- respondWith: ['text'],
+ respondWith: ['text', 'returnBinary'],
},
},
},
+ {
+ displayName: 'Text',
+ name: 'responseText',
+ type: 'string',
+ displayOptions: {
+ show: {
+ respondWith: ['showText'],
+ },
+ },
+ typeOptions: {
+ rows: 2,
+ },
+ default: '',
+ placeholder: 'e.g. Thanks for filling the form',
+ description: 'The text to display on the page. Use HTML to show a customized web page.',
+ },
+ {
+ displayName: 'Input Data Field Name',
+ name: 'inputDataFieldName',
+ type: 'string',
+ displayOptions: {
+ show: {
+ respondWith: ['returnBinary'],
+ },
+ },
+ default: 'data',
+ placeholder: 'e.g. data',
+ description:
+ 'Find the name of input field containing the binary data to return in the Input panel on the left, in the Binary tab',
+ hint: 'The name of the input field containing the binary file data to be returned',
+ },
{
displayName: 'Options',
name: 'options',
@@ -165,7 +206,7 @@ const completionProperties = updateDisplayOptions(
options: [{ ...formTitle, required: false, displayName: 'Completion Page Title' }],
displayOptions: {
show: {
- respondWith: ['text'],
+ respondWith: ['text', 'returnBinary'],
},
},
},
diff --git a/packages/nodes-base/nodes/Form/formCompletionUtils.ts b/packages/nodes-base/nodes/Form/formCompletionUtils.ts
index b1dab0c68c..bd1fd52028 100644
--- a/packages/nodes-base/nodes/Form/formCompletionUtils.ts
+++ b/packages/nodes-base/nodes/Form/formCompletionUtils.ts
@@ -3,7 +3,49 @@ import {
type NodeTypeAndVersion,
type IWebhookFunctions,
type IWebhookResponseData,
+ type IN8nHttpResponse,
+ type IDataObject,
+ ApplicationError,
+ type IBinaryData,
+ BINARY_ENCODING,
} from 'n8n-workflow';
+import { type Readable } from 'node:stream';
+
+import { sanitizeHtml } from './utils';
+
+const getBinaryDataFromNode = (context: IWebhookFunctions, nodeName: string): IDataObject => {
+ return context.evaluateExpression(`{{ $('${nodeName}').first().binary }}`) as IDataObject;
+};
+
+const respondWithBinary = (context: IWebhookFunctions, res: Response) => {
+ const inputDataFieldName = context.getNodeParameter('inputDataFieldName', '') as string;
+
+ const parentNodes = context.getParentNodes(context.getNode().name);
+
+ const binaryNode = parentNodes.find((node) =>
+ getBinaryDataFromNode(context, node?.name)?.hasOwnProperty(inputDataFieldName),
+ );
+
+ if (!binaryNode) {
+ throw new ApplicationError('No binary data found.');
+ }
+
+ const binaryData = getBinaryDataFromNode(context, binaryNode?.name)[
+ inputDataFieldName
+ ] as IBinaryData;
+
+ let responseBody: IN8nHttpResponse | Readable;
+ if (binaryData.id) {
+ responseBody = { binaryData };
+ } else {
+ responseBody = Buffer.from(binaryData.data, BINARY_ENCODING);
+ }
+
+ // res.setHeader('Content-Type', binaryData.mimeType);
+ // res.send(responseBody);
+
+ return { noWebhookResponse: true };
+};
export const renderFormCompletion = async (
context: IWebhookFunctions,
@@ -14,6 +56,12 @@ export const renderFormCompletion = async (
const completionMessage = context.getNodeParameter('completionMessage', '') as string;
const redirectUrl = context.getNodeParameter('redirectUrl', '') as string;
const options = context.getNodeParameter('options', {}) as { formTitle: string };
+ const respondWith = context.getNodeParameter('respondWith', '') as string;
+ const responseText = context.getNodeParameter('responseText', '') as string;
+
+ if (respondWith === 'returnBinary') {
+ return respondWithBinary(context, res);
+ }
if (redirectUrl) {
res.send(
@@ -35,6 +83,7 @@ export const renderFormCompletion = async (
message: completionMessage,
formTitle: title,
appendAttribution,
+ responseText: sanitizeHtml(responseText),
});
return { noWebhookResponse: true };
diff --git a/packages/nodes-base/nodes/Webhook/Webhook.node.ts b/packages/nodes-base/nodes/Webhook/Webhook.node.ts
index 9255dab346..c540c0bfdc 100644
--- a/packages/nodes-base/nodes/Webhook/Webhook.node.ts
+++ b/packages/nodes-base/nodes/Webhook/Webhook.node.ts
@@ -220,7 +220,7 @@ export class Webhook extends Node {
});
if (options.binaryData) {
- return await this.handleBinaryData(context, prepareOutput);
+ return await this.handleBinaryData(context, prepareOutput); // magic here
}
if (req.contentType === 'multipart/form-data') {