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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|