n8n-nodes-sotoros-gotenberg 1.0.5 → 1.0.7
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.
|
@@ -200,6 +200,57 @@ class Gotenberg {
|
|
|
200
200
|
};
|
|
201
201
|
return mimeToExt[mimeType] || 'bin';
|
|
202
202
|
};
|
|
203
|
+
// Вспомогательная функция для форматирования структуры данных с заменой длинных base64 строк
|
|
204
|
+
const formatDataStructure = (data, maxDepth = 5, currentDepth = 0) => {
|
|
205
|
+
if (currentDepth >= maxDepth) {
|
|
206
|
+
return '[max depth reached]';
|
|
207
|
+
}
|
|
208
|
+
// Если это null или undefined
|
|
209
|
+
if (data === null || data === undefined) {
|
|
210
|
+
return data;
|
|
211
|
+
}
|
|
212
|
+
// Если это примитивный тип
|
|
213
|
+
if (typeof data !== 'object') {
|
|
214
|
+
// Если это строка и она похожа на base64 (длинная строка из base64 символов)
|
|
215
|
+
if (typeof data === 'string' && data.length > 100 && /^[A-Za-z0-9+/=]+$/.test(data)) {
|
|
216
|
+
return `[binary data: ${data.length} bytes]`;
|
|
217
|
+
}
|
|
218
|
+
// Если это очень длинная строка (не base64, но все равно длинная)
|
|
219
|
+
if (typeof data === 'string' && data.length > 200) {
|
|
220
|
+
return `[long string: ${data.length} chars]`;
|
|
221
|
+
}
|
|
222
|
+
return data;
|
|
223
|
+
}
|
|
224
|
+
// Если это массив
|
|
225
|
+
if (Array.isArray(data)) {
|
|
226
|
+
return data.map((item, index) => {
|
|
227
|
+
// Ограничиваем вывод массивов до 10 элементов
|
|
228
|
+
if (index >= 10) {
|
|
229
|
+
return `[${data.length - 10} more items...]`;
|
|
230
|
+
}
|
|
231
|
+
return formatDataStructure(item, maxDepth, currentDepth + 1);
|
|
232
|
+
}).slice(0, 10);
|
|
233
|
+
}
|
|
234
|
+
// Если это объект
|
|
235
|
+
const formatted = {};
|
|
236
|
+
for (const key in data) {
|
|
237
|
+
if (Object.prototype.hasOwnProperty.call(data, key)) {
|
|
238
|
+
const value = data[key];
|
|
239
|
+
// Специальная обработка для binary данных
|
|
240
|
+
if (key === 'data' && typeof value === 'string' && value.length > 50) {
|
|
241
|
+
formatted[key] = `[binary data: ${value.length} bytes]`;
|
|
242
|
+
}
|
|
243
|
+
else if (key === 'binary' && typeof value === 'object' && value !== null) {
|
|
244
|
+
// Для binary объектов показываем структуру, но заменяем data
|
|
245
|
+
formatted[key] = formatDataStructure(value, maxDepth, currentDepth + 1);
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
formatted[key] = formatDataStructure(value, maxDepth, currentDepth + 1);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return formatted;
|
|
253
|
+
};
|
|
203
254
|
try {
|
|
204
255
|
// Извлекаем список из JSON свойства агрегированного элемента
|
|
205
256
|
let listData;
|
|
@@ -246,9 +297,11 @@ class Gotenberg {
|
|
|
246
297
|
// Элемент списка может быть INodeExecutionData (с полями json и binary)
|
|
247
298
|
// или обычным объектом с binary данными в разных местах
|
|
248
299
|
let itemBinary;
|
|
300
|
+
let binaryProperty;
|
|
249
301
|
// Вариант 1: элемент имеет структуру INodeExecutionData (json и binary)
|
|
250
302
|
if ('binary' in listItem && listItem.binary && typeof listItem.binary === 'object') {
|
|
251
303
|
itemBinary = listItem.binary;
|
|
304
|
+
binaryProperty = itemBinary[binaryPropertyName];
|
|
252
305
|
}
|
|
253
306
|
// Вариант 2: binary данные находятся в свойстве элемента напрямую
|
|
254
307
|
else if (binaryPropertyName in listItem) {
|
|
@@ -257,21 +310,25 @@ class Gotenberg {
|
|
|
257
310
|
// Может быть одиночным объектом или массивом
|
|
258
311
|
if (binaryProp.data) {
|
|
259
312
|
// Это одиночный binary объект
|
|
260
|
-
|
|
313
|
+
binaryProperty = binaryProp;
|
|
261
314
|
}
|
|
262
315
|
else if (Array.isArray(binaryProp)) {
|
|
263
316
|
// Это массив binary объектов
|
|
264
|
-
|
|
317
|
+
binaryProperty = binaryProp;
|
|
265
318
|
}
|
|
266
319
|
}
|
|
267
320
|
}
|
|
268
|
-
|
|
269
|
-
|
|
321
|
+
// Вариант 3: если binary данные не найдены в элементе списка,
|
|
322
|
+
// ищем их в aggregatedItem.binary по индексу
|
|
323
|
+
// Для первого элемента (index 0) используем binaryPropertyName, для остальных - binaryPropertyName_${index}
|
|
324
|
+
if (!binaryProperty && aggregatedItem.binary) {
|
|
325
|
+
const binaryKey = listIndex === 0 ? binaryPropertyName : `${binaryPropertyName}_${listIndex}`;
|
|
326
|
+
if (binaryKey in aggregatedItem.binary) {
|
|
327
|
+
binaryProperty = aggregatedItem.binary[binaryKey];
|
|
328
|
+
}
|
|
270
329
|
}
|
|
271
|
-
// Получаем binary свойство
|
|
272
|
-
const binaryProperty = itemBinary[binaryPropertyName];
|
|
273
330
|
if (!binaryProperty) {
|
|
274
|
-
continue; // Пропускаем элементы без
|
|
331
|
+
continue; // Пропускаем элементы без binary данных
|
|
275
332
|
}
|
|
276
333
|
// Обрабатываем как одиночное значение, так и массив
|
|
277
334
|
const binaryItems = Array.isArray(binaryProperty) ? binaryProperty : [binaryProperty];
|
|
@@ -283,8 +340,14 @@ class Gotenberg {
|
|
|
283
340
|
}
|
|
284
341
|
// Получаем buffer из binary данных (всегда base64 в n8n)
|
|
285
342
|
const dataBuffer = Buffer.from(binaryItem.data, 'base64');
|
|
343
|
+
// Используем fileName из метаданных элемента списка, если доступно
|
|
286
344
|
const fileName = binaryItem.fileName ||
|
|
287
|
-
|
|
345
|
+
(listItem && typeof listItem === 'object' && 'fileName' in listItem
|
|
346
|
+
? listItem.fileName
|
|
347
|
+
: undefined) ||
|
|
348
|
+
(listItem && typeof listItem === 'object' && 'fileExtension' in listItem
|
|
349
|
+
? `${binaryPropertyName}_${listIndex}.${listItem.fileExtension}`
|
|
350
|
+
: `${binaryPropertyName}_${listIndex}_${i}.${getFileExtensionFromMimeType(binaryItem.mimeType || '')}`);
|
|
288
351
|
formData.append('files', dataBuffer, {
|
|
289
352
|
filename: fileName,
|
|
290
353
|
contentType: binaryItem.mimeType || 'application/octet-stream',
|
|
@@ -293,7 +356,98 @@ class Gotenberg {
|
|
|
293
356
|
}
|
|
294
357
|
}
|
|
295
358
|
if (totalFilesCount === 0) {
|
|
296
|
-
|
|
359
|
+
// Собираем информацию о структуре элементов списка
|
|
360
|
+
const sampleItems = listData.slice(0, 3).map((item, index) => {
|
|
361
|
+
const itemInfo = {
|
|
362
|
+
index,
|
|
363
|
+
type: typeof item,
|
|
364
|
+
isObject: typeof item === 'object' && item !== null,
|
|
365
|
+
hasBinary: 'binary' in (item || {}),
|
|
366
|
+
hasBinaryProperty: binaryPropertyName in (item || {}),
|
|
367
|
+
keys: item && typeof item === 'object' ? Object.keys(item) : [],
|
|
368
|
+
};
|
|
369
|
+
// Если есть binary свойство, показываем его структуру
|
|
370
|
+
if (item && typeof item === 'object') {
|
|
371
|
+
if ('binary' in item && item.binary) {
|
|
372
|
+
itemInfo.binaryKeys = Object.keys(item.binary);
|
|
373
|
+
itemInfo.binaryStructure = formatDataStructure(item.binary);
|
|
374
|
+
}
|
|
375
|
+
if (binaryPropertyName in item) {
|
|
376
|
+
itemInfo[binaryPropertyName] = formatDataStructure(item[binaryPropertyName]);
|
|
377
|
+
}
|
|
378
|
+
// Показываем структуру json части (без длинных строк)
|
|
379
|
+
if ('json' in item) {
|
|
380
|
+
itemInfo.jsonKeys = Object.keys(item.json || {});
|
|
381
|
+
itemInfo.jsonSample = formatDataStructure(item.json);
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
// Если нет json, показываем структуру самого объекта
|
|
385
|
+
itemInfo.objectSample = formatDataStructure(item);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return itemInfo;
|
|
389
|
+
});
|
|
390
|
+
// Собираем информацию о binary данных в aggregatedItem
|
|
391
|
+
const aggregatedBinaryInfo = {
|
|
392
|
+
keys: Object.keys(aggregatedItem.binary || {}),
|
|
393
|
+
sampleBinaryData: {},
|
|
394
|
+
};
|
|
395
|
+
// Показываем структуру первых нескольких binary данных
|
|
396
|
+
const binaryKeysToShow = Object.keys(aggregatedItem.binary || {}).slice(0, 3);
|
|
397
|
+
for (const key of binaryKeysToShow) {
|
|
398
|
+
const binaryData = aggregatedItem.binary[key];
|
|
399
|
+
if (binaryData) {
|
|
400
|
+
aggregatedBinaryInfo.sampleBinaryData[key] = {
|
|
401
|
+
hasData: !!binaryData.data,
|
|
402
|
+
dataLength: binaryData.data ? binaryData.data.length : 0,
|
|
403
|
+
mimeType: binaryData.mimeType,
|
|
404
|
+
fileName: binaryData.fileName,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
// Форматируем структуру данных для отладки
|
|
409
|
+
const formattedStructure = formatDataStructure({
|
|
410
|
+
aggregatedItem: {
|
|
411
|
+
jsonKeys: Object.keys(aggregatedItem.json || {}),
|
|
412
|
+
binaryInfo: aggregatedBinaryInfo,
|
|
413
|
+
jsonSample: formatDataStructure(aggregatedItem.json),
|
|
414
|
+
},
|
|
415
|
+
listDataInfo: {
|
|
416
|
+
length: listData.length,
|
|
417
|
+
propertyName: listPropertyName || '(root array)',
|
|
418
|
+
sampleItems,
|
|
419
|
+
},
|
|
420
|
+
configuration: {
|
|
421
|
+
binaryPropertyName,
|
|
422
|
+
listPropertyName: listPropertyName || '(root array)',
|
|
423
|
+
expectedBinaryKeys: (() => {
|
|
424
|
+
const keys = [];
|
|
425
|
+
for (let i = 0; i < Math.min(listData.length, 10); i++) {
|
|
426
|
+
keys.push(i === 0 ? binaryPropertyName : `${binaryPropertyName}_${i}`);
|
|
427
|
+
}
|
|
428
|
+
return keys;
|
|
429
|
+
})(),
|
|
430
|
+
},
|
|
431
|
+
});
|
|
432
|
+
const structureInfo = JSON.stringify(formattedStructure, null, 2);
|
|
433
|
+
// Проверяем, есть ли binary данные в aggregatedItem.binary
|
|
434
|
+
const aggregatedBinaryKeys = Object.keys(aggregatedItem.binary || {});
|
|
435
|
+
const expectedBinaryKeys = [];
|
|
436
|
+
for (let i = 0; i < Math.min(listData.length, 10); i++) {
|
|
437
|
+
expectedBinaryKeys.push(i === 0 ? binaryPropertyName : `${binaryPropertyName}_${i}`);
|
|
438
|
+
}
|
|
439
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `No valid binary files found in any items. Please make sure binary data exists.\n\n` +
|
|
440
|
+
`Input data structure:\n${structureInfo}\n\n` +
|
|
441
|
+
`Binary data search locations:\n` +
|
|
442
|
+
`1. In list item: item.binary["${binaryPropertyName}"] or item["${binaryPropertyName}"]\n` +
|
|
443
|
+
`2. In aggregated item: aggregatedItem.binary["${binaryPropertyName}"] (for first item)\n` +
|
|
444
|
+
`3. In aggregated item: aggregatedItem.binary["${binaryPropertyName}_${1}"] (for second item), etc.\n\n` +
|
|
445
|
+
`Found binary keys in aggregatedItem.binary: ${aggregatedBinaryKeys.length > 0 ? aggregatedBinaryKeys.join(', ') : 'none'}\n` +
|
|
446
|
+
`Expected binary keys for first 10 items: ${expectedBinaryKeys.join(', ')}\n\n` +
|
|
447
|
+
`Tips:\n` +
|
|
448
|
+
`- Check that binaryPropertyName "${binaryPropertyName}" matches the property name\n` +
|
|
449
|
+
`- Verify that binary data exists in aggregatedItem.binary with keys: "${binaryPropertyName}", "${binaryPropertyName}_1", "${binaryPropertyName}_2", etc.\n` +
|
|
450
|
+
`- Make sure binary data has a "data" field with base64 content`);
|
|
297
451
|
}
|
|
298
452
|
// Добавляем опции в зависимости от операции
|
|
299
453
|
if (operation === 'office' || operation === 'html' || operation === 'markdown') {
|