n8n-nodes-sotoros-gotenberg 1.0.8 → 1.0.9

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.
@@ -288,79 +288,173 @@ class Gotenberg {
288
288
  // Создаем FormData для отправки в Gotenberg - один запрос для всех элементов списка
289
289
  const formData = new form_data_1.default();
290
290
  let totalFilesCount = 0;
291
+ const processedFiles = [];
292
+ // Собираем информацию о доступных binary данных в aggregatedItem
293
+ const aggregatedBinaryKeys = Object.keys(aggregatedItem.binary || {});
294
+ const debugInfo = {
295
+ aggregatedItemBinaryKeys: aggregatedBinaryKeys,
296
+ listDataLength: listData.length,
297
+ binaryPropertyName,
298
+ listPropertyName: listPropertyName || '(root array)',
299
+ processingSteps: [],
300
+ };
291
301
  // Собираем все binary данные из всех элементов списка
292
302
  for (let listIndex = 0; listIndex < listData.length; listIndex++) {
293
303
  const listItem = listData[listIndex];
304
+ const stepInfo = {
305
+ listIndex,
306
+ itemType: typeof listItem,
307
+ isObject: typeof listItem === 'object' && listItem !== null,
308
+ itemKeys: listItem && typeof listItem === 'object' ? Object.keys(listItem) : [],
309
+ foundBinary: false,
310
+ source: 'none',
311
+ binaryKey: undefined,
312
+ };
294
313
  if (!listItem || typeof listItem !== 'object') {
314
+ stepInfo.error = 'Item is not an object or is null';
315
+ debugInfo.processingSteps.push(stepInfo);
295
316
  continue; // Пропускаем некорректные элементы
296
317
  }
297
318
  // Элемент списка может быть INodeExecutionData (с полями json и binary)
298
319
  // или обычным объектом с binary данными в разных местах
299
320
  let itemBinary;
300
321
  let binaryProperty;
322
+ let binaryKeyFromAggregated;
323
+ let source = 'unknown';
301
324
  // Вариант 1: элемент имеет структуру INodeExecutionData (json и binary)
302
325
  if ('binary' in listItem && listItem.binary && typeof listItem.binary === 'object') {
303
326
  itemBinary = listItem.binary;
304
- binaryProperty = itemBinary[binaryPropertyName];
327
+ if (binaryPropertyName in itemBinary) {
328
+ binaryProperty = itemBinary[binaryPropertyName];
329
+ source = 'listItem.binary';
330
+ stepInfo.source = source;
331
+ stepInfo.binaryKey = binaryPropertyName;
332
+ stepInfo.foundBinary = true;
333
+ }
305
334
  }
306
335
  // Вариант 2: binary данные находятся в свойстве элемента напрямую
307
- else if (binaryPropertyName in listItem) {
336
+ if (!binaryProperty && binaryPropertyName in listItem) {
308
337
  const binaryProp = listItem[binaryPropertyName];
309
338
  if (binaryProp && typeof binaryProp === 'object') {
310
339
  // Может быть одиночным объектом или массивом
311
- if (binaryProp.data) {
340
+ if (binaryProp.data || binaryProp.fileName || binaryProp.mimeType) {
312
341
  // Это одиночный binary объект
313
342
  binaryProperty = binaryProp;
343
+ source = 'listItem[binaryPropertyName]';
344
+ stepInfo.source = source;
345
+ stepInfo.binaryKey = binaryPropertyName;
346
+ stepInfo.foundBinary = true;
314
347
  }
315
348
  else if (Array.isArray(binaryProp)) {
316
349
  // Это массив binary объектов
317
350
  binaryProperty = binaryProp;
351
+ source = 'listItem[binaryPropertyName] (array)';
352
+ stepInfo.source = source;
353
+ stepInfo.binaryKey = binaryPropertyName;
354
+ stepInfo.foundBinary = true;
318
355
  }
319
356
  }
320
357
  }
321
358
  // Вариант 3: если binary данные не найдены в элементе списка,
322
359
  // ищем их в aggregatedItem.binary по индексу
323
360
  // Для первого элемента (index 0) используем binaryPropertyName, для остальных - binaryPropertyName_${index}
324
- let binaryKeyFromAggregated;
325
361
  if (!binaryProperty && aggregatedItem.binary) {
326
362
  binaryKeyFromAggregated = listIndex === 0 ? binaryPropertyName : `${binaryPropertyName}_${listIndex}`;
327
363
  if (binaryKeyFromAggregated in aggregatedItem.binary) {
328
364
  binaryProperty = aggregatedItem.binary[binaryKeyFromAggregated];
365
+ source = 'aggregatedItem.binary';
366
+ stepInfo.source = source;
367
+ stepInfo.binaryKey = binaryKeyFromAggregated;
368
+ stepInfo.foundBinary = true;
369
+ }
370
+ else {
371
+ // Пробуем альтернативные варианты именования
372
+ const alternativeKeys = [
373
+ `${binaryPropertyName}_${listIndex + 1}`, // data_1 для index 0
374
+ `${listIndex}`, // просто индекс
375
+ `file_${listIndex}`, // file_0, file_1
376
+ ];
377
+ for (const altKey of alternativeKeys) {
378
+ if (altKey in aggregatedItem.binary) {
379
+ binaryProperty = aggregatedItem.binary[altKey];
380
+ source = `aggregatedItem.binary (alternative key: ${altKey})`;
381
+ stepInfo.source = source;
382
+ stepInfo.binaryKey = altKey;
383
+ stepInfo.foundBinary = true;
384
+ binaryKeyFromAggregated = altKey;
385
+ break;
386
+ }
387
+ }
329
388
  }
330
389
  }
331
390
  if (!binaryProperty) {
391
+ stepInfo.error = `Binary property not found. Tried: listItem.binary["${binaryPropertyName}"], listItem["${binaryPropertyName}"], aggregatedItem.binary["${binaryPropertyName}${listIndex === 0 ? '' : '_' + listIndex}"]`;
392
+ debugInfo.processingSteps.push(stepInfo);
332
393
  continue; // Пропускаем элементы без binary данных
333
394
  }
334
395
  // Обрабатываем как одиночное значение, так и массив
335
396
  const binaryItems = Array.isArray(binaryProperty) ? binaryProperty : [binaryProperty];
397
+ stepInfo.binaryItemsCount = binaryItems.length;
336
398
  // Добавляем все файлы из этого элемента списка
337
399
  for (let i = 0; i < binaryItems.length; i++) {
338
400
  const binaryItem = binaryItems[i];
339
401
  if (!binaryItem) {
402
+ stepInfo.error = `Binary item ${i} is null or undefined`;
340
403
  continue; // Пропускаем файлы без данных
341
404
  }
342
405
  // Получаем buffer из binary данных через хелпер n8n
343
406
  let dataBuffer;
407
+ let bufferError;
408
+ const fileInfo = {
409
+ listIndex,
410
+ fileIndex: i,
411
+ source,
412
+ binaryKey: binaryKeyFromAggregated || binaryPropertyName,
413
+ fileName: binaryItem.fileName || 'unknown',
414
+ mimeType: binaryItem.mimeType || 'unknown',
415
+ hasData: false,
416
+ fileSize: 0,
417
+ };
344
418
  try {
345
419
  // Если binary данные из aggregatedItem.binary, используем имя свойства напрямую
346
420
  if (binaryKeyFromAggregated) {
347
421
  dataBuffer = await this.helpers.getBinaryDataBuffer(0, binaryKeyFromAggregated);
422
+ fileInfo.source = `aggregatedItem.binary["${binaryKeyFromAggregated}"]`;
348
423
  }
349
424
  else {
350
425
  // Используем хелпер getBinaryDataBuffer для правильной обработки binary данных
351
426
  // Передаем itemIndex = 0 (aggregatedItem) и IBinaryData объект
352
427
  dataBuffer = await this.helpers.getBinaryDataBuffer(0, binaryItem);
428
+ fileInfo.source = source;
353
429
  }
354
430
  }
355
431
  catch (error) {
432
+ bufferError = error.message || String(error);
433
+ fileInfo.bufferError = bufferError;
356
434
  // Если не получилось через хелпер, пробуем напрямую (для обратной совместимости)
357
435
  if (binaryItem.data) {
358
- dataBuffer = Buffer.from(binaryItem.data, 'base64');
436
+ try {
437
+ dataBuffer = Buffer.from(binaryItem.data, 'base64');
438
+ fileInfo.source = `${source} (direct base64)`;
439
+ }
440
+ catch (e) {
441
+ bufferError = `Failed to decode base64: ${e.message}`;
442
+ fileInfo.bufferError = bufferError;
443
+ }
359
444
  }
360
445
  else {
446
+ fileInfo.bufferError = 'No data field in binaryItem and getBinaryDataBuffer failed';
447
+ debugInfo.processingSteps.push(stepInfo);
361
448
  continue; // Пропускаем файлы без данных
362
449
  }
363
450
  }
451
+ if (!dataBuffer || dataBuffer.length === 0) {
452
+ fileInfo.bufferError = 'Buffer is empty or null';
453
+ debugInfo.processingSteps.push(stepInfo);
454
+ continue; // Пропускаем пустые файлы
455
+ }
456
+ fileInfo.hasData = true;
457
+ fileInfo.fileSize = dataBuffer.length;
364
458
  // Используем fileName из метаданных элемента списка, если доступно
365
459
  const fileName = binaryItem.fileName ||
366
460
  (listItem && typeof listItem === 'object' && 'fileName' in listItem
@@ -369,14 +463,21 @@ class Gotenberg {
369
463
  (listItem && typeof listItem === 'object' && 'fileExtension' in listItem
370
464
  ? `${binaryPropertyName}_${listIndex}.${listItem.fileExtension}`
371
465
  : `${binaryPropertyName}_${listIndex}_${i}.${getFileExtensionFromMimeType(binaryItem.mimeType || '')}`);
466
+ fileInfo.fileName = fileName;
372
467
  formData.append('files', dataBuffer, {
373
468
  filename: fileName,
374
469
  contentType: binaryItem.mimeType || 'application/octet-stream',
375
470
  });
471
+ processedFiles.push(fileInfo);
376
472
  totalFilesCount++;
377
473
  }
474
+ stepInfo.processedFiles = processedFiles.filter((f) => f.listIndex === listIndex).length;
475
+ debugInfo.processingSteps.push(stepInfo);
378
476
  }
379
477
  if (totalFilesCount === 0) {
478
+ // Добавляем информацию о шагах обработки в debugInfo
479
+ debugInfo.processedFiles = processedFiles;
480
+ debugInfo.totalFilesCount = totalFilesCount;
380
481
  // Собираем информацию о структуре элементов списка
381
482
  const sampleItems = listData.slice(0, 3).map((item, index) => {
382
483
  const itemInfo = {
@@ -414,7 +515,7 @@ class Gotenberg {
414
515
  sampleBinaryData: {},
415
516
  };
416
517
  // Показываем структуру первых нескольких binary данных
417
- const binaryKeysToShow = Object.keys(aggregatedItem.binary || {}).slice(0, 3);
518
+ const binaryKeysToShow = Object.keys(aggregatedItem.binary || {}).slice(0, 5);
418
519
  for (const key of binaryKeysToShow) {
419
520
  const binaryData = aggregatedItem.binary[key];
420
521
  if (binaryData) {
@@ -423,6 +524,8 @@ class Gotenberg {
423
524
  dataLength: binaryData.data ? binaryData.data.length : 0,
424
525
  mimeType: binaryData.mimeType,
425
526
  fileName: binaryData.fileName,
527
+ hasId: !!binaryData.id,
528
+ id: binaryData.id,
426
529
  };
427
530
  }
428
531
  }
@@ -449,6 +552,7 @@ class Gotenberg {
449
552
  return keys;
450
553
  })(),
451
554
  },
555
+ debugInfo,
452
556
  });
453
557
  const structureInfo = JSON.stringify(formattedStructure, null, 2);
454
558
  // Проверяем, есть ли binary данные в aggregatedItem.binary
@@ -458,6 +562,7 @@ class Gotenberg {
458
562
  expectedBinaryKeys.push(i === 0 ? binaryPropertyName : `${binaryPropertyName}_${i}`);
459
563
  }
460
564
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), `No valid binary files found in any items. Please make sure binary data exists.\n\n` +
565
+ `Processing steps:\n${JSON.stringify(debugInfo.processingSteps, null, 2)}\n\n` +
461
566
  `Input data structure:\n${structureInfo}\n\n` +
462
567
  `Binary data search locations:\n` +
463
568
  `1. In list item: item.binary["${binaryPropertyName}"] or item["${binaryPropertyName}"]\n` +
@@ -468,7 +573,7 @@ class Gotenberg {
468
573
  `Tips:\n` +
469
574
  `- Check that binaryPropertyName "${binaryPropertyName}" matches the property name\n` +
470
575
  `- Verify that binary data exists in aggregatedItem.binary with keys: "${binaryPropertyName}", "${binaryPropertyName}_1", "${binaryPropertyName}_2", etc.\n` +
471
- `- Make sure binary data has a "data" field with base64 content`);
576
+ `- Make sure binary data has a "data" field with base64 content or uses n8n binary storage`);
472
577
  }
473
578
  // Добавляем опции в зависимости от операции
474
579
  if (operation === 'office' || operation === 'html' || operation === 'markdown') {
@@ -622,9 +727,14 @@ class Gotenberg {
622
727
  }
623
728
  // Проверяем, что получили валидный PDF
624
729
  if (pdfBuffer.length < 4 || pdfBuffer.toString('ascii', 0, 4) !== '%PDF') {
625
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid PDF received from Gotenberg. The response does not appear to be a valid PDF file.');
730
+ const pdfHeader = pdfBuffer.length > 0 ? pdfBuffer.toString('ascii', 0, Math.min(20, pdfBuffer.length)) : 'empty';
731
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid PDF received from Gotenberg. The response does not appear to be a valid PDF file.\n` +
732
+ `PDF buffer size: ${pdfBuffer.length} bytes\n` +
733
+ `First 20 bytes: ${pdfHeader}\n` +
734
+ `Processed files info:\n${JSON.stringify(processedFiles, null, 2)}\n` +
735
+ `Debug info:\n${JSON.stringify(debugInfo, null, 2)}`);
626
736
  }
627
- // Создаем один выходной элемент с результатом
737
+ // Создаем один выходной элемент с результатом и отладочной информацией
628
738
  const newItem = {
629
739
  json: {
630
740
  ...aggregatedItem.json,
@@ -632,6 +742,27 @@ class Gotenberg {
632
742
  operation,
633
743
  fileCount: totalFilesCount,
634
744
  processedItems: listData.length,
745
+ debug: {
746
+ processedFiles: processedFiles.map((f) => ({
747
+ source: f.source,
748
+ binaryKey: f.binaryKey,
749
+ fileName: f.fileName,
750
+ fileSize: f.fileSize,
751
+ mimeType: f.mimeType,
752
+ listIndex: f.listIndex,
753
+ fileIndex: f.fileIndex,
754
+ })),
755
+ processingSteps: debugInfo.processingSteps,
756
+ aggregatedBinaryKeys: debugInfo.aggregatedItemBinaryKeys,
757
+ configuration: {
758
+ binaryPropertyName,
759
+ listPropertyName: listPropertyName || '(root array)',
760
+ },
761
+ },
762
+ pdfInfo: {
763
+ size: pdfBuffer.length,
764
+ isValid: pdfBuffer.length >= 4 && pdfBuffer.toString('ascii', 0, 4) === '%PDF',
765
+ },
635
766
  },
636
767
  binary: {
637
768
  ...aggregatedItem.binary,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-sotoros-gotenberg",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "n8n custom node for Gotenberg integration with binary data support",
5
5
  "keywords": [
6
6
  "n8n-community-node",