n8n-nodes-hubseal 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/credentials/HubSealApi.credentials.d.ts +12 -0
- package/dist/credentials/HubSealApi.credentials.js +76 -0
- package/dist/nodes/HubSeal/HubSeal.node.d.ts +29 -0
- package/dist/nodes/HubSeal/HubSeal.node.js +741 -0
- package/dist/nodes/HubSeal/HubSealTrigger.node.d.ts +22 -0
- package/dist/nodes/HubSeal/HubSealTrigger.node.js +260 -0
- package/dist/nodes/HubSeal/hubseal.svg +5 -0
- package/package.json +56 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HubSeal API Credentials for n8n
|
|
3
|
+
*/
|
|
4
|
+
import { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
|
|
5
|
+
export declare class HubSealApi implements ICredentialType {
|
|
6
|
+
name: string;
|
|
7
|
+
displayName: string;
|
|
8
|
+
documentationUrl: string;
|
|
9
|
+
properties: INodeProperties[];
|
|
10
|
+
authenticate: IAuthenticateGeneric;
|
|
11
|
+
test: ICredentialTestRequest;
|
|
12
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* HubSeal API Credentials for n8n
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.HubSealApi = void 0;
|
|
7
|
+
class HubSealApi {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.name = 'hubSealApi';
|
|
10
|
+
this.displayName = 'HubSeal API';
|
|
11
|
+
this.documentationUrl = 'https://docs.hubwf.com/api';
|
|
12
|
+
this.properties = [
|
|
13
|
+
{
|
|
14
|
+
displayName: 'API Key',
|
|
15
|
+
name: 'apiKey',
|
|
16
|
+
type: 'string',
|
|
17
|
+
typeOptions: {
|
|
18
|
+
password: true,
|
|
19
|
+
},
|
|
20
|
+
default: '',
|
|
21
|
+
required: true,
|
|
22
|
+
description: 'Your HubSeal API key (format: hubwf_live_sk_xxxxx or hubwf_test_sk_xxxxx)',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
displayName: 'Tenant ID',
|
|
26
|
+
name: 'tenantId',
|
|
27
|
+
type: 'string',
|
|
28
|
+
default: '',
|
|
29
|
+
required: true,
|
|
30
|
+
description: 'Your organization/tenant ID from HubWF',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
displayName: 'Workspace ID',
|
|
34
|
+
name: 'workspaceId',
|
|
35
|
+
type: 'string',
|
|
36
|
+
default: '',
|
|
37
|
+
required: false,
|
|
38
|
+
description: 'Workspace ID (required for individual tenants only)',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
displayName: 'Environment',
|
|
42
|
+
name: 'environment',
|
|
43
|
+
type: 'options',
|
|
44
|
+
options: [
|
|
45
|
+
{
|
|
46
|
+
name: 'Production',
|
|
47
|
+
value: 'production',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'Staging',
|
|
51
|
+
value: 'staging',
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
default: 'production',
|
|
55
|
+
description: 'API environment',
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
this.authenticate = {
|
|
59
|
+
type: 'generic',
|
|
60
|
+
properties: {
|
|
61
|
+
headers: {
|
|
62
|
+
Authorization: '=Bearer {{$credentials.apiKey}}',
|
|
63
|
+
'X-Tenant-Id': '={{$credentials.tenantId}}',
|
|
64
|
+
'X-Workspace-Id': '={{$credentials.workspaceId}}',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
this.test = {
|
|
69
|
+
request: {
|
|
70
|
+
baseURL: '={{$credentials.environment === "production" ? "https://api.hubwf.com" : "https://api-staging.hubwf.com"}}',
|
|
71
|
+
url: '/v1/webhooks',
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.HubSealApi = HubSealApi;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ╔═══════════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
* ║ HUBSEAL N8N NODE ║
|
|
4
|
+
* ║ ║
|
|
5
|
+
* ║ n8n community node for HubSeal e-signature integration ║
|
|
6
|
+
* ║ ║
|
|
7
|
+
* ║ TRIGGERS: ║
|
|
8
|
+
* ║ - Document Created ║
|
|
9
|
+
* ║ - Document Signed ║
|
|
10
|
+
* ║ - Document Completed ║
|
|
11
|
+
* ║ - Document Declined ║
|
|
12
|
+
* ║ ║
|
|
13
|
+
* ║ ACTIONS: ║
|
|
14
|
+
* ║ - Create Signature Request ║
|
|
15
|
+
* ║ - Create from Template ║
|
|
16
|
+
* ║ - Get Document ║
|
|
17
|
+
* ║ - List Documents ║
|
|
18
|
+
* ║ - Download Signed Document ║
|
|
19
|
+
* ║ - Send Reminder ║
|
|
20
|
+
* ║ - Void Document ║
|
|
21
|
+
* ║ ║
|
|
22
|
+
* ║ @version 1.0.0 ║
|
|
23
|
+
* ╚═══════════════════════════════════════════════════════════════════════════════╝
|
|
24
|
+
*/
|
|
25
|
+
import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
26
|
+
export declare class HubSeal implements INodeType {
|
|
27
|
+
description: INodeTypeDescription;
|
|
28
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
29
|
+
}
|
|
@@ -0,0 +1,741 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ╔═══════════════════════════════════════════════════════════════════════════════╗
|
|
4
|
+
* ║ HUBSEAL N8N NODE ║
|
|
5
|
+
* ║ ║
|
|
6
|
+
* ║ n8n community node for HubSeal e-signature integration ║
|
|
7
|
+
* ║ ║
|
|
8
|
+
* ║ TRIGGERS: ║
|
|
9
|
+
* ║ - Document Created ║
|
|
10
|
+
* ║ - Document Signed ║
|
|
11
|
+
* ║ - Document Completed ║
|
|
12
|
+
* ║ - Document Declined ║
|
|
13
|
+
* ║ ║
|
|
14
|
+
* ║ ACTIONS: ║
|
|
15
|
+
* ║ - Create Signature Request ║
|
|
16
|
+
* ║ - Create from Template ║
|
|
17
|
+
* ║ - Get Document ║
|
|
18
|
+
* ║ - List Documents ║
|
|
19
|
+
* ║ - Download Signed Document ║
|
|
20
|
+
* ║ - Send Reminder ║
|
|
21
|
+
* ║ - Void Document ║
|
|
22
|
+
* ║ ║
|
|
23
|
+
* ║ @version 1.0.0 ║
|
|
24
|
+
* ╚═══════════════════════════════════════════════════════════════════════════════╝
|
|
25
|
+
*/
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.HubSeal = void 0;
|
|
28
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
29
|
+
class HubSeal {
|
|
30
|
+
constructor() {
|
|
31
|
+
this.description = {
|
|
32
|
+
displayName: 'HubSeal',
|
|
33
|
+
name: 'hubSeal',
|
|
34
|
+
icon: 'file:hubseal.svg',
|
|
35
|
+
group: ['transform'],
|
|
36
|
+
version: 1,
|
|
37
|
+
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
|
38
|
+
description: 'E-Signature workflow automation with HubSeal',
|
|
39
|
+
defaults: {
|
|
40
|
+
name: 'HubSeal',
|
|
41
|
+
},
|
|
42
|
+
inputs: ['main'],
|
|
43
|
+
outputs: ['main'],
|
|
44
|
+
credentials: [
|
|
45
|
+
{
|
|
46
|
+
name: 'hubSealApi',
|
|
47
|
+
required: true,
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
properties: [
|
|
51
|
+
// ============================================================
|
|
52
|
+
// RESOURCE SELECTOR
|
|
53
|
+
// ============================================================
|
|
54
|
+
{
|
|
55
|
+
displayName: 'Resource',
|
|
56
|
+
name: 'resource',
|
|
57
|
+
type: 'options',
|
|
58
|
+
noDataExpression: true,
|
|
59
|
+
options: [
|
|
60
|
+
{
|
|
61
|
+
name: 'Document',
|
|
62
|
+
value: 'document',
|
|
63
|
+
description: 'Manage signature requests and documents',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'Template',
|
|
67
|
+
value: 'template',
|
|
68
|
+
description: 'Work with signature templates',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 'Webhook',
|
|
72
|
+
value: 'webhook',
|
|
73
|
+
description: 'Manage webhook subscriptions',
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
default: 'document',
|
|
77
|
+
},
|
|
78
|
+
// ============================================================
|
|
79
|
+
// DOCUMENT OPERATIONS
|
|
80
|
+
// ============================================================
|
|
81
|
+
{
|
|
82
|
+
displayName: 'Operation',
|
|
83
|
+
name: 'operation',
|
|
84
|
+
type: 'options',
|
|
85
|
+
noDataExpression: true,
|
|
86
|
+
displayOptions: {
|
|
87
|
+
show: {
|
|
88
|
+
resource: ['document'],
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
options: [
|
|
92
|
+
{
|
|
93
|
+
name: 'Create',
|
|
94
|
+
value: 'create',
|
|
95
|
+
description: 'Create a new signature request',
|
|
96
|
+
action: 'Create a signature request',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: 'Create from Template',
|
|
100
|
+
value: 'createFromTemplate',
|
|
101
|
+
description: 'Create signature request from template',
|
|
102
|
+
action: 'Create from template',
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: 'Get',
|
|
106
|
+
value: 'get',
|
|
107
|
+
description: 'Get a signature request',
|
|
108
|
+
action: 'Get a signature request',
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: 'List',
|
|
112
|
+
value: 'list',
|
|
113
|
+
description: 'List signature requests',
|
|
114
|
+
action: 'List signature requests',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: 'Download',
|
|
118
|
+
value: 'download',
|
|
119
|
+
description: 'Download signed document',
|
|
120
|
+
action: 'Download signed document',
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: 'Send Reminder',
|
|
124
|
+
value: 'remind',
|
|
125
|
+
description: 'Send reminder to pending signers',
|
|
126
|
+
action: 'Send reminder',
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: 'Void',
|
|
130
|
+
value: 'void',
|
|
131
|
+
description: 'Void/cancel a signature request',
|
|
132
|
+
action: 'Void signature request',
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
default: 'create',
|
|
136
|
+
},
|
|
137
|
+
// ============================================================
|
|
138
|
+
// TEMPLATE OPERATIONS
|
|
139
|
+
// ============================================================
|
|
140
|
+
{
|
|
141
|
+
displayName: 'Operation',
|
|
142
|
+
name: 'operation',
|
|
143
|
+
type: 'options',
|
|
144
|
+
noDataExpression: true,
|
|
145
|
+
displayOptions: {
|
|
146
|
+
show: {
|
|
147
|
+
resource: ['template'],
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
options: [
|
|
151
|
+
{
|
|
152
|
+
name: 'Get',
|
|
153
|
+
value: 'get',
|
|
154
|
+
description: 'Get a template',
|
|
155
|
+
action: 'Get a template',
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: 'List',
|
|
159
|
+
value: 'list',
|
|
160
|
+
description: 'List templates',
|
|
161
|
+
action: 'List templates',
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
default: 'list',
|
|
165
|
+
},
|
|
166
|
+
// ============================================================
|
|
167
|
+
// WEBHOOK OPERATIONS
|
|
168
|
+
// ============================================================
|
|
169
|
+
{
|
|
170
|
+
displayName: 'Operation',
|
|
171
|
+
name: 'operation',
|
|
172
|
+
type: 'options',
|
|
173
|
+
noDataExpression: true,
|
|
174
|
+
displayOptions: {
|
|
175
|
+
show: {
|
|
176
|
+
resource: ['webhook'],
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
options: [
|
|
180
|
+
{
|
|
181
|
+
name: 'Create',
|
|
182
|
+
value: 'create',
|
|
183
|
+
description: 'Create webhook endpoint',
|
|
184
|
+
action: 'Create webhook',
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
name: 'Delete',
|
|
188
|
+
value: 'delete',
|
|
189
|
+
description: 'Delete webhook endpoint',
|
|
190
|
+
action: 'Delete webhook',
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
name: 'List',
|
|
194
|
+
value: 'list',
|
|
195
|
+
description: 'List webhook endpoints',
|
|
196
|
+
action: 'List webhooks',
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
default: 'list',
|
|
200
|
+
},
|
|
201
|
+
// ============================================================
|
|
202
|
+
// DOCUMENT: CREATE FIELDS
|
|
203
|
+
// ============================================================
|
|
204
|
+
{
|
|
205
|
+
displayName: 'Document Name',
|
|
206
|
+
name: 'documentName',
|
|
207
|
+
type: 'string',
|
|
208
|
+
required: true,
|
|
209
|
+
displayOptions: {
|
|
210
|
+
show: {
|
|
211
|
+
resource: ['document'],
|
|
212
|
+
operation: ['create'],
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
default: '',
|
|
216
|
+
description: 'Name of the document',
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
displayName: 'Document URL',
|
|
220
|
+
name: 'documentUrl',
|
|
221
|
+
type: 'string',
|
|
222
|
+
required: true,
|
|
223
|
+
displayOptions: {
|
|
224
|
+
show: {
|
|
225
|
+
resource: ['document'],
|
|
226
|
+
operation: ['create'],
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
default: '',
|
|
230
|
+
description: 'URL of the PDF document to sign',
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
displayName: 'Recipients',
|
|
234
|
+
name: 'recipients',
|
|
235
|
+
type: 'fixedCollection',
|
|
236
|
+
typeOptions: {
|
|
237
|
+
multipleValues: true,
|
|
238
|
+
},
|
|
239
|
+
displayOptions: {
|
|
240
|
+
show: {
|
|
241
|
+
resource: ['document'],
|
|
242
|
+
operation: ['create', 'createFromTemplate'],
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
default: {},
|
|
246
|
+
options: [
|
|
247
|
+
{
|
|
248
|
+
name: 'recipientValues',
|
|
249
|
+
displayName: 'Recipient',
|
|
250
|
+
values: [
|
|
251
|
+
{
|
|
252
|
+
displayName: 'Name',
|
|
253
|
+
name: 'name',
|
|
254
|
+
type: 'string',
|
|
255
|
+
default: '',
|
|
256
|
+
description: 'Recipient name',
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
displayName: 'Email',
|
|
260
|
+
name: 'email',
|
|
261
|
+
type: 'string',
|
|
262
|
+
placeholder: 'name@email.com',
|
|
263
|
+
default: '',
|
|
264
|
+
description: 'Recipient email address',
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
displayName: 'Role',
|
|
268
|
+
name: 'role',
|
|
269
|
+
type: 'string',
|
|
270
|
+
default: 'Signer',
|
|
271
|
+
description: 'Role name (e.g., Signer, Approver)',
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
},
|
|
275
|
+
],
|
|
276
|
+
description: 'Recipients who need to sign',
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
displayName: 'Additional Fields',
|
|
280
|
+
name: 'additionalFields',
|
|
281
|
+
type: 'collection',
|
|
282
|
+
placeholder: 'Add Field',
|
|
283
|
+
default: {},
|
|
284
|
+
displayOptions: {
|
|
285
|
+
show: {
|
|
286
|
+
resource: ['document'],
|
|
287
|
+
operation: ['create'],
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
options: [
|
|
291
|
+
{
|
|
292
|
+
displayName: 'Message',
|
|
293
|
+
name: 'message',
|
|
294
|
+
type: 'string',
|
|
295
|
+
typeOptions: {
|
|
296
|
+
rows: 4,
|
|
297
|
+
},
|
|
298
|
+
default: '',
|
|
299
|
+
description: 'Message to include in the signing email',
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
displayName: 'Expires In Days',
|
|
303
|
+
name: 'expiresInDays',
|
|
304
|
+
type: 'number',
|
|
305
|
+
default: 7,
|
|
306
|
+
description: 'Number of days until request expires',
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
displayName: 'Signing Order',
|
|
310
|
+
name: 'signingOrder',
|
|
311
|
+
type: 'options',
|
|
312
|
+
options: [
|
|
313
|
+
{
|
|
314
|
+
name: 'Parallel',
|
|
315
|
+
value: 'parallel',
|
|
316
|
+
description: 'All signers can sign simultaneously',
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
name: 'Sequential',
|
|
320
|
+
value: 'sequential',
|
|
321
|
+
description: 'Signers must sign in order',
|
|
322
|
+
},
|
|
323
|
+
],
|
|
324
|
+
default: 'parallel',
|
|
325
|
+
description: 'Order in which signers should sign',
|
|
326
|
+
},
|
|
327
|
+
],
|
|
328
|
+
},
|
|
329
|
+
// ============================================================
|
|
330
|
+
// DOCUMENT: CREATE FROM TEMPLATE FIELDS
|
|
331
|
+
// ============================================================
|
|
332
|
+
{
|
|
333
|
+
displayName: 'Template ID',
|
|
334
|
+
name: 'templateId',
|
|
335
|
+
type: 'string',
|
|
336
|
+
required: true,
|
|
337
|
+
displayOptions: {
|
|
338
|
+
show: {
|
|
339
|
+
resource: ['document'],
|
|
340
|
+
operation: ['createFromTemplate'],
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
default: '',
|
|
344
|
+
description: 'ID of the template to use',
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
displayName: 'Field Values',
|
|
348
|
+
name: 'fieldValues',
|
|
349
|
+
type: 'fixedCollection',
|
|
350
|
+
typeOptions: {
|
|
351
|
+
multipleValues: true,
|
|
352
|
+
},
|
|
353
|
+
displayOptions: {
|
|
354
|
+
show: {
|
|
355
|
+
resource: ['document'],
|
|
356
|
+
operation: ['createFromTemplate'],
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
default: {},
|
|
360
|
+
options: [
|
|
361
|
+
{
|
|
362
|
+
name: 'fieldValueItems',
|
|
363
|
+
displayName: 'Field Value',
|
|
364
|
+
values: [
|
|
365
|
+
{
|
|
366
|
+
displayName: 'Field Name',
|
|
367
|
+
name: 'name',
|
|
368
|
+
type: 'string',
|
|
369
|
+
default: '',
|
|
370
|
+
description: 'Name of the template field',
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
displayName: 'Value',
|
|
374
|
+
name: 'value',
|
|
375
|
+
type: 'string',
|
|
376
|
+
default: '',
|
|
377
|
+
description: 'Value to pre-fill',
|
|
378
|
+
},
|
|
379
|
+
],
|
|
380
|
+
},
|
|
381
|
+
],
|
|
382
|
+
description: 'Pre-fill template fields with values',
|
|
383
|
+
},
|
|
384
|
+
// ============================================================
|
|
385
|
+
// DOCUMENT: GET/DOWNLOAD/REMIND/VOID FIELDS
|
|
386
|
+
// ============================================================
|
|
387
|
+
{
|
|
388
|
+
displayName: 'Document ID',
|
|
389
|
+
name: 'documentId',
|
|
390
|
+
type: 'string',
|
|
391
|
+
required: true,
|
|
392
|
+
displayOptions: {
|
|
393
|
+
show: {
|
|
394
|
+
resource: ['document'],
|
|
395
|
+
operation: ['get', 'download', 'remind', 'void'],
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
default: '',
|
|
399
|
+
description: 'ID of the document',
|
|
400
|
+
},
|
|
401
|
+
// ============================================================
|
|
402
|
+
// DOCUMENT: LIST FILTERS
|
|
403
|
+
// ============================================================
|
|
404
|
+
{
|
|
405
|
+
displayName: 'Filters',
|
|
406
|
+
name: 'filters',
|
|
407
|
+
type: 'collection',
|
|
408
|
+
placeholder: 'Add Filter',
|
|
409
|
+
default: {},
|
|
410
|
+
displayOptions: {
|
|
411
|
+
show: {
|
|
412
|
+
resource: ['document'],
|
|
413
|
+
operation: ['list'],
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
options: [
|
|
417
|
+
{
|
|
418
|
+
displayName: 'Status',
|
|
419
|
+
name: 'status',
|
|
420
|
+
type: 'options',
|
|
421
|
+
options: [
|
|
422
|
+
{ name: 'All', value: '' },
|
|
423
|
+
{ name: 'Pending', value: 'pending' },
|
|
424
|
+
{ name: 'Completed', value: 'completed' },
|
|
425
|
+
{ name: 'Declined', value: 'declined' },
|
|
426
|
+
{ name: 'Voided', value: 'voided' },
|
|
427
|
+
{ name: 'Expired', value: 'expired' },
|
|
428
|
+
],
|
|
429
|
+
default: '',
|
|
430
|
+
description: 'Filter by status',
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
displayName: 'Limit',
|
|
434
|
+
name: 'limit',
|
|
435
|
+
type: 'number',
|
|
436
|
+
default: 50,
|
|
437
|
+
description: 'Max number of results',
|
|
438
|
+
},
|
|
439
|
+
],
|
|
440
|
+
},
|
|
441
|
+
// ============================================================
|
|
442
|
+
// TEMPLATE: GET FIELDS
|
|
443
|
+
// ============================================================
|
|
444
|
+
{
|
|
445
|
+
displayName: 'Template ID',
|
|
446
|
+
name: 'templateIdGet',
|
|
447
|
+
type: 'string',
|
|
448
|
+
required: true,
|
|
449
|
+
displayOptions: {
|
|
450
|
+
show: {
|
|
451
|
+
resource: ['template'],
|
|
452
|
+
operation: ['get'],
|
|
453
|
+
},
|
|
454
|
+
},
|
|
455
|
+
default: '',
|
|
456
|
+
description: 'ID of the template',
|
|
457
|
+
},
|
|
458
|
+
// ============================================================
|
|
459
|
+
// WEBHOOK: CREATE FIELDS
|
|
460
|
+
// ============================================================
|
|
461
|
+
{
|
|
462
|
+
displayName: 'Webhook URL',
|
|
463
|
+
name: 'webhookUrl',
|
|
464
|
+
type: 'string',
|
|
465
|
+
required: true,
|
|
466
|
+
displayOptions: {
|
|
467
|
+
show: {
|
|
468
|
+
resource: ['webhook'],
|
|
469
|
+
operation: ['create'],
|
|
470
|
+
},
|
|
471
|
+
},
|
|
472
|
+
default: '',
|
|
473
|
+
description: 'URL to receive webhook events',
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
displayName: 'Events',
|
|
477
|
+
name: 'events',
|
|
478
|
+
type: 'multiOptions',
|
|
479
|
+
displayOptions: {
|
|
480
|
+
show: {
|
|
481
|
+
resource: ['webhook'],
|
|
482
|
+
operation: ['create'],
|
|
483
|
+
},
|
|
484
|
+
},
|
|
485
|
+
options: [
|
|
486
|
+
{ name: 'Document Created', value: 'signature.request.created' },
|
|
487
|
+
{ name: 'Document Sent', value: 'signature.request.sent' },
|
|
488
|
+
{ name: 'Document Viewed', value: 'signature.request.viewed' },
|
|
489
|
+
{ name: 'Document Completed', value: 'signature.request.completed' },
|
|
490
|
+
{ name: 'Document Declined', value: 'signature.request.declined' },
|
|
491
|
+
{ name: 'Document Voided', value: 'signature.request.voided' },
|
|
492
|
+
{ name: 'Document Expired', value: 'signature.request.expired' },
|
|
493
|
+
{ name: 'Signer Completed', value: 'signature.signer.completed' },
|
|
494
|
+
{ name: 'Signer Declined', value: 'signature.signer.declined' },
|
|
495
|
+
],
|
|
496
|
+
default: ['signature.request.completed'],
|
|
497
|
+
description: 'Events to subscribe to',
|
|
498
|
+
},
|
|
499
|
+
// ============================================================
|
|
500
|
+
// WEBHOOK: DELETE FIELDS
|
|
501
|
+
// ============================================================
|
|
502
|
+
{
|
|
503
|
+
displayName: 'Webhook ID',
|
|
504
|
+
name: 'webhookId',
|
|
505
|
+
type: 'string',
|
|
506
|
+
required: true,
|
|
507
|
+
displayOptions: {
|
|
508
|
+
show: {
|
|
509
|
+
resource: ['webhook'],
|
|
510
|
+
operation: ['delete'],
|
|
511
|
+
},
|
|
512
|
+
},
|
|
513
|
+
default: '',
|
|
514
|
+
description: 'ID of the webhook to delete',
|
|
515
|
+
},
|
|
516
|
+
],
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
async execute() {
|
|
520
|
+
const items = this.getInputData();
|
|
521
|
+
const returnData = [];
|
|
522
|
+
const credentials = await this.getCredentials('hubSealApi');
|
|
523
|
+
const resource = this.getNodeParameter('resource', 0);
|
|
524
|
+
const operation = this.getNodeParameter('operation', 0);
|
|
525
|
+
const baseUrl = (credentials.environment === 'production')
|
|
526
|
+
? 'https://api.hubwf.com/v1'
|
|
527
|
+
: 'https://api-staging.hubwf.com/v1';
|
|
528
|
+
const headers = {
|
|
529
|
+
'Authorization': `Bearer ${credentials.apiKey}`,
|
|
530
|
+
'Content-Type': 'application/json',
|
|
531
|
+
'X-Tenant-Id': credentials.tenantId,
|
|
532
|
+
};
|
|
533
|
+
for (let i = 0; i < items.length; i++) {
|
|
534
|
+
try {
|
|
535
|
+
let responseData;
|
|
536
|
+
// ============================================================
|
|
537
|
+
// DOCUMENT OPERATIONS
|
|
538
|
+
// ============================================================
|
|
539
|
+
if (resource === 'document') {
|
|
540
|
+
// ----- CREATE -----
|
|
541
|
+
if (operation === 'create') {
|
|
542
|
+
const documentName = this.getNodeParameter('documentName', i);
|
|
543
|
+
const documentUrl = this.getNodeParameter('documentUrl', i);
|
|
544
|
+
const recipientsData = this.getNodeParameter('recipients', i);
|
|
545
|
+
const additionalFields = this.getNodeParameter('additionalFields', i);
|
|
546
|
+
const recipients = (recipientsData.recipientValues || []).map((r) => ({
|
|
547
|
+
name: r.name,
|
|
548
|
+
email: r.email,
|
|
549
|
+
role: r.role || 'Signer',
|
|
550
|
+
}));
|
|
551
|
+
const body = {
|
|
552
|
+
name: documentName,
|
|
553
|
+
document_url: documentUrl,
|
|
554
|
+
recipients,
|
|
555
|
+
message: additionalFields.message || null,
|
|
556
|
+
expires_in_days: additionalFields.expiresInDays || 7,
|
|
557
|
+
signing_order: additionalFields.signingOrder || 'parallel',
|
|
558
|
+
};
|
|
559
|
+
const response = await this.helpers.request({
|
|
560
|
+
method: 'POST',
|
|
561
|
+
url: `${baseUrl}/documents`,
|
|
562
|
+
headers,
|
|
563
|
+
body,
|
|
564
|
+
json: true,
|
|
565
|
+
});
|
|
566
|
+
responseData = response;
|
|
567
|
+
}
|
|
568
|
+
// ----- CREATE FROM TEMPLATE -----
|
|
569
|
+
else if (operation === 'createFromTemplate') {
|
|
570
|
+
const templateId = this.getNodeParameter('templateId', i);
|
|
571
|
+
const recipientsData = this.getNodeParameter('recipients', i);
|
|
572
|
+
const fieldValuesData = this.getNodeParameter('fieldValues', i);
|
|
573
|
+
const recipients = (recipientsData.recipientValues || []).map((r) => ({
|
|
574
|
+
name: r.name,
|
|
575
|
+
email: r.email,
|
|
576
|
+
role: r.role,
|
|
577
|
+
}));
|
|
578
|
+
const fields = {};
|
|
579
|
+
(fieldValuesData.fieldValueItems || []).forEach((fv) => {
|
|
580
|
+
fields[fv.name] = fv.value;
|
|
581
|
+
});
|
|
582
|
+
const body = {
|
|
583
|
+
template_id: templateId,
|
|
584
|
+
recipients,
|
|
585
|
+
fields,
|
|
586
|
+
};
|
|
587
|
+
const response = await this.helpers.request({
|
|
588
|
+
method: 'POST',
|
|
589
|
+
url: `${baseUrl}/documents`,
|
|
590
|
+
headers,
|
|
591
|
+
body,
|
|
592
|
+
json: true,
|
|
593
|
+
});
|
|
594
|
+
responseData = response;
|
|
595
|
+
}
|
|
596
|
+
// ----- GET -----
|
|
597
|
+
else if (operation === 'get') {
|
|
598
|
+
const documentId = this.getNodeParameter('documentId', i);
|
|
599
|
+
const response = await this.helpers.request({
|
|
600
|
+
method: 'GET',
|
|
601
|
+
url: `${baseUrl}/documents/${documentId}`,
|
|
602
|
+
headers,
|
|
603
|
+
json: true,
|
|
604
|
+
});
|
|
605
|
+
responseData = response;
|
|
606
|
+
}
|
|
607
|
+
// ----- LIST -----
|
|
608
|
+
else if (operation === 'list') {
|
|
609
|
+
const filters = this.getNodeParameter('filters', i);
|
|
610
|
+
const queryParams = new URLSearchParams();
|
|
611
|
+
if (filters.status)
|
|
612
|
+
queryParams.append('status', filters.status);
|
|
613
|
+
if (filters.limit)
|
|
614
|
+
queryParams.append('limit', String(filters.limit));
|
|
615
|
+
const url = `${baseUrl}/documents${queryParams.toString() ? '?' + queryParams.toString() : ''}`;
|
|
616
|
+
const response = await this.helpers.request({
|
|
617
|
+
method: 'GET',
|
|
618
|
+
url,
|
|
619
|
+
headers,
|
|
620
|
+
json: true,
|
|
621
|
+
});
|
|
622
|
+
responseData = response;
|
|
623
|
+
}
|
|
624
|
+
// ----- DOWNLOAD -----
|
|
625
|
+
else if (operation === 'download') {
|
|
626
|
+
const documentId = this.getNodeParameter('documentId', i);
|
|
627
|
+
const response = await this.helpers.request({
|
|
628
|
+
method: 'GET',
|
|
629
|
+
url: `${baseUrl}/documents/${documentId}/download`,
|
|
630
|
+
headers,
|
|
631
|
+
json: true,
|
|
632
|
+
});
|
|
633
|
+
responseData = response;
|
|
634
|
+
}
|
|
635
|
+
// ----- REMIND -----
|
|
636
|
+
else if (operation === 'remind') {
|
|
637
|
+
const documentId = this.getNodeParameter('documentId', i);
|
|
638
|
+
const response = await this.helpers.request({
|
|
639
|
+
method: 'POST',
|
|
640
|
+
url: `${baseUrl}/documents/${documentId}/remind`,
|
|
641
|
+
headers,
|
|
642
|
+
json: true,
|
|
643
|
+
});
|
|
644
|
+
responseData = response;
|
|
645
|
+
}
|
|
646
|
+
// ----- VOID -----
|
|
647
|
+
else if (operation === 'void') {
|
|
648
|
+
const documentId = this.getNodeParameter('documentId', i);
|
|
649
|
+
const response = await this.helpers.request({
|
|
650
|
+
method: 'DELETE',
|
|
651
|
+
url: `${baseUrl}/documents/${documentId}`,
|
|
652
|
+
headers,
|
|
653
|
+
json: true,
|
|
654
|
+
});
|
|
655
|
+
responseData = response;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
// ============================================================
|
|
659
|
+
// TEMPLATE OPERATIONS
|
|
660
|
+
// ============================================================
|
|
661
|
+
else if (resource === 'template') {
|
|
662
|
+
// ----- GET -----
|
|
663
|
+
if (operation === 'get') {
|
|
664
|
+
const templateId = this.getNodeParameter('templateIdGet', i);
|
|
665
|
+
const response = await this.helpers.request({
|
|
666
|
+
method: 'GET',
|
|
667
|
+
url: `${baseUrl}/templates/${templateId}`,
|
|
668
|
+
headers,
|
|
669
|
+
json: true,
|
|
670
|
+
});
|
|
671
|
+
responseData = response;
|
|
672
|
+
}
|
|
673
|
+
// ----- LIST -----
|
|
674
|
+
else if (operation === 'list') {
|
|
675
|
+
const response = await this.helpers.request({
|
|
676
|
+
method: 'GET',
|
|
677
|
+
url: `${baseUrl}/templates`,
|
|
678
|
+
headers,
|
|
679
|
+
json: true,
|
|
680
|
+
});
|
|
681
|
+
responseData = response;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
// ============================================================
|
|
685
|
+
// WEBHOOK OPERATIONS
|
|
686
|
+
// ============================================================
|
|
687
|
+
else if (resource === 'webhook') {
|
|
688
|
+
// ----- CREATE -----
|
|
689
|
+
if (operation === 'create') {
|
|
690
|
+
const webhookUrl = this.getNodeParameter('webhookUrl', i);
|
|
691
|
+
const events = this.getNodeParameter('events', i);
|
|
692
|
+
const body = {
|
|
693
|
+
url: webhookUrl,
|
|
694
|
+
subscribed_events: events,
|
|
695
|
+
};
|
|
696
|
+
const response = await this.helpers.request({
|
|
697
|
+
method: 'POST',
|
|
698
|
+
url: `${baseUrl}/webhooks`,
|
|
699
|
+
headers,
|
|
700
|
+
body,
|
|
701
|
+
json: true,
|
|
702
|
+
});
|
|
703
|
+
responseData = response;
|
|
704
|
+
}
|
|
705
|
+
// ----- DELETE -----
|
|
706
|
+
else if (operation === 'delete') {
|
|
707
|
+
const webhookId = this.getNodeParameter('webhookId', i);
|
|
708
|
+
const response = await this.helpers.request({
|
|
709
|
+
method: 'DELETE',
|
|
710
|
+
url: `${baseUrl}/webhooks/${webhookId}`,
|
|
711
|
+
headers,
|
|
712
|
+
json: true,
|
|
713
|
+
});
|
|
714
|
+
responseData = response;
|
|
715
|
+
}
|
|
716
|
+
// ----- LIST -----
|
|
717
|
+
else if (operation === 'list') {
|
|
718
|
+
const response = await this.helpers.request({
|
|
719
|
+
method: 'GET',
|
|
720
|
+
url: `${baseUrl}/webhooks`,
|
|
721
|
+
headers,
|
|
722
|
+
json: true,
|
|
723
|
+
});
|
|
724
|
+
responseData = response;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(responseData), { itemData: { item: i } });
|
|
728
|
+
returnData.push(...executionData);
|
|
729
|
+
}
|
|
730
|
+
catch (error) {
|
|
731
|
+
if (this.continueOnFail()) {
|
|
732
|
+
returnData.push({ json: { error: error.message }, pairedItem: { item: i } });
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
735
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex: i });
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
return [returnData];
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
exports.HubSeal = HubSeal;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ╔═══════════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
* ║ HUBSEAL TRIGGER NODE (n8n) ║
|
|
4
|
+
* ║ ║
|
|
5
|
+
* ║ Webhook trigger for HubSeal signature events ║
|
|
6
|
+
* ║ Automatically manages webhook subscriptions ║
|
|
7
|
+
* ║ ║
|
|
8
|
+
* ║ @version 1.0.0 ║
|
|
9
|
+
* ╚═══════════════════════════════════════════════════════════════════════════════╝
|
|
10
|
+
*/
|
|
11
|
+
import { IHookFunctions, IWebhookFunctions, INodeType, INodeTypeDescription, IWebhookResponseData } from 'n8n-workflow';
|
|
12
|
+
export declare class HubSealTrigger implements INodeType {
|
|
13
|
+
description: INodeTypeDescription;
|
|
14
|
+
webhookMethods: {
|
|
15
|
+
default: {
|
|
16
|
+
checkExists(this: IHookFunctions): Promise<boolean>;
|
|
17
|
+
create(this: IHookFunctions): Promise<boolean>;
|
|
18
|
+
delete(this: IHookFunctions): Promise<boolean>;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
webhook(this: IWebhookFunctions): Promise<IWebhookResponseData>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ╔═══════════════════════════════════════════════════════════════════════════════╗
|
|
4
|
+
* ║ HUBSEAL TRIGGER NODE (n8n) ║
|
|
5
|
+
* ║ ║
|
|
6
|
+
* ║ Webhook trigger for HubSeal signature events ║
|
|
7
|
+
* ║ Automatically manages webhook subscriptions ║
|
|
8
|
+
* ║ ║
|
|
9
|
+
* ║ @version 1.0.0 ║
|
|
10
|
+
* ╚═══════════════════════════════════════════════════════════════════════════════╝
|
|
11
|
+
*/
|
|
12
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
+
}
|
|
18
|
+
Object.defineProperty(o, k2, desc);
|
|
19
|
+
}) : (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
o[k2] = m[k];
|
|
22
|
+
}));
|
|
23
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
+
}) : function(o, v) {
|
|
26
|
+
o["default"] = v;
|
|
27
|
+
});
|
|
28
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.HubSealTrigger = void 0;
|
|
37
|
+
const crypto = __importStar(require("crypto"));
|
|
38
|
+
class HubSealTrigger {
|
|
39
|
+
constructor() {
|
|
40
|
+
this.description = {
|
|
41
|
+
displayName: 'HubSeal Trigger',
|
|
42
|
+
name: 'hubSealTrigger',
|
|
43
|
+
icon: 'file:hubseal.svg',
|
|
44
|
+
group: ['trigger'],
|
|
45
|
+
version: 1,
|
|
46
|
+
description: 'Trigger workflow on HubSeal signature events',
|
|
47
|
+
defaults: {
|
|
48
|
+
name: 'HubSeal Trigger',
|
|
49
|
+
},
|
|
50
|
+
inputs: [],
|
|
51
|
+
outputs: ['main'],
|
|
52
|
+
credentials: [
|
|
53
|
+
{
|
|
54
|
+
name: 'hubSealApi',
|
|
55
|
+
required: true,
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
webhooks: [
|
|
59
|
+
{
|
|
60
|
+
name: 'default',
|
|
61
|
+
httpMethod: 'POST',
|
|
62
|
+
responseMode: 'onReceived',
|
|
63
|
+
path: 'webhook',
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
properties: [
|
|
67
|
+
{
|
|
68
|
+
displayName: 'Events',
|
|
69
|
+
name: 'events',
|
|
70
|
+
type: 'multiOptions',
|
|
71
|
+
options: [
|
|
72
|
+
{
|
|
73
|
+
name: 'Document Created',
|
|
74
|
+
value: 'signature.request.created',
|
|
75
|
+
description: 'When a new signature request is created',
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'Document Sent',
|
|
79
|
+
value: 'signature.request.sent',
|
|
80
|
+
description: 'When signing invitations are sent',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'Document Viewed',
|
|
84
|
+
value: 'signature.request.viewed',
|
|
85
|
+
description: 'When a signer views the document',
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: 'Document Completed',
|
|
89
|
+
value: 'signature.request.completed',
|
|
90
|
+
description: 'When all signers have completed',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: 'Document Declined',
|
|
94
|
+
value: 'signature.request.declined',
|
|
95
|
+
description: 'When a signer declines to sign',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: 'Document Voided',
|
|
99
|
+
value: 'signature.request.voided',
|
|
100
|
+
description: 'When the document is voided',
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'Document Expired',
|
|
104
|
+
value: 'signature.request.expired',
|
|
105
|
+
description: 'When the document expires',
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: 'Signer Completed',
|
|
109
|
+
value: 'signature.signer.completed',
|
|
110
|
+
description: 'When an individual signer completes',
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: 'Signer Declined',
|
|
114
|
+
value: 'signature.signer.declined',
|
|
115
|
+
description: 'When an individual signer declines',
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
default: ['signature.request.completed'],
|
|
119
|
+
required: true,
|
|
120
|
+
description: 'Events to trigger on',
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
displayName: 'Verify Signature',
|
|
124
|
+
name: 'verifySignature',
|
|
125
|
+
type: 'boolean',
|
|
126
|
+
default: true,
|
|
127
|
+
description: 'Whether to verify the webhook HMAC signature',
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
};
|
|
131
|
+
this.webhookMethods = {
|
|
132
|
+
default: {
|
|
133
|
+
// ----- CHECK IF WEBHOOK EXISTS -----
|
|
134
|
+
async checkExists() {
|
|
135
|
+
const webhookData = this.getWorkflowStaticData('node');
|
|
136
|
+
if (webhookData.webhookId === undefined) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
const credentials = await this.getCredentials('hubSealApi');
|
|
140
|
+
const baseUrl = (credentials.environment === 'production')
|
|
141
|
+
? 'https://api.hubwf.com/v1'
|
|
142
|
+
: 'https://api-staging.hubwf.com/v1';
|
|
143
|
+
try {
|
|
144
|
+
await this.helpers.request({
|
|
145
|
+
method: 'GET',
|
|
146
|
+
url: `${baseUrl}/webhooks/${webhookData.webhookId}`,
|
|
147
|
+
headers: {
|
|
148
|
+
'Authorization': `Bearer ${credentials.apiKey}`,
|
|
149
|
+
'X-Tenant-Id': credentials.tenantId,
|
|
150
|
+
},
|
|
151
|
+
json: true,
|
|
152
|
+
});
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
// ----- CREATE WEBHOOK -----
|
|
160
|
+
async create() {
|
|
161
|
+
var _a, _b;
|
|
162
|
+
const webhookUrl = this.getNodeWebhookUrl('default');
|
|
163
|
+
const events = this.getNodeParameter('events');
|
|
164
|
+
const credentials = await this.getCredentials('hubSealApi');
|
|
165
|
+
const baseUrl = (credentials.environment === 'production')
|
|
166
|
+
? 'https://api.hubwf.com/v1'
|
|
167
|
+
: 'https://api-staging.hubwf.com/v1';
|
|
168
|
+
const body = {
|
|
169
|
+
url: webhookUrl,
|
|
170
|
+
subscribed_events: events,
|
|
171
|
+
description: `n8n Webhook - ${this.getNode().name}`,
|
|
172
|
+
};
|
|
173
|
+
try {
|
|
174
|
+
const response = await this.helpers.request({
|
|
175
|
+
method: 'POST',
|
|
176
|
+
url: `${baseUrl}/webhooks`,
|
|
177
|
+
headers: {
|
|
178
|
+
'Authorization': `Bearer ${credentials.apiKey}`,
|
|
179
|
+
'X-Tenant-Id': credentials.tenantId,
|
|
180
|
+
'Content-Type': 'application/json',
|
|
181
|
+
},
|
|
182
|
+
body,
|
|
183
|
+
json: true,
|
|
184
|
+
});
|
|
185
|
+
const webhookData = this.getWorkflowStaticData('node');
|
|
186
|
+
webhookData.webhookId = ((_a = response.webhook) === null || _a === void 0 ? void 0 : _a.id) || response.id;
|
|
187
|
+
webhookData.webhookSecret = ((_b = response.webhook) === null || _b === void 0 ? void 0 : _b.secret) || response.secret;
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
// ----- DELETE WEBHOOK -----
|
|
195
|
+
async delete() {
|
|
196
|
+
const webhookData = this.getWorkflowStaticData('node');
|
|
197
|
+
if (webhookData.webhookId === undefined) {
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
const credentials = await this.getCredentials('hubSealApi');
|
|
201
|
+
const baseUrl = (credentials.environment === 'production')
|
|
202
|
+
? 'https://api.hubwf.com/v1'
|
|
203
|
+
: 'https://api-staging.hubwf.com/v1';
|
|
204
|
+
try {
|
|
205
|
+
await this.helpers.request({
|
|
206
|
+
method: 'DELETE',
|
|
207
|
+
url: `${baseUrl}/webhooks/${webhookData.webhookId}`,
|
|
208
|
+
headers: {
|
|
209
|
+
'Authorization': `Bearer ${credentials.apiKey}`,
|
|
210
|
+
'X-Tenant-Id': credentials.tenantId,
|
|
211
|
+
},
|
|
212
|
+
json: true,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
// Ignore errors on delete
|
|
217
|
+
}
|
|
218
|
+
delete webhookData.webhookId;
|
|
219
|
+
delete webhookData.webhookSecret;
|
|
220
|
+
return true;
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
// ----- HANDLE INCOMING WEBHOOK -----
|
|
226
|
+
async webhook() {
|
|
227
|
+
const req = this.getRequestObject();
|
|
228
|
+
const body = this.getBodyData();
|
|
229
|
+
const verifySignature = this.getNodeParameter('verifySignature');
|
|
230
|
+
// Verify HMAC signature if enabled
|
|
231
|
+
if (verifySignature) {
|
|
232
|
+
const webhookData = this.getWorkflowStaticData('node');
|
|
233
|
+
const secret = webhookData.webhookSecret;
|
|
234
|
+
if (secret) {
|
|
235
|
+
const signature = req.headers['x-hubseal-signature'];
|
|
236
|
+
const timestamp = req.headers['x-hubseal-timestamp'];
|
|
237
|
+
if (signature && timestamp) {
|
|
238
|
+
const payload = `${timestamp}.${JSON.stringify(body)}`;
|
|
239
|
+
const expectedSignature = crypto
|
|
240
|
+
.createHmac('sha256', secret)
|
|
241
|
+
.update(payload)
|
|
242
|
+
.digest('hex');
|
|
243
|
+
if (!crypto.timingSafeEqual(Buffer.from(`sha256=${expectedSignature}`), Buffer.from(signature))) {
|
|
244
|
+
// Invalid signature - still return 200 to not give hints
|
|
245
|
+
return {
|
|
246
|
+
workflowData: [],
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
// Return the webhook payload
|
|
253
|
+
return {
|
|
254
|
+
workflowData: [
|
|
255
|
+
this.helpers.returnJsonArray(body),
|
|
256
|
+
],
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
exports.HubSealTrigger = HubSealTrigger;
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-nodes-hubseal",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "n8n community node for HubSeal e-signature workflows",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package",
|
|
7
|
+
"n8n",
|
|
8
|
+
"hubseal",
|
|
9
|
+
"hubwf",
|
|
10
|
+
"e-signature",
|
|
11
|
+
"electronic-signature",
|
|
12
|
+
"document-signing"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"homepage": "https://hubwf.com",
|
|
16
|
+
"author": {
|
|
17
|
+
"name": "SmartHubHQ",
|
|
18
|
+
"email": "support@hubwf.com"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/smarthubhq/n8n-nodes-hubseal.git"
|
|
23
|
+
},
|
|
24
|
+
"main": "index.js",
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsc",
|
|
27
|
+
"dev": "tsc --watch",
|
|
28
|
+
"format": "prettier nodes credentials --write",
|
|
29
|
+
"lint": "eslint nodes credentials package.json",
|
|
30
|
+
"lintfix": "eslint nodes credentials package.json --fix",
|
|
31
|
+
"prepublishOnly": "npm run build"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist"
|
|
35
|
+
],
|
|
36
|
+
"n8n": {
|
|
37
|
+
"n8nNodesApiVersion": 1,
|
|
38
|
+
"credentials": [
|
|
39
|
+
"dist/credentials/HubSealApi.credentials.js"
|
|
40
|
+
],
|
|
41
|
+
"nodes": [
|
|
42
|
+
"dist/nodes/HubSeal/HubSeal.node.js",
|
|
43
|
+
"dist/nodes/HubSeal/HubSealTrigger.node.js"
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/node": "^18.0.0",
|
|
48
|
+
"gulp": "^4.0.2",
|
|
49
|
+
"n8n-workflow": "*",
|
|
50
|
+
"prettier": "^2.8.0",
|
|
51
|
+
"typescript": "~5.2.0"
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"n8n-workflow": "*"
|
|
55
|
+
}
|
|
56
|
+
}
|