n8n-nodes-sotoros-gotenberg 1.0.8 → 1.0.10
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,95 +288,125 @@ class Gotenberg {
|
|
|
288
288
|
// Создаем FormData для отправки в Gotenberg - один запрос для всех элементов списка
|
|
289
289
|
const formData = new form_data_1.default();
|
|
290
290
|
let totalFilesCount = 0;
|
|
291
|
-
|
|
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
|
+
};
|
|
301
|
+
// Собираем все binary данные из aggregatedItem.binary
|
|
302
|
+
// Используем только getBinaryDataBuffer(0, key) для получения файлов
|
|
292
303
|
for (let listIndex = 0; listIndex < listData.length; listIndex++) {
|
|
293
304
|
const listItem = listData[listIndex];
|
|
305
|
+
const stepInfo = {
|
|
306
|
+
listIndex,
|
|
307
|
+
itemType: typeof listItem,
|
|
308
|
+
isObject: typeof listItem === 'object' && listItem !== null,
|
|
309
|
+
itemKeys: listItem && typeof listItem === 'object' ? Object.keys(listItem) : [],
|
|
310
|
+
foundBinary: false,
|
|
311
|
+
binaryKey: undefined,
|
|
312
|
+
processedFiles: 0,
|
|
313
|
+
};
|
|
294
314
|
if (!listItem || typeof listItem !== 'object') {
|
|
315
|
+
stepInfo.error = 'Item is not an object or is null';
|
|
316
|
+
debugInfo.processingSteps.push(stepInfo);
|
|
295
317
|
continue; // Пропускаем некорректные элементы
|
|
296
318
|
}
|
|
297
|
-
//
|
|
298
|
-
// или обычным объектом с binary данными в разных местах
|
|
299
|
-
let itemBinary;
|
|
300
|
-
let binaryProperty;
|
|
301
|
-
// Вариант 1: элемент имеет структуру INodeExecutionData (json и binary)
|
|
302
|
-
if ('binary' in listItem && listItem.binary && typeof listItem.binary === 'object') {
|
|
303
|
-
itemBinary = listItem.binary;
|
|
304
|
-
binaryProperty = itemBinary[binaryPropertyName];
|
|
305
|
-
}
|
|
306
|
-
// Вариант 2: binary данные находятся в свойстве элемента напрямую
|
|
307
|
-
else if (binaryPropertyName in listItem) {
|
|
308
|
-
const binaryProp = listItem[binaryPropertyName];
|
|
309
|
-
if (binaryProp && typeof binaryProp === 'object') {
|
|
310
|
-
// Может быть одиночным объектом или массивом
|
|
311
|
-
if (binaryProp.data) {
|
|
312
|
-
// Это одиночный binary объект
|
|
313
|
-
binaryProperty = binaryProp;
|
|
314
|
-
}
|
|
315
|
-
else if (Array.isArray(binaryProp)) {
|
|
316
|
-
// Это массив binary объектов
|
|
317
|
-
binaryProperty = binaryProp;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
// Вариант 3: если binary данные не найдены в элементе списка,
|
|
322
|
-
// ищем их в aggregatedItem.binary по индексу
|
|
319
|
+
// Определяем ключ для binary данных в aggregatedItem.binary
|
|
323
320
|
// Для первого элемента (index 0) используем binaryPropertyName, для остальных - binaryPropertyName_${index}
|
|
324
|
-
let
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
321
|
+
let binaryKey;
|
|
322
|
+
const possibleKeys = [
|
|
323
|
+
listIndex === 0 ? binaryPropertyName : `${binaryPropertyName}_${listIndex}`, // data или data_0, data_1, etc.
|
|
324
|
+
`${binaryPropertyName}_${listIndex + 1}`, // data_1 для index 0
|
|
325
|
+
`${listIndex}`, // просто индекс
|
|
326
|
+
`file_${listIndex}`, // file_0, file_1
|
|
327
|
+
];
|
|
328
|
+
// Ищем первый доступный ключ в aggregatedItem.binary
|
|
329
|
+
for (const key of possibleKeys) {
|
|
330
|
+
if (aggregatedItem.binary && key in aggregatedItem.binary) {
|
|
331
|
+
binaryKey = key;
|
|
332
|
+
stepInfo.binaryKey = key;
|
|
333
|
+
stepInfo.foundBinary = true;
|
|
334
|
+
break;
|
|
329
335
|
}
|
|
330
336
|
}
|
|
331
|
-
if (!
|
|
337
|
+
if (!binaryKey) {
|
|
338
|
+
stepInfo.error = `Binary key not found in aggregatedItem.binary. Tried keys: ${possibleKeys.join(', ')}. Available keys: ${aggregatedBinaryKeys.join(', ')}`;
|
|
339
|
+
debugInfo.processingSteps.push(stepInfo);
|
|
332
340
|
continue; // Пропускаем элементы без binary данных
|
|
333
341
|
}
|
|
334
|
-
//
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
// Используем fileName из метаданных элемента списка, если доступно
|
|
365
|
-
const fileName = binaryItem.fileName ||
|
|
366
|
-
(listItem && typeof listItem === 'object' && 'fileName' in listItem
|
|
367
|
-
? listItem.fileName
|
|
368
|
-
: undefined) ||
|
|
369
|
-
(listItem && typeof listItem === 'object' && 'fileExtension' in listItem
|
|
370
|
-
? `${binaryPropertyName}_${listIndex}.${listItem.fileExtension}`
|
|
371
|
-
: `${binaryPropertyName}_${listIndex}_${i}.${getFileExtensionFromMimeType(binaryItem.mimeType || '')}`);
|
|
372
|
-
formData.append('files', dataBuffer, {
|
|
373
|
-
filename: fileName,
|
|
374
|
-
contentType: binaryItem.mimeType || 'application/octet-stream',
|
|
375
|
-
});
|
|
376
|
-
totalFilesCount++;
|
|
342
|
+
// Получаем метаданные binary файла из aggregatedItem.binary
|
|
343
|
+
if (!aggregatedItem.binary) {
|
|
344
|
+
stepInfo.error = 'aggregatedItem.binary is undefined';
|
|
345
|
+
debugInfo.processingSteps.push(stepInfo);
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
const binaryMetadata = aggregatedItem.binary[binaryKey];
|
|
349
|
+
if (!binaryMetadata) {
|
|
350
|
+
stepInfo.error = `Binary metadata not found for key: ${binaryKey}`;
|
|
351
|
+
debugInfo.processingSteps.push(stepInfo);
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
// Получаем buffer из binary данных через хелпер n8n
|
|
355
|
+
// Используем только getBinaryDataBuffer(0, key)
|
|
356
|
+
let dataBuffer;
|
|
357
|
+
let bufferError;
|
|
358
|
+
const fileInfo = {
|
|
359
|
+
listIndex,
|
|
360
|
+
fileIndex: 0,
|
|
361
|
+
binaryKey,
|
|
362
|
+
fileName: binaryMetadata.fileName || 'unknown',
|
|
363
|
+
mimeType: binaryMetadata.mimeType || 'application/octet-stream',
|
|
364
|
+
hasData: false,
|
|
365
|
+
fileSize: 0,
|
|
366
|
+
};
|
|
367
|
+
try {
|
|
368
|
+
// Используем только getBinaryDataBuffer(0, key) для получения файла
|
|
369
|
+
dataBuffer = await this.helpers.getBinaryDataBuffer(0, binaryKey);
|
|
370
|
+
fileInfo.hasData = true;
|
|
371
|
+
fileInfo.fileSize = dataBuffer.length;
|
|
377
372
|
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
bufferError = error.message || String(error);
|
|
375
|
+
fileInfo.error = bufferError;
|
|
376
|
+
stepInfo.error = `Failed to get binary data buffer: ${bufferError}`;
|
|
377
|
+
debugInfo.processingSteps.push(stepInfo);
|
|
378
|
+
processedFiles.push(fileInfo);
|
|
379
|
+
continue; // Пропускаем файлы, которые не удалось получить
|
|
380
|
+
}
|
|
381
|
+
if (!dataBuffer || dataBuffer.length === 0) {
|
|
382
|
+
fileInfo.error = 'Buffer is empty or null';
|
|
383
|
+
stepInfo.error = 'Buffer is empty or null';
|
|
384
|
+
debugInfo.processingSteps.push(stepInfo);
|
|
385
|
+
processedFiles.push(fileInfo);
|
|
386
|
+
continue; // Пропускаем пустые файлы
|
|
387
|
+
}
|
|
388
|
+
// Используем fileName из метаданных или генерируем на основе данных элемента списка
|
|
389
|
+
const fileName = binaryMetadata.fileName ||
|
|
390
|
+
(listItem && typeof listItem === 'object' && 'fileName' in listItem
|
|
391
|
+
? listItem.fileName
|
|
392
|
+
: undefined) ||
|
|
393
|
+
(listItem && typeof listItem === 'object' && 'fileExtension' in listItem
|
|
394
|
+
? `${binaryPropertyName}_${listIndex}.${listItem.fileExtension}`
|
|
395
|
+
: `${binaryPropertyName}_${listIndex}.${getFileExtensionFromMimeType(binaryMetadata.mimeType || '')}`);
|
|
396
|
+
fileInfo.fileName = fileName;
|
|
397
|
+
formData.append('files', dataBuffer, {
|
|
398
|
+
filename: fileName,
|
|
399
|
+
contentType: binaryMetadata.mimeType || 'application/octet-stream',
|
|
400
|
+
});
|
|
401
|
+
processedFiles.push(fileInfo);
|
|
402
|
+
totalFilesCount++;
|
|
403
|
+
stepInfo.processedFiles = 1;
|
|
404
|
+
debugInfo.processingSteps.push(stepInfo);
|
|
378
405
|
}
|
|
379
406
|
if (totalFilesCount === 0) {
|
|
407
|
+
// Добавляем информацию о шагах обработки в debugInfo
|
|
408
|
+
debugInfo.processedFiles = processedFiles;
|
|
409
|
+
debugInfo.totalFilesCount = totalFilesCount;
|
|
380
410
|
// Собираем информацию о структуре элементов списка
|
|
381
411
|
const sampleItems = listData.slice(0, 3).map((item, index) => {
|
|
382
412
|
const itemInfo = {
|
|
@@ -414,7 +444,7 @@ class Gotenberg {
|
|
|
414
444
|
sampleBinaryData: {},
|
|
415
445
|
};
|
|
416
446
|
// Показываем структуру первых нескольких binary данных
|
|
417
|
-
const binaryKeysToShow = Object.keys(aggregatedItem.binary || {}).slice(0,
|
|
447
|
+
const binaryKeysToShow = Object.keys(aggregatedItem.binary || {}).slice(0, 5);
|
|
418
448
|
for (const key of binaryKeysToShow) {
|
|
419
449
|
const binaryData = aggregatedItem.binary[key];
|
|
420
450
|
if (binaryData) {
|
|
@@ -423,6 +453,8 @@ class Gotenberg {
|
|
|
423
453
|
dataLength: binaryData.data ? binaryData.data.length : 0,
|
|
424
454
|
mimeType: binaryData.mimeType,
|
|
425
455
|
fileName: binaryData.fileName,
|
|
456
|
+
hasId: !!binaryData.id,
|
|
457
|
+
id: binaryData.id,
|
|
426
458
|
};
|
|
427
459
|
}
|
|
428
460
|
}
|
|
@@ -449,6 +481,7 @@ class Gotenberg {
|
|
|
449
481
|
return keys;
|
|
450
482
|
})(),
|
|
451
483
|
},
|
|
484
|
+
debugInfo,
|
|
452
485
|
});
|
|
453
486
|
const structureInfo = JSON.stringify(formattedStructure, null, 2);
|
|
454
487
|
// Проверяем, есть ли binary данные в aggregatedItem.binary
|
|
@@ -458,6 +491,7 @@ class Gotenberg {
|
|
|
458
491
|
expectedBinaryKeys.push(i === 0 ? binaryPropertyName : `${binaryPropertyName}_${i}`);
|
|
459
492
|
}
|
|
460
493
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `No valid binary files found in any items. Please make sure binary data exists.\n\n` +
|
|
494
|
+
`Processing steps:\n${JSON.stringify(debugInfo.processingSteps, null, 2)}\n\n` +
|
|
461
495
|
`Input data structure:\n${structureInfo}\n\n` +
|
|
462
496
|
`Binary data search locations:\n` +
|
|
463
497
|
`1. In list item: item.binary["${binaryPropertyName}"] or item["${binaryPropertyName}"]\n` +
|
|
@@ -468,7 +502,7 @@ class Gotenberg {
|
|
|
468
502
|
`Tips:\n` +
|
|
469
503
|
`- Check that binaryPropertyName "${binaryPropertyName}" matches the property name\n` +
|
|
470
504
|
`- 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`);
|
|
505
|
+
`- Make sure binary data has a "data" field with base64 content or uses n8n binary storage`);
|
|
472
506
|
}
|
|
473
507
|
// Добавляем опции в зависимости от операции
|
|
474
508
|
if (operation === 'office' || operation === 'html' || operation === 'markdown') {
|
|
@@ -622,9 +656,14 @@ class Gotenberg {
|
|
|
622
656
|
}
|
|
623
657
|
// Проверяем, что получили валидный PDF
|
|
624
658
|
if (pdfBuffer.length < 4 || pdfBuffer.toString('ascii', 0, 4) !== '%PDF') {
|
|
625
|
-
|
|
659
|
+
const pdfHeader = pdfBuffer.length > 0 ? pdfBuffer.toString('ascii', 0, Math.min(20, pdfBuffer.length)) : 'empty';
|
|
660
|
+
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` +
|
|
661
|
+
`PDF buffer size: ${pdfBuffer.length} bytes\n` +
|
|
662
|
+
`First 20 bytes: ${pdfHeader}\n` +
|
|
663
|
+
`Processed files info:\n${JSON.stringify(processedFiles, null, 2)}\n` +
|
|
664
|
+
`Debug info:\n${JSON.stringify(debugInfo, null, 2)}`);
|
|
626
665
|
}
|
|
627
|
-
// Создаем один выходной элемент с результатом
|
|
666
|
+
// Создаем один выходной элемент с результатом и отладочной информацией
|
|
628
667
|
const newItem = {
|
|
629
668
|
json: {
|
|
630
669
|
...aggregatedItem.json,
|
|
@@ -632,6 +671,28 @@ class Gotenberg {
|
|
|
632
671
|
operation,
|
|
633
672
|
fileCount: totalFilesCount,
|
|
634
673
|
processedItems: listData.length,
|
|
674
|
+
debug: {
|
|
675
|
+
processedFiles: processedFiles.map((f) => ({
|
|
676
|
+
binaryKey: f.binaryKey,
|
|
677
|
+
fileName: f.fileName,
|
|
678
|
+
fileSize: f.fileSize,
|
|
679
|
+
mimeType: f.mimeType,
|
|
680
|
+
listIndex: f.listIndex,
|
|
681
|
+
fileIndex: f.fileIndex,
|
|
682
|
+
hasData: f.hasData,
|
|
683
|
+
error: f.error,
|
|
684
|
+
})),
|
|
685
|
+
processingSteps: debugInfo.processingSteps,
|
|
686
|
+
aggregatedBinaryKeys: debugInfo.aggregatedItemBinaryKeys,
|
|
687
|
+
configuration: {
|
|
688
|
+
binaryPropertyName,
|
|
689
|
+
listPropertyName: listPropertyName || '(root array)',
|
|
690
|
+
},
|
|
691
|
+
},
|
|
692
|
+
pdfInfo: {
|
|
693
|
+
size: pdfBuffer.length,
|
|
694
|
+
isValid: pdfBuffer.length >= 4 && pdfBuffer.toString('ascii', 0, 4) === '%PDF',
|
|
695
|
+
},
|
|
635
696
|
},
|
|
636
697
|
binary: {
|
|
637
698
|
...aggregatedItem.binary,
|