2022-08-17 08:50:24 -07:00
import { BINARY_ENCODING , IExecuteFunctions , WAIT_TIME_UNLIMITED } from 'n8n-core' ;
2021-08-21 05:11:32 -07:00
import {
2022-04-14 23:00:47 -07:00
ICredentialDataDecryptedObject ,
2021-08-21 05:11:32 -07:00
IDataObject ,
INodeExecutionData ,
INodeType ,
INodeTypeDescription ,
IWebhookFunctions ,
IWebhookResponseData ,
NodeOperationError ,
} from 'n8n-workflow' ;
2022-04-08 14:32:08 -07:00
import fs from 'fs' ;
2022-11-24 07:54:43 -08:00
import stream from 'stream' ;
import { promisify } from 'util' ;
import basicAuth from 'basic-auth' ;
import type { Response } from 'express' ;
2022-04-08 14:32:08 -07:00
import formidable from 'formidable' ;
import isbot from 'isbot' ;
2022-11-24 07:54:43 -08:00
import { file as tmpFile } from 'tmp-promise' ;
const pipeline = promisify ( stream . pipeline ) ;
2021-08-21 05:11:32 -07:00
function authorizationError ( resp : Response , realm : string , responseCode : number , message? : string ) {
if ( message === undefined ) {
message = 'Authorization problem!' ;
if ( responseCode === 401 ) {
message = 'Authorization is required!' ;
} else if ( responseCode === 403 ) {
message = 'Authorization data is wrong!' ;
}
}
resp . writeHead ( responseCode , { 'WWW-Authenticate' : ` Basic realm=" ${ realm } " ` } ) ;
resp . end ( message ) ;
return {
noWebhookResponse : true ,
} ;
}
export class Wait implements INodeType {
description : INodeTypeDescription = {
displayName : 'Wait' ,
name : 'wait' ,
icon : 'fa:pause-circle' ,
group : [ 'organization' ] ,
version : 1 ,
description : 'Wait before continue with execution' ,
defaults : {
name : 'Wait' ,
color : '#804050' ,
} ,
inputs : [ 'main' ] ,
outputs : [ 'main' ] ,
credentials : [
{
name : 'httpBasicAuth' ,
required : true ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
incomingAuthentication : [ 'basicAuth' ] ,
2021-08-21 05:11:32 -07:00
} ,
} ,
} ,
{
name : 'httpHeaderAuth' ,
required : true ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
incomingAuthentication : [ 'headerAuth' ] ,
2021-08-21 05:11:32 -07:00
} ,
} ,
} ,
] ,
webhooks : [
{
name : 'default' ,
httpMethod : '={{$parameter["httpMethod"]}}' ,
isFullPath : true ,
responseCode : '={{$parameter["responseCode"]}}' ,
responseMode : '={{$parameter["responseMode"]}}' ,
responseData : '={{$parameter["responseData"]}}' ,
responseBinaryPropertyName : '={{$parameter["responseBinaryPropertyName"]}}' ,
responseContentType : '={{$parameter["options"]["responseContentType"]}}' ,
responsePropertyName : '={{$parameter["options"]["responsePropertyName"]}}' ,
responseHeaders : '={{$parameter["options"]["responseHeaders"]}}' ,
path : '={{$parameter["options"]["webhookSuffix"] || ""}}' ,
restartWebhook : true ,
} ,
] ,
properties : [
{
2022-06-03 10:23:49 -07:00
displayName : 'Webhook Authentication' ,
2021-08-21 05:11:32 -07:00
name : 'incomingAuthentication' ,
type : 'options' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
resume : [ 'webhook' ] ,
2021-08-21 05:11:32 -07:00
} ,
} ,
options : [
{
name : 'Basic Auth' ,
value : 'basicAuth' ,
} ,
{
name : 'Header Auth' ,
value : 'headerAuth' ,
} ,
{
name : 'None' ,
value : 'none' ,
} ,
] ,
default : 'none' ,
2022-08-17 08:50:24 -07:00
description :
2022-09-29 14:02:25 -07:00
'If and how incoming resume-webhook-requests to $execution.resumeUrl should be authenticated for additional security' ,
2021-08-21 05:11:32 -07:00
} ,
{
displayName : 'Resume' ,
name : 'resume' ,
type : 'options' ,
options : [
{
2022-06-03 10:23:49 -07:00
name : 'After Time Interval' ,
2021-08-21 05:11:32 -07:00
value : 'timeInterval' ,
description : 'Waits for a certain amount of time' ,
} ,
{
2022-06-03 10:23:49 -07:00
name : 'At Specified Time' ,
2021-08-21 05:11:32 -07:00
value : 'specificTime' ,
2021-08-26 08:27:19 -07:00
description : 'Waits until a specific date and time to continue' ,
2021-08-21 05:11:32 -07:00
} ,
{
2022-06-03 10:23:49 -07:00
name : 'On Webhook Call' ,
2021-08-21 05:11:32 -07:00
value : 'webhook' ,
2021-08-26 08:27:19 -07:00
description : 'Waits for a webhook call before continuing' ,
2021-08-21 05:11:32 -07:00
} ,
] ,
default : 'timeInterval' ,
2021-08-26 08:27:19 -07:00
description : 'Determines the waiting mode to use before the workflow continues' ,
2021-08-21 05:11:32 -07:00
} ,
// ----------------------------------
// resume:specificTime
// ----------------------------------
{
displayName : 'Date and Time' ,
name : 'dateTime' ,
type : 'dateTime' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
resume : [ 'specificTime' ] ,
2021-08-21 05:11:32 -07:00
} ,
} ,
default : '' ,
description : 'The date and time to wait for before continuing' ,
} ,
// ----------------------------------
// resume:timeInterval
// ----------------------------------
{
2021-08-26 08:27:19 -07:00
displayName : 'Wait Amount' ,
2021-08-21 05:11:32 -07:00
name : 'amount' ,
type : 'number' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
resume : [ 'timeInterval' ] ,
2021-08-21 05:11:32 -07:00
} ,
} ,
typeOptions : {
minValue : 0 ,
numberPrecision : 2 ,
} ,
default : 1 ,
description : 'The time to wait' ,
} ,
{
2021-08-26 08:27:19 -07:00
displayName : 'Wait Unit' ,
2021-08-21 05:11:32 -07:00
name : 'unit' ,
type : 'options' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
resume : [ 'timeInterval' ] ,
2021-08-21 05:11:32 -07:00
} ,
} ,
options : [
{
name : 'Seconds' ,
value : 'seconds' ,
} ,
{
name : 'Minutes' ,
value : 'minutes' ,
} ,
{
name : 'Hours' ,
value : 'hours' ,
} ,
{
name : 'Days' ,
value : 'days' ,
} ,
] ,
default : 'hours' ,
2021-08-26 08:27:19 -07:00
description : 'The time unit of the Wait Amount value' ,
2021-08-21 05:11:32 -07:00
} ,
// ----------------------------------
// resume:webhook
// ----------------------------------
{
2022-08-17 08:50:24 -07:00
displayName :
2022-09-29 14:02:25 -07:00
'The webhook URL will be generated at run time. It can be referenced with the <strong>$execution.resumeUrl</strong> variable. Send it somewhere before getting to this node. <a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.wait/?utm_source=n8n_app&utm_medium=node_settings_modal-credential_link&utm_campaign=n8n-nodes-base.wait" target="_blank">More info</a>' ,
2021-08-21 05:11:32 -07:00
name : 'webhookNotice' ,
type : 'notice' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
resume : [ 'webhook' ] ,
2021-08-21 05:11:32 -07:00
} ,
} ,
default : '' ,
} ,
{
displayName : 'HTTP Method' ,
name : 'httpMethod' ,
type : 'options' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
resume : [ 'webhook' ] ,
2021-08-21 05:11:32 -07:00
} ,
} ,
options : [
2022-02-20 01:30:01 -08:00
{
name : 'DELETE' ,
value : 'DELETE' ,
} ,
2021-08-21 05:11:32 -07:00
{
name : 'GET' ,
value : 'GET' ,
} ,
{
name : 'HEAD' ,
value : 'HEAD' ,
} ,
2022-02-20 01:30:01 -08:00
{
name : 'PATCH' ,
value : 'PATCH' ,
} ,
2021-08-21 05:11:32 -07:00
{
name : 'POST' ,
value : 'POST' ,
} ,
2022-02-20 01:30:01 -08:00
{
name : 'PUT' ,
value : 'PUT' ,
} ,
2021-08-21 05:11:32 -07:00
] ,
default : 'GET' ,
2021-08-26 08:27:19 -07:00
description : 'The HTTP method of the Webhook call' ,
2021-08-21 05:11:32 -07:00
} ,
{
displayName : 'Response Code' ,
name : 'responseCode' ,
type : 'number' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
resume : [ 'webhook' ] ,
2021-08-21 05:11:32 -07:00
} ,
} ,
typeOptions : {
minValue : 100 ,
maxValue : 599 ,
} ,
default : 200 ,
description : 'The HTTP Response code to return' ,
} ,
{
2021-11-05 10:19:23 -07:00
displayName : 'Respond' ,
2021-08-21 05:11:32 -07:00
name : 'responseMode' ,
type : 'options' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
resume : [ 'webhook' ] ,
2021-08-21 05:11:32 -07:00
} ,
} ,
options : [
{
2021-11-05 10:19:23 -07:00
name : 'Immediately' ,
2021-08-21 05:11:32 -07:00
value : 'onReceived' ,
2021-11-05 10:19:23 -07:00
description : 'As soon as this node executes' ,
2021-08-21 05:11:32 -07:00
} ,
{
2022-06-03 10:23:49 -07:00
name : 'When Last Node Finishes' ,
2021-08-21 05:11:32 -07:00
value : 'lastNode' ,
2021-11-05 10:19:23 -07:00
description : 'Returns data of the last-executed node' ,
2021-08-21 05:11:32 -07:00
} ,
2021-11-05 09:45:51 -07:00
{
2022-08-17 08:50:24 -07:00
name : "Using 'Respond to Webhook' Node" ,
2021-11-05 09:45:51 -07:00
value : 'responseNode' ,
2021-11-05 10:19:23 -07:00
description : 'Response defined in that node' ,
2021-11-05 09:45:51 -07:00
} ,
2021-08-21 05:11:32 -07:00
] ,
default : 'onReceived' ,
description : 'When and how to respond to the webhook' ,
} ,
{
displayName : 'Response Data' ,
name : 'responseData' ,
type : 'options' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
resume : [ 'webhook' ] ,
responseMode : [ 'lastNode' ] ,
2021-08-21 05:11:32 -07:00
} ,
} ,
options : [
{
name : 'All Entries' ,
value : 'allEntries' ,
2022-04-22 09:29:51 -07:00
description : 'Returns all the entries of the last node. Always returns an array.' ,
2021-08-21 05:11:32 -07:00
} ,
{
name : 'First Entry JSON' ,
value : 'firstEntryJson' ,
2022-08-17 08:50:24 -07:00
description :
'Returns the JSON data of the first entry of the last node. Always returns a JSON object.' ,
2021-08-21 05:11:32 -07:00
} ,
{
name : 'First Entry Binary' ,
value : 'firstEntryBinary' ,
2022-08-17 08:50:24 -07:00
description :
'Returns the binary data of the first entry of the last node. Always returns a binary file.' ,
2021-08-21 05:11:32 -07:00
} ,
] ,
default : 'firstEntryJson' ,
2022-08-17 08:50:24 -07:00
description :
'What data should be returned. If it should return all the items as array or only the first item as object.' ,
2021-08-21 05:11:32 -07:00
} ,
{
displayName : 'Property Name' ,
name : 'responseBinaryPropertyName' ,
type : 'string' ,
required : true ,
default : 'data' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
resume : [ 'webhook' ] ,
responseData : [ 'firstEntryBinary' ] ,
2021-08-21 05:11:32 -07:00
} ,
} ,
description : 'Name of the binary property to return' ,
} ,
{
2022-06-03 10:23:49 -07:00
displayName : 'Limit Wait Time' ,
2021-08-21 05:11:32 -07:00
name : 'limitWaitTime' ,
type : 'boolean' ,
default : false ,
2022-06-20 07:54:01 -07:00
// eslint-disable-next-line n8n-nodes-base/node-param-description-boolean-without-whether
2022-08-17 08:50:24 -07:00
description :
'If no webhook call is received, the workflow will automatically resume execution after the specified limit type' ,
2021-08-21 05:11:32 -07:00
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
resume : [ 'webhook' ] ,
2021-08-21 05:11:32 -07:00
} ,
} ,
} ,
{
2022-06-03 10:23:49 -07:00
displayName : 'Limit Type' ,
2021-08-21 05:11:32 -07:00
name : 'limitType' ,
type : 'options' ,
default : 'afterTimeInterval' ,
2022-08-17 08:50:24 -07:00
description :
'Sets the condition for the execution to resume. Can be a specified date or after some time.' ,
2021-08-21 05:11:32 -07:00
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
limitWaitTime : [ true ] ,
resume : [ 'webhook' ] ,
2021-08-21 05:11:32 -07:00
} ,
} ,
options : [
{
2022-06-03 10:23:49 -07:00
name : 'After Time Interval' ,
2021-08-26 08:27:19 -07:00
description : 'Waits for a certain amount of time' ,
2021-08-21 05:11:32 -07:00
value : 'afterTimeInterval' ,
} ,
{
2022-06-03 10:23:49 -07:00
name : 'At Specified Time' ,
2021-08-26 08:27:19 -07:00
description : 'Waits until the set date and time to continue' ,
2021-08-21 05:11:32 -07:00
value : 'atSpecifiedTime' ,
} ,
] ,
} ,
{
displayName : 'Amount' ,
name : 'resumeAmount' ,
type : 'number' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
limitType : [ 'afterTimeInterval' ] ,
limitWaitTime : [ true ] ,
resume : [ 'webhook' ] ,
2021-08-21 05:11:32 -07:00
} ,
} ,
typeOptions : {
minValue : 0 ,
numberPrecision : 2 ,
} ,
default : 1 ,
description : 'The time to wait' ,
} ,
{
displayName : 'Unit' ,
name : 'resumeUnit' ,
type : 'options' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
limitType : [ 'afterTimeInterval' ] ,
limitWaitTime : [ true ] ,
resume : [ 'webhook' ] ,
2021-08-21 05:11:32 -07:00
} ,
} ,
options : [
{
name : 'Seconds' ,
value : 'seconds' ,
} ,
{
name : 'Minutes' ,
value : 'minutes' ,
} ,
{
name : 'Hours' ,
value : 'hours' ,
} ,
{
name : 'Days' ,
value : 'days' ,
} ,
] ,
default : 'hours' ,
description : 'Unit of the interval value' ,
} ,
{
displayName : 'Max Date and Time' ,
name : 'maxDateAndTime' ,
type : 'dateTime' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
limitType : [ 'atSpecifiedTime' ] ,
limitWaitTime : [ true ] ,
resume : [ 'webhook' ] ,
2021-08-21 05:11:32 -07:00
} ,
} ,
default : '' ,
2021-08-26 08:27:19 -07:00
description : 'Continue execution after the specified date and time' ,
2021-08-21 05:11:32 -07:00
} ,
{
displayName : 'Options' ,
name : 'options' ,
type : 'collection' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
resume : [ 'webhook' ] ,
2021-08-21 05:11:32 -07:00
} ,
} ,
placeholder : 'Add Option' ,
default : { } ,
options : [
{
displayName : 'Binary Data' ,
name : 'binaryData' ,
type : 'boolean' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
'/httpMethod' : [ 'PATCH' , 'PUT' , 'POST' ] ,
2021-08-21 05:11:32 -07:00
} ,
} ,
default : false ,
2022-06-20 07:54:01 -07:00
description : 'Whether the webhook will receive binary data' ,
2021-08-21 05:11:32 -07:00
} ,
{
displayName : 'Binary Property' ,
name : 'binaryPropertyName' ,
type : 'string' ,
default : 'data' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
binaryData : [ true ] ,
2021-08-21 05:11:32 -07:00
} ,
} ,
2022-08-17 08:50:24 -07:00
description :
'Name of the binary property to which to write the data of the received file. If the data gets received via "Form-Data Multipart" it will be the prefix and a number starting with 0 will be attached to it.' ,
2021-08-21 05:11:32 -07:00
} ,
2022-01-02 01:20:12 -08:00
{
displayName : 'Ignore Bots' ,
name : 'ignoreBots' ,
type : 'boolean' ,
default : false ,
2022-08-17 08:50:24 -07:00
description :
'Whether to ignore requests from bots like link previewers and web crawlers' ,
2022-01-02 01:20:12 -08:00
} ,
2021-08-21 05:11:32 -07:00
{
displayName : 'Response Data' ,
name : 'responseData' ,
type : 'string' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
'/responseMode' : [ 'onReceived' ] ,
2021-08-21 05:11:32 -07:00
} ,
} ,
default : '' ,
placeholder : 'success' ,
description : 'Custom response data to send' ,
} ,
{
displayName : 'Response Content-Type' ,
name : 'responseContentType' ,
type : 'string' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
'/responseData' : [ 'firstEntryJson' ] ,
'/responseMode' : [ 'lastNode' ] ,
2021-08-21 05:11:32 -07:00
} ,
} ,
default : '' ,
placeholder : 'application/xml' ,
2022-05-20 14:47:24 -07:00
// eslint-disable-next-line n8n-nodes-base/node-param-description-miscased-json
2022-08-17 08:50:24 -07:00
description :
'Set a custom content-type to return if another one as the "application/json" should be returned' ,
2021-08-21 05:11:32 -07:00
} ,
{
displayName : 'Response Headers' ,
name : 'responseHeaders' ,
placeholder : 'Add Response Header' ,
description : 'Add headers to the webhook response' ,
type : 'fixedCollection' ,
typeOptions : {
multipleValues : true ,
} ,
default : { } ,
options : [
{
name : 'entries' ,
displayName : 'Entries' ,
values : [
{
displayName : 'Name' ,
name : 'name' ,
type : 'string' ,
default : '' ,
description : 'Name of the header' ,
} ,
{
displayName : 'Value' ,
name : 'value' ,
type : 'string' ,
default : '' ,
description : 'Value of the header' ,
} ,
] ,
} ,
] ,
} ,
{
displayName : 'Property Name' ,
name : 'responsePropertyName' ,
type : 'string' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
'/responseData' : [ 'firstEntryJson' ] ,
'/responseMode' : [ 'lastNode' ] ,
2021-08-21 05:11:32 -07:00
} ,
} ,
default : 'data' ,
description : 'Name of the property to return the data of instead of the whole JSON' ,
} ,
{
displayName : 'Webhook Suffix' ,
name : 'webhookSuffix' ,
type : 'string' ,
default : '' ,
placeholder : 'webhook' ,
2022-08-17 08:50:24 -07:00
description :
'This suffix path will be appended to the restart URL. Helpful when using multiple wait nodes. Note: Does not support expressions.' ,
2021-08-21 05:11:32 -07:00
} ,
// {
// displayName: 'Raw Body',
// name: 'rawBody',
// type: 'boolean',
// displayOptions: {
// hide: {
// binaryData: [
// true,
// ],
// },
// },
// default: false,
// description: 'Raw body (binary)',
// },
] ,
} ,
] ,
} ;
async webhook ( this : IWebhookFunctions ) : Promise < IWebhookResponseData > {
// INFO: Currently (20.06.2021) 100% identical with Webook-Node
const incomingAuthentication = this . getNodeParameter ( 'incomingAuthentication' ) as string ;
const options = this . getNodeParameter ( 'options' , { } ) as IDataObject ;
const req = this . getRequestObject ( ) ;
const resp = this . getResponseObject ( ) ;
const headers = this . getHeaderData ( ) ;
const realm = 'Webhook' ;
2021-12-09 05:28:14 -08:00
const ignoreBots = options . ignoreBots as boolean ;
if ( ignoreBots && isbot ( ( headers as IDataObject ) [ 'user-agent' ] as string ) ) {
return authorizationError ( resp , realm , 403 ) ;
}
2021-08-21 05:11:32 -07:00
if ( incomingAuthentication === 'basicAuth' ) {
// Basic authorization is needed to call webhook
2022-04-14 23:00:47 -07:00
let httpBasicAuth : ICredentialDataDecryptedObject | undefined ;
try {
httpBasicAuth = await this . getCredentials ( 'httpBasicAuth' ) ;
} catch ( error ) {
// Do nothing
}
2021-08-21 05:11:32 -07:00
if ( httpBasicAuth === undefined || ! httpBasicAuth . user || ! httpBasicAuth . password ) {
// Data is not defined on node so can not authenticate
return authorizationError ( resp , realm , 500 , 'No authentication data defined on node!' ) ;
}
const basicAuthData = basicAuth ( req ) ;
if ( basicAuthData === undefined ) {
// Authorization data is missing
return authorizationError ( resp , realm , 401 ) ;
}
2022-08-17 08:50:24 -07:00
if (
2022-12-02 12:54:28 -08:00
basicAuthData . name !== httpBasicAuth . user ||
basicAuthData . pass !== httpBasicAuth . password
2022-08-17 08:50:24 -07:00
) {
2021-08-21 05:11:32 -07:00
// Provided authentication data is wrong
return authorizationError ( resp , realm , 403 ) ;
}
} else if ( incomingAuthentication === 'headerAuth' ) {
// Special header with value is needed to call webhook
2022-04-14 23:00:47 -07:00
let httpHeaderAuth : ICredentialDataDecryptedObject | undefined ;
try {
httpHeaderAuth = await this . getCredentials ( 'httpHeaderAuth' ) ;
} catch ( error ) {
// Do nothing
}
2021-08-21 05:11:32 -07:00
if ( httpHeaderAuth === undefined || ! httpHeaderAuth . name || ! httpHeaderAuth . value ) {
// Data is not defined on node so can not authenticate
return authorizationError ( resp , realm , 500 , 'No authentication data defined on node!' ) ;
}
const headerName = ( httpHeaderAuth . name as string ) . toLowerCase ( ) ;
2022-08-17 08:50:24 -07:00
const headerValue = httpHeaderAuth . value as string ;
2021-08-21 05:11:32 -07:00
2022-08-17 08:50:24 -07:00
if (
! headers . hasOwnProperty ( headerName ) ||
( headers as IDataObject ) [ headerName ] !== headerValue
) {
2021-08-21 05:11:32 -07:00
// Provided authentication data is wrong
return authorizationError ( resp , realm , 403 ) ;
}
}
2022-11-24 07:54:43 -08:00
const mimeType = headers [ 'content-type' ] ? ? 'application/json' ;
2021-08-21 05:11:32 -07:00
if ( mimeType . includes ( 'multipart/form-data' ) ) {
2021-08-29 11:56:19 -07:00
const form = new formidable . IncomingForm ( { multiples : true } ) ;
2021-08-21 05:11:32 -07:00
2022-11-08 06:28:21 -08:00
return new Promise ( ( resolve , _reject ) = > {
2021-08-21 05:11:32 -07:00
form . parse ( req , async ( err , data , files ) = > {
const returnItem : INodeExecutionData = {
binary : { } ,
json : {
headers ,
params : this.getParamsData ( ) ,
query : this.getQueryData ( ) ,
body : data ,
} ,
} ;
let count = 0 ;
2021-08-29 11:56:19 -07:00
for ( const xfile of Object . keys ( files ) ) {
2021-08-29 14:08:56 -07:00
const processFiles : formidable.File [ ] = [ ] ;
2021-08-29 11:56:19 -07:00
let multiFile = false ;
if ( Array . isArray ( files [ xfile ] ) ) {
2022-08-17 08:50:24 -07:00
processFiles . push ( . . . ( files [ xfile ] as formidable . File [ ] ) ) ;
2021-08-29 11:56:19 -07:00
multiFile = true ;
} else {
2021-08-29 14:08:56 -07:00
processFiles . push ( files [ xfile ] as formidable . File ) ;
2021-08-21 05:11:32 -07:00
}
2021-08-29 11:56:19 -07:00
let fileCount = 0 ;
2021-08-29 14:08:56 -07:00
for ( const file of processFiles ) {
2021-08-29 11:56:19 -07:00
let binaryPropertyName = xfile ;
if ( binaryPropertyName . endsWith ( '[]' ) ) {
binaryPropertyName = binaryPropertyName . slice ( 0 , - 2 ) ;
}
2022-12-02 12:54:28 -08:00
if ( multiFile ) {
2021-08-29 11:56:19 -07:00
binaryPropertyName += fileCount ++ ;
}
if ( options . binaryPropertyName ) {
binaryPropertyName = ` ${ options . binaryPropertyName } ${ count } ` ;
}
2022-11-24 07:54:43 -08:00
const fileJson = file . toJSON ( ) ;
returnItem . binary ! [ binaryPropertyName ] = await this . helpers . copyBinaryFile (
file . path ,
fileJson . name || fileJson . filename ,
2022-08-17 08:50:24 -07:00
fileJson . type as string ,
) ;
2021-08-29 11:56:19 -07:00
count += 1 ;
}
2021-08-21 05:11:32 -07:00
}
resolve ( {
2022-08-17 08:50:24 -07:00
workflowData : [ [ returnItem ] ] ,
2021-08-21 05:11:32 -07:00
} ) ;
} ) ;
} ) ;
}
if ( options . binaryData === true ) {
2022-11-24 07:54:43 -08:00
const binaryFile = await tmpFile ( { prefix : 'n8n-webhook-' } ) ;
2021-08-21 05:11:32 -07:00
2022-11-24 07:54:43 -08:00
try {
await pipeline ( req , fs . createWriteStream ( binaryFile . path ) ) ;
2021-08-21 05:11:32 -07:00
2022-11-24 07:54:43 -08:00
const returnItem : INodeExecutionData = {
binary : { } ,
json : {
headers ,
params : this.getParamsData ( ) ,
query : this.getQueryData ( ) ,
body : this.getBodyData ( ) ,
} ,
} ;
2021-08-21 05:11:32 -07:00
2022-11-24 07:54:43 -08:00
const binaryPropertyName = ( options . binaryPropertyName || 'data' ) as string ;
returnItem . binary ! [ binaryPropertyName ] = await this . helpers . copyBinaryFile (
binaryFile . path ,
mimeType ,
) ;
2021-08-21 05:11:32 -07:00
2022-11-24 07:54:43 -08:00
return {
workflowData : [ [ returnItem ] ] ,
} ;
} catch ( error ) {
throw new NodeOperationError ( this . getNode ( ) , error ) ;
} finally {
await binaryFile . cleanup ( ) ;
}
2021-08-21 05:11:32 -07:00
}
const response : INodeExecutionData = {
json : {
headers ,
params : this.getParamsData ( ) ,
query : this.getQueryData ( ) ,
body : this.getBodyData ( ) ,
} ,
} ;
if ( options . rawBody ) {
response . binary = {
data : {
data : req.rawBody.toString ( BINARY_ENCODING ) ,
mimeType ,
} ,
} ;
}
let webhookResponse : string | undefined ;
if ( options . responseData ) {
webhookResponse = options . responseData as string ;
}
return {
webhookResponse ,
2022-08-17 08:50:24 -07:00
workflowData : [ [ response ] ] ,
2021-08-21 05:11:32 -07:00
} ;
}
async execute ( this : IExecuteFunctions ) : Promise < INodeExecutionData [ ] [ ] > {
const resume = this . getNodeParameter ( 'resume' , 0 ) as string ;
if ( resume === 'webhook' ) {
let waitTill = new Date ( WAIT_TIME_UNLIMITED ) ;
const limitWaitTime = this . getNodeParameter ( 'limitWaitTime' , 0 ) ;
if ( limitWaitTime === true ) {
const limitType = this . getNodeParameter ( 'limitType' , 0 ) ;
if ( limitType === 'afterTimeInterval' ) {
let waitAmount = this . getNodeParameter ( 'resumeAmount' , 0 ) as number ;
const resumeUnit = this . getNodeParameter ( 'resumeUnit' , 0 ) ;
if ( resumeUnit === 'minutes' ) {
waitAmount *= 60 ;
}
if ( resumeUnit === 'hours' ) {
waitAmount *= 60 * 60 ;
}
if ( resumeUnit === 'days' ) {
waitAmount *= 60 * 60 * 24 ;
}
waitAmount *= 1000 ;
waitTill = new Date ( new Date ( ) . getTime ( ) + waitAmount ) ;
} else {
waitTill = new Date ( this . getNodeParameter ( 'maxDateAndTime' , 0 ) as string ) ;
}
}
await this . putExecutionToWait ( waitTill ) ;
return [ this . getInputData ( ) ] ;
}
let waitTill : Date ;
if ( resume === 'timeInterval' ) {
const unit = this . getNodeParameter ( 'unit' , 0 ) as string ;
let waitAmount = this . getNodeParameter ( 'amount' , 0 ) as number ;
if ( unit === 'minutes' ) {
waitAmount *= 60 ;
}
if ( unit === 'hours' ) {
waitAmount *= 60 * 60 ;
}
if ( unit === 'days' ) {
waitAmount *= 60 * 60 * 24 ;
}
waitAmount *= 1000 ;
waitTill = new Date ( new Date ( ) . getTime ( ) + waitAmount ) ;
} else {
// resume: dateTime
const dateTime = this . getNodeParameter ( 'dateTime' , 0 ) as string ;
waitTill = new Date ( dateTime ) ;
}
const waitValue = Math . max ( waitTill . getTime ( ) - new Date ( ) . getTime ( ) , 0 ) ;
if ( waitValue < 65000 ) {
// If wait time is shorter than 65 seconds leave execution active because
// we just check the database every 60 seconds.
2022-11-08 06:28:21 -08:00
return new Promise ( ( resolve , _reject ) = > {
2021-08-21 05:11:32 -07:00
setTimeout ( ( ) = > {
resolve ( [ this . getInputData ( ) ] ) ;
} , waitValue ) ;
} ) ;
}
// If longer than 60 seconds put execution to wait
await this . putExecutionToWait ( waitTill ) ;
return [ this . getInputData ( ) ] ;
}
}