n8n-nodes-make-pdf 1.0.9 → 1.2.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.
@@ -10,7 +10,8 @@ class MakePdf {
10
10
  icon: 'file:MakePdf.svg',
11
11
  group: ['transform'],
12
12
  version: 1,
13
- description: 'Generate PDF via Gotenberg (HTML → PDF)',
13
+ subtitle: '={{$parameter["method"] + ": " + $parameter["url"]}}',
14
+ description: 'Make HTTP requests to Gotenberg for PDF generation',
14
15
  defaults: {
15
16
  name: 'Make PDF',
16
17
  },
@@ -18,79 +19,554 @@ class MakePdf {
18
19
  outputs: ['main'],
19
20
  properties: [
20
21
  {
21
- displayName: 'Input Binary Property',
22
- name: 'binaryPropertyName',
22
+ displayName: 'Method',
23
+ name: 'method',
24
+ type: 'options',
25
+ options: [
26
+ { name: 'DELETE', value: 'DELETE' },
27
+ { name: 'GET', value: 'GET' },
28
+ { name: 'HEAD', value: 'HEAD' },
29
+ { name: 'PATCH', value: 'PATCH' },
30
+ { name: 'POST', value: 'POST' },
31
+ { name: 'PUT', value: 'PUT' },
32
+ ],
33
+ default: 'POST',
34
+ description: 'The request method to use',
35
+ },
36
+ {
37
+ displayName: 'URL',
38
+ name: 'url',
23
39
  type: 'string',
24
- default: 'data',
40
+ default: 'http://gotenberg:3000/forms/chromium/convert/html',
41
+ placeholder: 'http://gotenberg:3000/forms/chromium/convert/html',
42
+ description: 'The URL to make the request to',
25
43
  required: true,
26
- description: 'Name of the binary property which contains the HTML data',
27
- placeholder: 'e.g. data',
28
44
  },
29
45
  {
30
- displayName: 'Output Binary Property',
31
- name: 'outputBinaryPropertyName',
46
+ displayName: 'Authentication',
47
+ name: 'authentication',
48
+ type: 'options',
49
+ options: [
50
+ { name: 'None', value: 'none' },
51
+ { name: 'Header Auth', value: 'headerAuth' },
52
+ { name: 'Query Auth', value: 'queryAuth' },
53
+ ],
54
+ default: 'none',
55
+ description: 'Authentication method to use',
56
+ },
57
+ {
58
+ displayName: 'Send Query Parameters',
59
+ name: 'sendQuery',
60
+ type: 'boolean',
61
+ default: false,
62
+ description: 'Whether the request has query params or not',
63
+ },
64
+ {
65
+ displayName: 'Query Parameters',
66
+ name: 'queryParameters',
67
+ type: 'fixedCollection',
68
+ typeOptions: {
69
+ multipleValues: true,
70
+ },
71
+ placeholder: 'Add Parameter',
72
+ default: { parameters: [] },
73
+ displayOptions: {
74
+ show: {
75
+ sendQuery: [true],
76
+ },
77
+ },
78
+ options: [
79
+ {
80
+ name: 'parameters',
81
+ displayName: 'Parameter',
82
+ values: [
83
+ {
84
+ displayName: 'Name',
85
+ name: 'name',
86
+ type: 'string',
87
+ default: '',
88
+ description: 'Name of the parameter',
89
+ },
90
+ {
91
+ displayName: 'Value',
92
+ name: 'value',
93
+ type: 'string',
94
+ default: '',
95
+ description: 'Value of the parameter',
96
+ },
97
+ ],
98
+ },
99
+ ],
100
+ description: 'The query parameters to send',
101
+ },
102
+ {
103
+ displayName: 'Send Headers',
104
+ name: 'sendHeaders',
105
+ type: 'boolean',
106
+ default: false,
107
+ description: 'Whether the request has headers or not',
108
+ },
109
+ {
110
+ displayName: 'Header Parameters',
111
+ name: 'headerParameters',
112
+ type: 'fixedCollection',
113
+ typeOptions: {
114
+ multipleValues: true,
115
+ },
116
+ placeholder: 'Add Header',
117
+ default: { parameters: [] },
118
+ displayOptions: {
119
+ show: {
120
+ sendHeaders: [true],
121
+ },
122
+ },
123
+ options: [
124
+ {
125
+ name: 'parameters',
126
+ displayName: 'Header',
127
+ values: [
128
+ {
129
+ displayName: 'Name',
130
+ name: 'name',
131
+ type: 'string',
132
+ default: '',
133
+ description: 'Name of the header',
134
+ },
135
+ {
136
+ displayName: 'Value',
137
+ name: 'value',
138
+ type: 'string',
139
+ default: '',
140
+ description: 'Value of the header',
141
+ },
142
+ ],
143
+ },
144
+ ],
145
+ description: 'The headers to send',
146
+ },
147
+ {
148
+ displayName: 'Send Body',
149
+ name: 'sendBody',
150
+ type: 'boolean',
151
+ default: true,
152
+ description: 'Whether the request has a body or not',
153
+ },
154
+ {
155
+ displayName: 'Body Content Type',
156
+ name: 'contentType',
157
+ type: 'options',
158
+ displayOptions: {
159
+ show: {
160
+ sendBody: [true],
161
+ },
162
+ },
163
+ options: [
164
+ {
165
+ name: 'Form-Data Multipart',
166
+ value: 'multipart-form-data',
167
+ },
168
+ {
169
+ name: 'Form Urlencoded',
170
+ value: 'form-urlencoded',
171
+ },
172
+ {
173
+ name: 'JSON',
174
+ value: 'json',
175
+ },
176
+ {
177
+ name: 'RAW/Custom',
178
+ value: 'raw',
179
+ },
180
+ {
181
+ name: 'n8n Binary Data',
182
+ value: 'binaryData',
183
+ },
184
+ ],
185
+ default: 'multipart-form-data',
186
+ description: 'Content-Type to use for body',
187
+ },
188
+ {
189
+ displayName: 'Body Parameters',
190
+ name: 'bodyParameters',
191
+ type: 'fixedCollection',
192
+ typeOptions: {
193
+ multipleValues: true,
194
+ },
195
+ placeholder: 'Add Parameter',
196
+ default: { parameters: [] },
197
+ displayOptions: {
198
+ show: {
199
+ sendBody: [true],
200
+ contentType: ['multipart-form-data', 'form-urlencoded'],
201
+ },
202
+ },
203
+ options: [
204
+ {
205
+ name: 'parameters',
206
+ displayName: 'Parameter',
207
+ values: [
208
+ {
209
+ displayName: 'Parameter Type',
210
+ name: 'parameterType',
211
+ type: 'options',
212
+ options: [
213
+ {
214
+ name: 'n8n Binary Data',
215
+ value: 'formBinaryData',
216
+ },
217
+ {
218
+ name: 'Form Data',
219
+ value: 'formData',
220
+ },
221
+ ],
222
+ default: 'formData',
223
+ },
224
+ {
225
+ displayName: 'Name',
226
+ name: 'name',
227
+ type: 'string',
228
+ default: '',
229
+ description: 'Name of the parameter',
230
+ },
231
+ {
232
+ displayName: 'Input Data Field Name',
233
+ name: 'inputDataFieldName',
234
+ type: 'string',
235
+ displayOptions: {
236
+ show: {
237
+ parameterType: ['formBinaryData'],
238
+ },
239
+ },
240
+ default: '',
241
+ description: 'The name of the incoming field containing the binary file data',
242
+ },
243
+ {
244
+ displayName: 'Value',
245
+ name: 'value',
246
+ type: 'string',
247
+ displayOptions: {
248
+ show: {
249
+ parameterType: ['formData'],
250
+ },
251
+ },
252
+ default: '',
253
+ description: 'Value of the parameter',
254
+ },
255
+ ],
256
+ },
257
+ ],
258
+ description: 'Body parameters to send',
259
+ },
260
+ {
261
+ displayName: 'JSON/RAW Body',
262
+ name: 'jsonBody',
32
263
  type: 'string',
33
- default: 'data',
34
- required: true,
35
- description: 'Name of the binary property to store the PDF result',
36
- placeholder: 'e.g. data',
264
+ displayOptions: {
265
+ show: {
266
+ sendBody: [true],
267
+ contentType: ['json', 'raw'],
268
+ },
269
+ },
270
+ default: '',
271
+ typeOptions: {
272
+ alwaysOpenEditWindow: true,
273
+ },
274
+ description: 'JSON or RAW body content',
37
275
  },
38
276
  {
39
- displayName: 'Gotenberg URL',
40
- name: 'gotenbergUrl',
277
+ displayName: 'Input Binary Field',
278
+ name: 'inputBinaryField',
41
279
  type: 'string',
42
- default: 'http://gotenberg:3000/forms/chromium/convert/html',
280
+ displayOptions: {
281
+ show: {
282
+ sendBody: [true],
283
+ contentType: ['binaryData'],
284
+ },
285
+ },
286
+ default: 'data',
43
287
  required: true,
44
- description: 'The URL of your Gotenberg instance',
45
- placeholder: 'http://gotenberg:3000/forms/chromium/convert/html',
288
+ description: 'The name of the incoming field containing the binary file data',
289
+ },
290
+ {
291
+ displayName: 'Options',
292
+ name: 'options',
293
+ type: 'collection',
294
+ placeholder: 'Add Option',
295
+ default: {},
296
+ options: [
297
+ {
298
+ displayName: 'Batching',
299
+ name: 'batching',
300
+ placeholder: 'Add Batching',
301
+ type: 'fixedCollection',
302
+ typeOptions: {
303
+ multipleValues: false,
304
+ },
305
+ default: {},
306
+ options: [
307
+ {
308
+ displayName: 'Batch',
309
+ name: 'batch',
310
+ values: [
311
+ {
312
+ displayName: 'Items per Batch',
313
+ name: 'batchSize',
314
+ type: 'number',
315
+ typeOptions: {
316
+ minValue: 1,
317
+ },
318
+ default: 50,
319
+ description: 'Input will be split into multiple batches to throttle requests. -1 for disabled.',
320
+ },
321
+ {
322
+ displayName: 'Batch Interval',
323
+ name: 'batchInterval',
324
+ type: 'number',
325
+ typeOptions: {
326
+ minValue: 0,
327
+ },
328
+ default: 1000,
329
+ description: 'Time (in milliseconds) between each batch of requests. 0 for disabled.',
330
+ },
331
+ ],
332
+ },
333
+ ],
334
+ },
335
+ {
336
+ displayName: 'Ignore SSL Issues',
337
+ name: 'allowUnauthorizedCerts',
338
+ type: 'boolean',
339
+ default: false,
340
+ description: 'Whether to download the response even if SSL certificate validation is not possible',
341
+ },
342
+ {
343
+ displayName: 'Follow Redirect',
344
+ name: 'followRedirect',
345
+ type: 'boolean',
346
+ default: true,
347
+ description: 'Whether to follow redirects',
348
+ },
349
+ {
350
+ displayName: 'Redirect Count',
351
+ name: 'maxRedirects',
352
+ type: 'number',
353
+ typeOptions: {
354
+ minValue: 0,
355
+ },
356
+ displayOptions: {
357
+ show: {
358
+ followRedirect: [true],
359
+ },
360
+ },
361
+ default: 21,
362
+ description: 'Maximum number of redirects to follow',
363
+ },
364
+ {
365
+ displayName: 'Response Format',
366
+ name: 'responseFormat',
367
+ type: 'options',
368
+ options: [
369
+ {
370
+ name: 'Autodetect',
371
+ value: 'autodetect',
372
+ },
373
+ {
374
+ name: 'File',
375
+ value: 'file',
376
+ },
377
+ {
378
+ name: 'JSON',
379
+ value: 'json',
380
+ },
381
+ {
382
+ name: 'Text',
383
+ value: 'text',
384
+ },
385
+ ],
386
+ default: 'autodetect',
387
+ description: 'The format in which the data gets returned from the URL',
388
+ },
389
+ {
390
+ displayName: 'Output Binary Field Name',
391
+ name: 'outputBinaryField',
392
+ displayOptions: {
393
+ show: {
394
+ responseFormat: ['file'],
395
+ },
396
+ },
397
+ type: 'string',
398
+ default: 'data',
399
+ description: 'Name of the binary property to write the file to',
400
+ },
401
+ {
402
+ displayName: 'Timeout',
403
+ name: 'timeout',
404
+ type: 'number',
405
+ typeOptions: {
406
+ minValue: 1,
407
+ },
408
+ default: 10000,
409
+ description: 'Time in ms to wait for a response before giving up on the request',
410
+ },
411
+ {
412
+ displayName: 'Proxy',
413
+ name: 'proxy',
414
+ type: 'string',
415
+ default: '',
416
+ placeholder: 'http://myproxy:3128',
417
+ description: 'HTTP proxy to use',
418
+ },
419
+ ],
46
420
  },
47
421
  ],
48
422
  };
49
423
  }
50
424
  async execute() {
51
- var _a;
425
+ var _a, _b;
52
426
  const items = this.getInputData();
53
427
  const returnData = [];
54
428
  for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
55
429
  try {
56
- const binaryPropertyName = this.getNodeParameter('binaryPropertyName', itemIndex);
57
- const outputBinaryPropertyName = this.getNodeParameter('outputBinaryPropertyName', itemIndex);
58
- const gotenbergUrl = this.getNodeParameter('gotenbergUrl', itemIndex);
59
- const binaryData = (_a = items[itemIndex].binary) === null || _a === void 0 ? void 0 : _a[binaryPropertyName];
60
- if (!binaryData) {
61
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `No binary data found in property "${binaryPropertyName}"`, { itemIndex });
62
- }
63
- if (!binaryData.data) {
64
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Binary property "${binaryPropertyName}" has no data`, { itemIndex });
65
- }
66
- // Convert base64 to Buffer
67
- const htmlBuffer = Buffer.from(binaryData.data, 'base64');
68
- // Make request to Gotenberg
69
- const response = await this.helpers.httpRequest({
70
- method: 'POST',
71
- url: gotenbergUrl,
72
- body: htmlBuffer,
73
- headers: {
74
- 'Content-Type': 'text/html',
75
- },
76
- encoding: 'arraybuffer',
430
+ const method = this.getNodeParameter('method', itemIndex, 'POST');
431
+ const url = this.getNodeParameter('url', itemIndex, '');
432
+ const sendQuery = this.getNodeParameter('sendQuery', itemIndex, false);
433
+ const sendHeaders = this.getNodeParameter('sendHeaders', itemIndex, false);
434
+ const sendBody = this.getNodeParameter('sendBody', itemIndex, false);
435
+ const contentType = this.getNodeParameter('contentType', itemIndex, '');
436
+ const options = this.getNodeParameter('options', itemIndex, {});
437
+ // Build request options
438
+ const requestOptions = {
439
+ method,
440
+ url,
441
+ headers: {},
77
442
  returnFullResponse: false,
78
- });
79
- // Create new binary data for PDF
80
- const newBinaryData = {
81
- data: Buffer.from(response).toString('base64'),
82
- mimeType: 'application/pdf',
83
- fileName: binaryData.fileName ? binaryData.fileName.replace(/\.[^.]+$/, '.pdf') : 'output.pdf',
84
- };
85
- // Prepare output item
86
- const newItem = {
87
- json: items[itemIndex].json,
88
- binary: {
89
- ...items[itemIndex].binary,
90
- [outputBinaryPropertyName]: newBinaryData,
91
- },
92
- pairedItem: itemIndex,
93
443
  };
444
+ // Add query parameters
445
+ if (sendQuery) {
446
+ const queryParams = this.getNodeParameter('queryParameters.parameters', itemIndex, []);
447
+ if (queryParams && queryParams.length > 0) {
448
+ const qs = {};
449
+ for (const param of queryParams) {
450
+ qs[param.name] = param.value;
451
+ }
452
+ requestOptions.qs = qs;
453
+ }
454
+ }
455
+ // Add headers
456
+ if (sendHeaders) {
457
+ const headerParams = this.getNodeParameter('headerParameters.parameters', itemIndex, []);
458
+ if (headerParams && headerParams.length > 0) {
459
+ for (const param of headerParams) {
460
+ requestOptions.headers[param.name] = param.value;
461
+ }
462
+ }
463
+ }
464
+ // Add body
465
+ if (sendBody && contentType) {
466
+ if (contentType === 'json') {
467
+ const jsonBody = this.getNodeParameter('jsonBody', itemIndex, '');
468
+ requestOptions.body = jsonBody;
469
+ requestOptions.headers['Content-Type'] = 'application/json';
470
+ }
471
+ else if (contentType === 'raw') {
472
+ const rawBody = this.getNodeParameter('jsonBody', itemIndex, '');
473
+ requestOptions.body = rawBody;
474
+ }
475
+ else if (contentType === 'form-urlencoded') {
476
+ const bodyParams = this.getNodeParameter('bodyParameters.parameters', itemIndex, []);
477
+ const form = {};
478
+ for (const param of bodyParams) {
479
+ form[param.name] = param.value;
480
+ }
481
+ requestOptions.form = form;
482
+ }
483
+ else if (contentType === 'multipart-form-data') {
484
+ const FormData = require('form-data');
485
+ const formData = new FormData();
486
+ const bodyParams = this.getNodeParameter('bodyParameters.parameters', itemIndex, []);
487
+ for (const param of bodyParams) {
488
+ const paramName = param.name;
489
+ const paramType = param.parameterType;
490
+ if (paramType === 'formBinaryData') {
491
+ const inputDataFieldName = param.inputDataFieldName;
492
+ const binaryData = (_a = items[itemIndex].binary) === null || _a === void 0 ? void 0 : _a[inputDataFieldName];
493
+ if (binaryData && binaryData.data) {
494
+ const buffer = Buffer.from(binaryData.data, 'base64');
495
+ formData.append(paramName, buffer, {
496
+ filename: binaryData.fileName || 'file',
497
+ contentType: binaryData.mimeType || 'application/octet-stream',
498
+ });
499
+ }
500
+ }
501
+ else {
502
+ formData.append(paramName, param.value);
503
+ }
504
+ }
505
+ requestOptions.body = formData;
506
+ Object.assign(requestOptions.headers, formData.getHeaders());
507
+ }
508
+ else if (contentType === 'binaryData') {
509
+ const inputBinaryField = this.getNodeParameter('inputBinaryField', itemIndex, 'data');
510
+ const binaryData = (_b = items[itemIndex].binary) === null || _b === void 0 ? void 0 : _b[inputBinaryField];
511
+ if (!binaryData || !binaryData.data) {
512
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `No binary data found in property "${inputBinaryField}"`, { itemIndex });
513
+ }
514
+ requestOptions.body = Buffer.from(binaryData.data, 'base64');
515
+ if (binaryData.mimeType) {
516
+ requestOptions.headers['Content-Type'] = binaryData.mimeType;
517
+ }
518
+ }
519
+ }
520
+ // Add options
521
+ if (options.allowUnauthorizedCerts === true) {
522
+ requestOptions.skipSslCertificateValidation = true;
523
+ }
524
+ if (options.followRedirect !== undefined) {
525
+ requestOptions.followRedirect = options.followRedirect;
526
+ if (options.followRedirect && options.maxRedirects) {
527
+ requestOptions.maxRedirects = options.maxRedirects;
528
+ }
529
+ }
530
+ if (options.timeout !== undefined) {
531
+ requestOptions.timeout = options.timeout;
532
+ }
533
+ if (options.proxy) {
534
+ requestOptions.proxy = options.proxy;
535
+ }
536
+ const responseFormat = options.responseFormat || 'autodetect';
537
+ if (responseFormat === 'file') {
538
+ requestOptions.encoding = 'arraybuffer';
539
+ requestOptions.returnFullResponse = false;
540
+ }
541
+ else if (responseFormat === 'json') {
542
+ requestOptions.json = true;
543
+ }
544
+ // Make the request
545
+ const response = await this.helpers.httpRequest(requestOptions);
546
+ // Process response
547
+ let newItem;
548
+ if (responseFormat === 'file') {
549
+ const outputBinaryField = options.outputBinaryField || 'data';
550
+ const binaryData = {
551
+ data: Buffer.from(response).toString('base64'),
552
+ mimeType: 'application/pdf',
553
+ fileName: 'output.pdf',
554
+ };
555
+ newItem = {
556
+ json: items[itemIndex].json,
557
+ binary: {
558
+ ...items[itemIndex].binary,
559
+ [outputBinaryField]: binaryData,
560
+ },
561
+ pairedItem: itemIndex,
562
+ };
563
+ }
564
+ else {
565
+ newItem = {
566
+ json: typeof response === 'object' ? response : { data: response },
567
+ pairedItem: itemIndex,
568
+ };
569
+ }
94
570
  returnData.push(newItem);
95
571
  }
96
572
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-make-pdf",
3
- "version": "1.0.9",
3
+ "version": "1.2.0",
4
4
  "description": "n8n node to generate PDFs via Gotenberg - Convert HTML to PDF with ease",
5
5
  "keywords": [
6
6
  "n8n",
@@ -36,7 +36,8 @@
36
36
  "lint": "tsc --noEmit"
37
37
  },
38
38
  "dependencies": {
39
- "n8n-workflow": "^1.0.0 || ^2.0.0"
39
+ "n8n-workflow": "^1.0.0 || ^2.0.0",
40
+ "form-data": "^4.0.0"
40
41
  },
41
42
  "devDependencies": {
42
43
  "@types/node": "^25.0.3",