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
- itemBinary = { [binaryPropertyName]: binaryProp };
313
+ binaryProperty = binaryProp;
261
314
  }
262
315
  else if (Array.isArray(binaryProp)) {
263
316
  // Это массив binary объектов
264
- itemBinary = { [binaryPropertyName]: binaryProp };
317
+ binaryProperty = binaryProp;
265
318
  }
266
319
  }
267
320
  }
268
- if (!itemBinary || Object.keys(itemBinary).length === 0) {
269
- continue; // Пропускаем элементы без binary данных
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; // Пропускаем элементы без указанного binary свойства
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
- `${binaryPropertyName}_${listIndex}_${i}.${getFileExtensionFromMimeType(binaryItem.mimeType || '')}`;
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
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No valid binary files found in any items. Please make sure binary data exists.');
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') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-sotoros-gotenberg",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "n8n custom node for Gotenberg integration with binary data support",
5
5
  "keywords": [
6
6
  "n8n-community-node",