n8n-nodes-insign 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/LICENSE +21 -0
- package/README.md +430 -0
- package/dist/credentials/InsignApi.credentials.d.ts +12 -0
- package/dist/credentials/InsignApi.credentials.js +116 -0
- package/dist/credentials/InsignApi.credentials.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/nodes/Insign/GenericFunctions.d.ts +21 -0
- package/dist/nodes/Insign/GenericFunctions.js +163 -0
- package/dist/nodes/Insign/GenericFunctions.js.map +1 -0
- package/dist/nodes/Insign/Insign.node.d.ts +5 -0
- package/dist/nodes/Insign/Insign.node.js +615 -0
- package/dist/nodes/Insign/Insign.node.js.map +1 -0
- package/dist/nodes/Insign/insign.svg +8 -0
- package/dist/nodes/InsignTrigger/InsignTrigger.node.d.ts +5 -0
- package/dist/nodes/InsignTrigger/InsignTrigger.node.js +143 -0
- package/dist/nodes/InsignTrigger/InsignTrigger.node.js.map +1 -0
- package/dist/nodes/InsignTrigger/insign.svg +8 -0
- package/package.json +69 -0
|
@@ -0,0 +1,615 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Insign = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
const crypto_1 = require("crypto");
|
|
6
|
+
const GenericFunctions_1 = require("./GenericFunctions");
|
|
7
|
+
function sniffMime(buf) {
|
|
8
|
+
if (buf.length >= 4 && buf.slice(0, 4).toString() === '%PDF')
|
|
9
|
+
return 'application/pdf';
|
|
10
|
+
if (buf.length >= 2 && buf[0] === 0x50 && buf[1] === 0x4b)
|
|
11
|
+
return 'application/zip';
|
|
12
|
+
return 'application/octet-stream';
|
|
13
|
+
}
|
|
14
|
+
function buildSignedWebhookUrl(baseUrl, correlationId, secret) {
|
|
15
|
+
const url = new URL(baseUrl);
|
|
16
|
+
url.searchParams.set('cid', correlationId);
|
|
17
|
+
if (secret) {
|
|
18
|
+
const crypto = require('crypto');
|
|
19
|
+
const sig = crypto.createHmac('sha256', secret).update(correlationId).digest('hex');
|
|
20
|
+
url.searchParams.set('sig', sig);
|
|
21
|
+
}
|
|
22
|
+
return url.toString();
|
|
23
|
+
}
|
|
24
|
+
const ADDITIONAL_FIELDS = {
|
|
25
|
+
displayName: 'Additional Fields (JSON)',
|
|
26
|
+
name: 'additionalFields',
|
|
27
|
+
type: 'json',
|
|
28
|
+
default: '{}',
|
|
29
|
+
description: 'Free-form JSON merged into the request body. Use this for guiProperties, branding, or any inSign field not exposed above.',
|
|
30
|
+
};
|
|
31
|
+
class Insign {
|
|
32
|
+
constructor() {
|
|
33
|
+
this.description = {
|
|
34
|
+
displayName: 'inSign',
|
|
35
|
+
name: 'insign',
|
|
36
|
+
icon: 'file:insign.svg',
|
|
37
|
+
group: ['transform'],
|
|
38
|
+
version: 1,
|
|
39
|
+
subtitle: '={{$parameter["operation"]}}',
|
|
40
|
+
description: 'Interact with the inSign electronic signature API',
|
|
41
|
+
defaults: { name: 'inSign' },
|
|
42
|
+
inputs: ['main'],
|
|
43
|
+
outputs: ['main'],
|
|
44
|
+
credentials: [{ name: 'insignApi', required: true }],
|
|
45
|
+
properties: [
|
|
46
|
+
{
|
|
47
|
+
displayName: 'Operation',
|
|
48
|
+
name: 'operation',
|
|
49
|
+
type: 'options',
|
|
50
|
+
noDataExpression: true,
|
|
51
|
+
default: 'createSession',
|
|
52
|
+
options: [
|
|
53
|
+
{ name: 'Create Session', value: 'createSession', action: 'Create a session with a document', description: 'POST /configure/session with the document inlined as base64' },
|
|
54
|
+
{ name: 'Get Status', value: 'getStatus', action: 'Get full session status', description: 'POST /get/status' },
|
|
55
|
+
{ name: 'Check Status', value: 'checkStatus', action: 'Lightweight status check', description: 'POST /get/checkstatus' },
|
|
56
|
+
{ name: 'Send Reminder', value: 'sendReminder', action: 'Send a manual reminder to the signer', description: 'POST /load/sendManualReminder' },
|
|
57
|
+
{ name: 'Make Extern', value: 'makeExtern', action: 'Hand session to external signers', description: 'POST /extern/beginmulti' },
|
|
58
|
+
{ name: 'Abort Extern', value: 'abortExtern', action: 'Abort external signing', description: 'POST /extern/abort' },
|
|
59
|
+
{ name: 'Download Signed Documents', value: 'download', action: 'Download signed PDFs', description: 'POST /get/documents/download' },
|
|
60
|
+
{ name: 'Get Audit Trail', value: 'getAudit', action: 'Get the audit trail as JSON', description: 'POST /get/audit' },
|
|
61
|
+
{ name: 'Purge Session', value: 'purge', action: 'Delete a session permanently', description: 'POST /persistence/purge (irreversible)' },
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
// ── Create Session ─────────────────────────────────────────────
|
|
65
|
+
{
|
|
66
|
+
displayName: 'Display Name',
|
|
67
|
+
name: 'displayName',
|
|
68
|
+
type: 'string',
|
|
69
|
+
default: '',
|
|
70
|
+
required: true,
|
|
71
|
+
displayOptions: { show: { operation: ['createSession'] } },
|
|
72
|
+
description: 'Human-readable name shown in the inSign UI',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
displayName: 'Binary Property (Document)',
|
|
76
|
+
name: 'binaryPropertyName',
|
|
77
|
+
type: 'string',
|
|
78
|
+
default: 'data',
|
|
79
|
+
required: true,
|
|
80
|
+
displayOptions: { show: { operation: ['createSession'] } },
|
|
81
|
+
description: 'Name of the binary property on the input item containing the PDF. Sent inline as base64.',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
displayName: 'Document Display Name',
|
|
85
|
+
name: 'documentDisplayName',
|
|
86
|
+
type: 'string',
|
|
87
|
+
default: '',
|
|
88
|
+
displayOptions: { show: { operation: ['createSession'] } },
|
|
89
|
+
description: 'Optional display name for the document. Defaults to the binary file name.',
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
displayName: 'Scan Signature Tags',
|
|
93
|
+
name: 'scanSigTags',
|
|
94
|
+
type: 'boolean',
|
|
95
|
+
default: true,
|
|
96
|
+
displayOptions: { show: { operation: ['createSession'] } },
|
|
97
|
+
description: 'Whether to scan the PDF for signature tags',
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
displayName: 'Allow Form Editing',
|
|
101
|
+
name: 'allowFormEditing',
|
|
102
|
+
type: 'boolean',
|
|
103
|
+
default: true,
|
|
104
|
+
displayOptions: { show: { operation: ['createSession'] } },
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
displayName: 'Upload Enabled',
|
|
108
|
+
name: 'uploadEnabled',
|
|
109
|
+
type: 'boolean',
|
|
110
|
+
default: false,
|
|
111
|
+
displayOptions: { show: { operation: ['createSession'] } },
|
|
112
|
+
description: 'Whether the signer may upload extra documents',
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
displayName: 'For User',
|
|
116
|
+
name: 'forUser',
|
|
117
|
+
type: 'string',
|
|
118
|
+
default: '',
|
|
119
|
+
displayOptions: { show: { operation: ['createSession'] } },
|
|
120
|
+
description: 'User/session identifier inside your inSign account. Leave empty to auto-generate one.',
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
displayName: 'User Full Name',
|
|
124
|
+
name: 'userFullName',
|
|
125
|
+
type: 'string',
|
|
126
|
+
default: '',
|
|
127
|
+
displayOptions: { show: { operation: ['createSession'] } },
|
|
128
|
+
},
|
|
129
|
+
// ── Common session behavior toggles (prominent) ────────────────
|
|
130
|
+
{
|
|
131
|
+
displayName: 'Auto-Finish on Last Signature',
|
|
132
|
+
name: 'workflowFinishAfterLastSign',
|
|
133
|
+
type: 'boolean',
|
|
134
|
+
default: false,
|
|
135
|
+
displayOptions: { show: { operation: ['createSession'] } },
|
|
136
|
+
description: 'When on, the session completes automatically as soon as the last mandatory signature lands. Maps to workflowFinishAfterLastSign.',
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
displayName: 'Auto-Complete on External Finish',
|
|
140
|
+
name: 'externCompleteOnFinish',
|
|
141
|
+
type: 'boolean',
|
|
142
|
+
default: false,
|
|
143
|
+
displayOptions: { show: { operation: ['createSession'] } },
|
|
144
|
+
description: 'When on, the session is marked complete as soon as the external signer presses Done. Maps to externCompleteOnFinish.',
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
displayName: 'Email Signed Docs to External',
|
|
148
|
+
name: 'externSendDocsOnFinishCustomer',
|
|
149
|
+
type: 'boolean',
|
|
150
|
+
default: false,
|
|
151
|
+
displayOptions: { show: { operation: ['createSession'] } },
|
|
152
|
+
description: 'Whether the external signer receives the signed PDFs by email once the session is completed. Maps to externSendDocsOnFinishCustomer.',
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
displayName: 'Download Link in Emails',
|
|
156
|
+
name: 'documentEmailDownload',
|
|
157
|
+
type: 'boolean',
|
|
158
|
+
default: false,
|
|
159
|
+
displayOptions: { show: { operation: ['createSession'] } },
|
|
160
|
+
description: 'Include a signed-documents download link in the notification emails. Maps to documentEmailDownload.',
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
displayName: 'Skip Finish Modal (Owner)',
|
|
164
|
+
name: 'skipFinishModal',
|
|
165
|
+
type: 'boolean',
|
|
166
|
+
default: false,
|
|
167
|
+
displayOptions: { show: { operation: ['createSession'] } },
|
|
168
|
+
description: 'When on, clicking Done does not show the consultant confirmation dialog. Merged into guiProperties.guiFertigbuttonSkipModalDialog.',
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
displayName: 'Skip Finish Modal (External)',
|
|
172
|
+
name: 'skipFinishModalExtern',
|
|
173
|
+
type: 'boolean',
|
|
174
|
+
default: false,
|
|
175
|
+
displayOptions: { show: { operation: ['createSession'] } },
|
|
176
|
+
description: 'Same as above but for the external signer. Merged into guiProperties.guiFertigbuttonSkipModalDialogExtern.',
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
displayName: 'External User Guidance',
|
|
180
|
+
name: 'externUserGuidance',
|
|
181
|
+
type: 'boolean',
|
|
182
|
+
default: true,
|
|
183
|
+
displayOptions: { show: { operation: ['createSession'] } },
|
|
184
|
+
description: 'Step-by-step arrows and highlights for external signers. Maps to externUserGuidance.',
|
|
185
|
+
},
|
|
186
|
+
// ── GDPR / legal links ─────────────────────────────────────────
|
|
187
|
+
{
|
|
188
|
+
displayName: 'GDPR Consent Popup',
|
|
189
|
+
name: 'gdprPopupActive',
|
|
190
|
+
type: 'boolean',
|
|
191
|
+
default: false,
|
|
192
|
+
displayOptions: { show: { operation: ['createSession'] } },
|
|
193
|
+
description: 'Show the GDPR / DSGVO consent dialog before signing. Maps to gdprPopupActive.',
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
displayName: 'Privacy Policy URL',
|
|
197
|
+
name: 'privacyLink',
|
|
198
|
+
type: 'string',
|
|
199
|
+
default: '',
|
|
200
|
+
placeholder: 'https://example.com/privacy',
|
|
201
|
+
displayOptions: { show: { operation: ['createSession'] } },
|
|
202
|
+
description: 'Privacy link shown to the signer. Maps to privacyLink.',
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
displayName: 'Imprint URL',
|
|
206
|
+
name: 'imprintLink',
|
|
207
|
+
type: 'string',
|
|
208
|
+
default: '',
|
|
209
|
+
placeholder: 'https://example.com/imprint',
|
|
210
|
+
displayOptions: { show: { operation: ['createSession'] } },
|
|
211
|
+
description: 'Imprint link shown to the signer. Maps to imprintLink.',
|
|
212
|
+
},
|
|
213
|
+
// ── Branding ───────────────────────────────────────────────────
|
|
214
|
+
{
|
|
215
|
+
displayName: 'External CSS / Properties URL',
|
|
216
|
+
name: 'externalPropertiesURL',
|
|
217
|
+
type: 'string',
|
|
218
|
+
default: '',
|
|
219
|
+
placeholder: 'https://example.com/insign-branding.css',
|
|
220
|
+
displayOptions: { show: { operation: ['createSession'] } },
|
|
221
|
+
description: 'URL to an external CSS or properties file for custom branding. Maps to externalPropertiesURL.',
|
|
222
|
+
},
|
|
223
|
+
// ── Webhook / correlation ──────────────────────────────────────
|
|
224
|
+
{
|
|
225
|
+
displayName: 'Correlation ID',
|
|
226
|
+
name: 'correlationId',
|
|
227
|
+
type: 'string',
|
|
228
|
+
default: '',
|
|
229
|
+
displayOptions: { show: { operation: ['createSession'] } },
|
|
230
|
+
description: 'Your own identifier for this session, round-tripped through customInfo and echoed back in the webhook callback (cid query param). Leave empty to auto-generate. Use it to correlate n8n runs with inSign events.',
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
displayName: 'Browser Callback URL',
|
|
234
|
+
name: 'callbackURL',
|
|
235
|
+
type: 'string',
|
|
236
|
+
default: '',
|
|
237
|
+
placeholder: 'https://your-n8n.example.com/webhook/signed-thanks',
|
|
238
|
+
displayOptions: { show: { operation: ['createSession'] } },
|
|
239
|
+
description: 'URL the browser navigates to once the signer presses Done. Typically a "thank you" page. This is the inSign `callbackURL` field — runs in the signer\'s browser, separate from the server-side webhook below.',
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
displayName: 'Server-Side Webhook URL',
|
|
243
|
+
name: 'serverSidecallbackURL',
|
|
244
|
+
type: 'string',
|
|
245
|
+
default: '',
|
|
246
|
+
placeholder: 'https://your-n8n.example.com/webhook/insign',
|
|
247
|
+
displayOptions: { show: { operation: ['createSession'] } },
|
|
248
|
+
description: 'Paste the **Production URL** from your inSign Trigger node here. inSign will GET this URL on every session event. The node appends `?cid=<correlationId>&sig=<hmac>` (if a Webhook HMAC Secret is configured on the credential) so the inSign Trigger can authenticate the call.',
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
...ADDITIONAL_FIELDS,
|
|
252
|
+
displayOptions: { show: { operation: ['createSession'] } },
|
|
253
|
+
},
|
|
254
|
+
// ── sessionId-only operations ──────────────────────────────────
|
|
255
|
+
{
|
|
256
|
+
displayName: 'Session ID',
|
|
257
|
+
name: 'sessionId',
|
|
258
|
+
type: 'string',
|
|
259
|
+
default: '',
|
|
260
|
+
required: true,
|
|
261
|
+
displayOptions: {
|
|
262
|
+
show: {
|
|
263
|
+
operation: ['getStatus', 'checkStatus', 'sendReminder', 'makeExtern', 'abortExtern', 'download', 'getAudit', 'purge'],
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
// ── Send Reminder additional fields ────────────────────────────
|
|
268
|
+
{
|
|
269
|
+
...ADDITIONAL_FIELDS,
|
|
270
|
+
displayOptions: { show: { operation: ['sendReminder'] } },
|
|
271
|
+
},
|
|
272
|
+
// ── Make Extern ────────────────────────────────────────────────
|
|
273
|
+
{
|
|
274
|
+
displayName: 'Extern Users',
|
|
275
|
+
name: 'externUsers',
|
|
276
|
+
placeholder: 'Add Signer',
|
|
277
|
+
type: 'fixedCollection',
|
|
278
|
+
typeOptions: { multipleValues: true },
|
|
279
|
+
default: {},
|
|
280
|
+
required: true,
|
|
281
|
+
displayOptions: { show: { operation: ['makeExtern'] } },
|
|
282
|
+
description: 'At least one external signer must be supplied.',
|
|
283
|
+
options: [
|
|
284
|
+
{
|
|
285
|
+
name: 'user',
|
|
286
|
+
displayName: 'Signer',
|
|
287
|
+
values: [
|
|
288
|
+
{ displayName: 'Recipient (Email)', name: 'recipient', type: 'string', default: '', required: true, description: 'The signer\'s email address. Required by inSign.' },
|
|
289
|
+
{ displayName: 'Real Name', name: 'realName', type: 'string', default: '' },
|
|
290
|
+
{ displayName: 'Phone (SMS)', name: 'recipientsms', type: 'string', default: '', description: 'Phone number for SMS delivery. Required if Send SMS is on.' },
|
|
291
|
+
{ displayName: 'Role Type', name: 'roletype', type: 'options', default: 'signer', options: [
|
|
292
|
+
{ name: 'Signer', value: 'signer' },
|
|
293
|
+
{ name: 'Owner', value: 'owner' },
|
|
294
|
+
] },
|
|
295
|
+
{ displayName: 'Send Email', name: 'sendEmails', type: 'boolean', default: true, description: 'Whether inSign should email the invitation/access link to this signer. Maps to externUsers[].sendEmails.' },
|
|
296
|
+
{ displayName: 'Send SMS', name: 'sendSMS', type: 'boolean', default: false, description: 'Whether inSign should SMS the invitation/access link. Maps to externUsers[].sendSMS. Requires Phone (SMS) to be set.' },
|
|
297
|
+
{ displayName: 'Note', name: 'note', type: 'string', default: '', description: 'Personal note included in the invitation email.' },
|
|
298
|
+
],
|
|
299
|
+
},
|
|
300
|
+
],
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
...ADDITIONAL_FIELDS,
|
|
304
|
+
displayOptions: { show: { operation: ['makeExtern'] } },
|
|
305
|
+
},
|
|
306
|
+
// ── Download: output shape + binary property ───────────────────
|
|
307
|
+
{
|
|
308
|
+
displayName: 'Output Mode',
|
|
309
|
+
name: 'downloadOutputMode',
|
|
310
|
+
type: 'options',
|
|
311
|
+
default: 'auto',
|
|
312
|
+
displayOptions: { show: { operation: ['download'] } },
|
|
313
|
+
options: [
|
|
314
|
+
{
|
|
315
|
+
name: 'Auto (whatever inSign returned)',
|
|
316
|
+
value: 'auto',
|
|
317
|
+
description: 'Emit the raw response body — PDF if single doc, ZIP if multi-doc',
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
name: 'ZIP (always)',
|
|
321
|
+
value: 'zip',
|
|
322
|
+
description: 'Emit a ZIP archive. A single PDF is wrapped into a one-file ZIP.',
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
name: 'Single Files (unzipped)',
|
|
326
|
+
value: 'files',
|
|
327
|
+
description: 'One output item per document. A single PDF is emitted as-is; a ZIP is unpacked into one item per entry.',
|
|
328
|
+
},
|
|
329
|
+
],
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
displayName: 'Put Output In Binary Property',
|
|
333
|
+
name: 'outputBinaryProperty',
|
|
334
|
+
type: 'string',
|
|
335
|
+
default: 'data',
|
|
336
|
+
required: true,
|
|
337
|
+
displayOptions: { show: { operation: ['download'] } },
|
|
338
|
+
},
|
|
339
|
+
// ── Purge warning ──────────────────────────────────────────────
|
|
340
|
+
{
|
|
341
|
+
displayName: 'Purging a session is irreversible. All documents, audit data, and signatures will be permanently deleted.',
|
|
342
|
+
name: 'purgeNotice',
|
|
343
|
+
type: 'notice',
|
|
344
|
+
default: '',
|
|
345
|
+
displayOptions: { show: { operation: ['purge'] } },
|
|
346
|
+
},
|
|
347
|
+
// ── Global options (shown for every operation) ─────────────────
|
|
348
|
+
{
|
|
349
|
+
displayName: 'Options',
|
|
350
|
+
name: 'requestOptions',
|
|
351
|
+
type: 'collection',
|
|
352
|
+
placeholder: 'Add option',
|
|
353
|
+
default: {},
|
|
354
|
+
options: [
|
|
355
|
+
{
|
|
356
|
+
displayName: 'Request Timeout (Ms)',
|
|
357
|
+
name: 'timeout',
|
|
358
|
+
type: 'number',
|
|
359
|
+
typeOptions: { minValue: 1000, maxValue: 300000 },
|
|
360
|
+
default: 30000,
|
|
361
|
+
description: 'How long to wait for the inSign API before giving up',
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
displayName: 'Trace ID',
|
|
365
|
+
name: 'traceId',
|
|
366
|
+
type: 'string',
|
|
367
|
+
default: '',
|
|
368
|
+
description: 'Custom X-Request-Id header sent to inSign. Leave empty to auto-generate. Useful for correlating n8n runs with inSign server logs.',
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
displayName: 'Include Request Metadata',
|
|
372
|
+
name: 'includeMeta',
|
|
373
|
+
type: 'boolean',
|
|
374
|
+
default: false,
|
|
375
|
+
description: 'Whether to add a `_meta` object to each output item containing the traceId, duration, method, and path. Handy for debugging.',
|
|
376
|
+
},
|
|
377
|
+
],
|
|
378
|
+
},
|
|
379
|
+
],
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
async execute() {
|
|
383
|
+
const items = this.getInputData();
|
|
384
|
+
const out = [];
|
|
385
|
+
for (let i = 0; i < items.length; i++) {
|
|
386
|
+
const operation = this.getNodeParameter('operation', i);
|
|
387
|
+
const reqOpts = this.getNodeParameter('requestOptions', i, {});
|
|
388
|
+
const extras = { timeout: reqOpts.timeout, traceId: reqOpts.traceId };
|
|
389
|
+
const withMeta = (json, meta) => (reqOpts.includeMeta ? { ...json, _meta: meta } : json);
|
|
390
|
+
try {
|
|
391
|
+
if (operation === 'createSession') {
|
|
392
|
+
const binaryProp = this.getNodeParameter('binaryPropertyName', i);
|
|
393
|
+
const binary = this.helpers.assertBinaryData(i, binaryProp);
|
|
394
|
+
const buffer = await this.helpers.getBinaryDataBuffer(i, binaryProp);
|
|
395
|
+
const base64 = buffer.toString('base64');
|
|
396
|
+
const autoId = () => `n8n-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
397
|
+
const document = {
|
|
398
|
+
id: autoId(),
|
|
399
|
+
displayname: this.getNodeParameter('documentDisplayName', i) || binary.fileName || 'document.pdf',
|
|
400
|
+
file: base64,
|
|
401
|
+
scanSigTags: this.getNodeParameter('scanSigTags', i),
|
|
402
|
+
allowFormEditing: this.getNodeParameter('allowFormEditing', i),
|
|
403
|
+
};
|
|
404
|
+
const body = {
|
|
405
|
+
displayname: this.getNodeParameter('displayName', i),
|
|
406
|
+
// foruser is required by inSign — always populate it.
|
|
407
|
+
// Use crypto.randomUUID() when the user leaves the field empty so
|
|
408
|
+
// every session has a stable, well-formed identifier.
|
|
409
|
+
foruser: (this.getNodeParameter('forUser', i) || '').trim() || (0, crypto_1.randomUUID)(),
|
|
410
|
+
documents: [document],
|
|
411
|
+
uploadEnabled: this.getNodeParameter('uploadEnabled', i),
|
|
412
|
+
};
|
|
413
|
+
const userFullName = this.getNodeParameter('userFullName', i, '');
|
|
414
|
+
if (userFullName)
|
|
415
|
+
body.userFullName = userFullName;
|
|
416
|
+
// Prominent behavior toggles → root fields
|
|
417
|
+
const rootBooleans = [
|
|
418
|
+
['workflowFinishAfterLastSign', 'workflowFinishAfterLastSign'],
|
|
419
|
+
['externCompleteOnFinish', 'externCompleteOnFinish'],
|
|
420
|
+
['externSendDocsOnFinishCustomer', 'externSendDocsOnFinishCustomer'],
|
|
421
|
+
['documentEmailDownload', 'documentEmailDownload'],
|
|
422
|
+
['externUserGuidance', 'externUserGuidance'],
|
|
423
|
+
['gdprPopupActive', 'gdprPopupActive'],
|
|
424
|
+
];
|
|
425
|
+
for (const [paramName, apiName] of rootBooleans) {
|
|
426
|
+
const val = this.getNodeParameter(paramName, i, undefined);
|
|
427
|
+
if (val !== undefined)
|
|
428
|
+
body[apiName] = val;
|
|
429
|
+
}
|
|
430
|
+
const rootStrings = [
|
|
431
|
+
['privacyLink', 'privacyLink'],
|
|
432
|
+
['imprintLink', 'imprintLink'],
|
|
433
|
+
['externalPropertiesURL', 'externalPropertiesURL'],
|
|
434
|
+
['callbackURL', 'callbackURL'],
|
|
435
|
+
];
|
|
436
|
+
for (const [paramName, apiName] of rootStrings) {
|
|
437
|
+
const val = this.getNodeParameter(paramName, i, '') || '';
|
|
438
|
+
if (val)
|
|
439
|
+
body[apiName] = val;
|
|
440
|
+
}
|
|
441
|
+
// Correlation ID + signed webhook URL
|
|
442
|
+
const correlationId = (this.getNodeParameter('correlationId', i, '') || '').trim() || autoId();
|
|
443
|
+
body.customInfo = correlationId;
|
|
444
|
+
const webhookUrl = (this.getNodeParameter('serverSidecallbackURL', i, '') || '').trim();
|
|
445
|
+
if (webhookUrl) {
|
|
446
|
+
const creds = await this.getCredentials('insignApi');
|
|
447
|
+
const secret = creds.webhookSecret || '';
|
|
448
|
+
const signed = buildSignedWebhookUrl(webhookUrl, correlationId, secret);
|
|
449
|
+
body.serverSidecallbackURL = signed;
|
|
450
|
+
}
|
|
451
|
+
// Skip-modal toggles → guiProperties.*
|
|
452
|
+
const guiProps = {};
|
|
453
|
+
if (this.getNodeParameter('skipFinishModal', i, false)) {
|
|
454
|
+
guiProps.guiFertigbuttonSkipModalDialog = true;
|
|
455
|
+
}
|
|
456
|
+
if (this.getNodeParameter('skipFinishModalExtern', i, false)) {
|
|
457
|
+
guiProps.guiFertigbuttonSkipModalDialogExtern = true;
|
|
458
|
+
}
|
|
459
|
+
if (Object.keys(guiProps).length > 0)
|
|
460
|
+
body.guiProperties = guiProps;
|
|
461
|
+
const extra = this.getNodeParameter('additionalFields', i);
|
|
462
|
+
const merged = (0, GenericFunctions_1.mergeAdditionalFields)(body, extra);
|
|
463
|
+
const { data, meta } = await GenericFunctions_1.insignRequest.call(this, 'POST', '/configure/session', merged, 'json', extras);
|
|
464
|
+
out.push({ json: withMeta(data, meta), pairedItem: { item: i } });
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
const sessionId = this.getNodeParameter('sessionId', i);
|
|
468
|
+
if (operation === 'getStatus') {
|
|
469
|
+
const { data, meta } = await GenericFunctions_1.insignRequest.call(this, 'POST', '/get/status', { sessionid: sessionId }, 'json', extras);
|
|
470
|
+
out.push({ json: withMeta(data, meta), pairedItem: { item: i } });
|
|
471
|
+
}
|
|
472
|
+
else if (operation === 'checkStatus') {
|
|
473
|
+
const { data, meta } = await GenericFunctions_1.insignRequest.call(this, 'POST', '/get/checkstatus', { sessionid: sessionId }, 'json', extras);
|
|
474
|
+
out.push({ json: withMeta(data, meta), pairedItem: { item: i } });
|
|
475
|
+
}
|
|
476
|
+
else if (operation === 'sendReminder') {
|
|
477
|
+
const extra = this.getNodeParameter('additionalFields', i);
|
|
478
|
+
const body = (0, GenericFunctions_1.mergeAdditionalFields)({ sessionid: sessionId }, extra);
|
|
479
|
+
const { data, meta } = await GenericFunctions_1.insignRequest.call(this, 'POST', '/load/sendManualReminder', body, 'json', extras);
|
|
480
|
+
out.push({ json: withMeta(data, meta), pairedItem: { item: i } });
|
|
481
|
+
}
|
|
482
|
+
else if (operation === 'makeExtern') {
|
|
483
|
+
const externUsers = this.getNodeParameter('externUsers', i).user ?? [];
|
|
484
|
+
const extra = this.getNodeParameter('additionalFields', i);
|
|
485
|
+
const body = (0, GenericFunctions_1.mergeAdditionalFields)({ sessionid: sessionId, externUsers }, extra);
|
|
486
|
+
const { data, meta } = await GenericFunctions_1.insignRequest.call(this, 'POST', '/extern/beginmulti', body, 'json', extras);
|
|
487
|
+
out.push({ json: withMeta(data, meta), pairedItem: { item: i } });
|
|
488
|
+
}
|
|
489
|
+
else if (operation === 'abortExtern') {
|
|
490
|
+
const { data, meta } = await GenericFunctions_1.insignRequest.call(this, 'POST', '/extern/abort', { sessionid: sessionId }, 'json', extras);
|
|
491
|
+
out.push({ json: withMeta(data, meta), pairedItem: { item: i } });
|
|
492
|
+
}
|
|
493
|
+
else if (operation === 'download') {
|
|
494
|
+
const outProp = this.getNodeParameter('outputBinaryProperty', i);
|
|
495
|
+
const mode = this.getNodeParameter('downloadOutputMode', i);
|
|
496
|
+
const { data: res, meta } = await GenericFunctions_1.insignRequest.call(this, 'POST', '/get/documents/download', { sessionid: sessionId }, 'arraybuffer', extras);
|
|
497
|
+
const body = Buffer.from(res.body);
|
|
498
|
+
const headers = (res.headers ?? {});
|
|
499
|
+
const contentType = (headers['content-type'] || sniffMime(body)).split(';')[0].trim();
|
|
500
|
+
const isZip = contentType === 'application/zip' || (body[0] === 0x50 && body[1] === 0x4b);
|
|
501
|
+
if (mode === 'auto') {
|
|
502
|
+
const ext = isZip ? 'zip' : 'pdf';
|
|
503
|
+
const mime = isZip ? 'application/zip' : 'application/pdf';
|
|
504
|
+
const binary = await this.helpers.prepareBinaryData(body, `${sessionId}.${ext}`, mime);
|
|
505
|
+
out.push({
|
|
506
|
+
json: withMeta({ sessionid: sessionId, size: body.length, contentType: mime }, meta),
|
|
507
|
+
binary: { [outProp]: binary },
|
|
508
|
+
pairedItem: { item: i },
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
else if (mode === 'zip') {
|
|
512
|
+
let zipBuffer;
|
|
513
|
+
if (isZip) {
|
|
514
|
+
zipBuffer = body;
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
const JSZip = require('jszip');
|
|
518
|
+
const zip = new JSZip();
|
|
519
|
+
zip.file(`${sessionId}.pdf`, body);
|
|
520
|
+
zipBuffer = await zip.generateAsync({ type: 'nodebuffer' });
|
|
521
|
+
}
|
|
522
|
+
const binary = await this.helpers.prepareBinaryData(zipBuffer, `${sessionId}.zip`, 'application/zip');
|
|
523
|
+
out.push({
|
|
524
|
+
json: withMeta({ sessionid: sessionId, size: zipBuffer.length, contentType: 'application/zip' }, meta),
|
|
525
|
+
binary: { [outProp]: binary },
|
|
526
|
+
pairedItem: { item: i },
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
else if (mode === 'files') {
|
|
530
|
+
if (!isZip) {
|
|
531
|
+
const binary = await this.helpers.prepareBinaryData(body, `${sessionId}.pdf`, 'application/pdf');
|
|
532
|
+
out.push({
|
|
533
|
+
json: withMeta({ sessionid: sessionId, fileName: `${sessionId}.pdf`, size: body.length, contentType: 'application/pdf' }, meta),
|
|
534
|
+
binary: { [outProp]: binary },
|
|
535
|
+
pairedItem: { item: i },
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
const JSZip = require('jszip');
|
|
540
|
+
const zip = await JSZip.loadAsync(body);
|
|
541
|
+
const entries = Object.values(zip.files).filter((e) => !e.dir);
|
|
542
|
+
if (entries.length === 0) {
|
|
543
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Downloaded ZIP for session ${sessionId} contained no files`);
|
|
544
|
+
}
|
|
545
|
+
for (const entry of entries) {
|
|
546
|
+
const fileBuf = await entry.async('nodebuffer');
|
|
547
|
+
const mime = entry.name.toLowerCase().endsWith('.pdf') ? 'application/pdf' : 'application/octet-stream';
|
|
548
|
+
const binary = await this.helpers.prepareBinaryData(fileBuf, entry.name, mime);
|
|
549
|
+
out.push({
|
|
550
|
+
json: withMeta({ sessionid: sessionId, fileName: entry.name, size: fileBuf.length, contentType: mime }, meta),
|
|
551
|
+
binary: { [outProp]: binary },
|
|
552
|
+
pairedItem: { item: i },
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
else if (operation === 'getAudit') {
|
|
559
|
+
const { data, meta } = await GenericFunctions_1.insignRequest.call(this, 'POST', '/get/audit', { sessionid: sessionId }, 'json', extras);
|
|
560
|
+
// Audit is an array of events — wrap it in an object so it fits n8n's json shape.
|
|
561
|
+
const payload = Array.isArray(data) ? { sessionid: sessionId, events: data } : data;
|
|
562
|
+
out.push({ json: withMeta(payload, meta), pairedItem: { item: i } });
|
|
563
|
+
}
|
|
564
|
+
else if (operation === 'purge') {
|
|
565
|
+
const { data, meta } = await GenericFunctions_1.insignRequest.call(this, 'POST', '/persistence/purge', { sessionid: sessionId }, 'json', extras);
|
|
566
|
+
out.push({ json: withMeta(data, meta), pairedItem: { item: i } });
|
|
567
|
+
}
|
|
568
|
+
else {
|
|
569
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown operation: ${operation}`);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
catch (error) {
|
|
573
|
+
if (this.continueOnFail()) {
|
|
574
|
+
// Build a structured failure record so downstream IF / Set / Switch
|
|
575
|
+
// nodes can branch on error code / message / response body cleanly.
|
|
576
|
+
const errAny = error;
|
|
577
|
+
const errResp = errAny?.errorResponse ?? errAny?.cause?.errorResponse ?? null;
|
|
578
|
+
const httpStatus = errAny?.httpCode ?? errAny?.status ?? errAny?.cause?.response?.status ?? null;
|
|
579
|
+
// Surface inSign's own {error, message} from HTTP-200 logical errors,
|
|
580
|
+
// OR the full HTTP response body for transport-level failures.
|
|
581
|
+
let insignError = null;
|
|
582
|
+
let insignMessage = null;
|
|
583
|
+
let responseBody = null;
|
|
584
|
+
if (errResp && typeof errResp === 'object') {
|
|
585
|
+
insignError = errResp.error ?? null;
|
|
586
|
+
insignMessage = errResp.message ?? null;
|
|
587
|
+
responseBody = errResp;
|
|
588
|
+
}
|
|
589
|
+
else {
|
|
590
|
+
responseBody =
|
|
591
|
+
errAny?.response?.body ??
|
|
592
|
+
errAny?.response?.data ??
|
|
593
|
+
errAny?.cause?.response?.body ??
|
|
594
|
+
null;
|
|
595
|
+
}
|
|
596
|
+
out.push({
|
|
597
|
+
json: {
|
|
598
|
+
error: insignError,
|
|
599
|
+
message: insignMessage ?? error.message,
|
|
600
|
+
httpStatus,
|
|
601
|
+
responseBody: responseBody,
|
|
602
|
+
trace: errAny?.context?.traceId ?? null,
|
|
603
|
+
},
|
|
604
|
+
pairedItem: { item: i },
|
|
605
|
+
});
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
throw error;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return [out];
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
exports.Insign = Insign;
|
|
615
|
+
//# sourceMappingURL=Insign.node.js.map
|