n8n-nodes-sotoros-gotenberg 1.0.6 → 1.0.8

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.
@@ -297,9 +297,11 @@ class Gotenberg {
297
297
  // Элемент списка может быть INodeExecutionData (с полями json и binary)
298
298
  // или обычным объектом с binary данными в разных местах
299
299
  let itemBinary;
300
+ let binaryProperty;
300
301
  // Вариант 1: элемент имеет структуру INodeExecutionData (json и binary)
301
302
  if ('binary' in listItem && listItem.binary && typeof listItem.binary === 'object') {
302
303
  itemBinary = listItem.binary;
304
+ binaryProperty = itemBinary[binaryPropertyName];
303
305
  }
304
306
  // Вариант 2: binary данные находятся в свойстве элемента напрямую
305
307
  else if (binaryPropertyName in listItem) {
@@ -308,34 +310,65 @@ class Gotenberg {
308
310
  // Может быть одиночным объектом или массивом
309
311
  if (binaryProp.data) {
310
312
  // Это одиночный binary объект
311
- itemBinary = { [binaryPropertyName]: binaryProp };
313
+ binaryProperty = binaryProp;
312
314
  }
313
315
  else if (Array.isArray(binaryProp)) {
314
316
  // Это массив binary объектов
315
- itemBinary = { [binaryPropertyName]: binaryProp };
317
+ binaryProperty = binaryProp;
316
318
  }
317
319
  }
318
320
  }
319
- if (!itemBinary || Object.keys(itemBinary).length === 0) {
320
- continue; // Пропускаем элементы без binary данных
321
+ // Вариант 3: если binary данные не найдены в элементе списка,
322
+ // ищем их в aggregatedItem.binary по индексу
323
+ // Для первого элемента (index 0) используем binaryPropertyName, для остальных - binaryPropertyName_${index}
324
+ let binaryKeyFromAggregated;
325
+ if (!binaryProperty && aggregatedItem.binary) {
326
+ binaryKeyFromAggregated = listIndex === 0 ? binaryPropertyName : `${binaryPropertyName}_${listIndex}`;
327
+ if (binaryKeyFromAggregated in aggregatedItem.binary) {
328
+ binaryProperty = aggregatedItem.binary[binaryKeyFromAggregated];
329
+ }
321
330
  }
322
- // Получаем binary свойство
323
- const binaryProperty = itemBinary[binaryPropertyName];
324
331
  if (!binaryProperty) {
325
- continue; // Пропускаем элементы без указанного binary свойства
332
+ continue; // Пропускаем элементы без binary данных
326
333
  }
327
334
  // Обрабатываем как одиночное значение, так и массив
328
335
  const binaryItems = Array.isArray(binaryProperty) ? binaryProperty : [binaryProperty];
329
336
  // Добавляем все файлы из этого элемента списка
330
337
  for (let i = 0; i < binaryItems.length; i++) {
331
338
  const binaryItem = binaryItems[i];
332
- if (!binaryItem || !binaryItem.data) {
339
+ if (!binaryItem) {
333
340
  continue; // Пропускаем файлы без данных
334
341
  }
335
- // Получаем buffer из binary данных (всегда base64 в n8n)
336
- const dataBuffer = Buffer.from(binaryItem.data, 'base64');
342
+ // Получаем buffer из binary данных через хелпер n8n
343
+ let dataBuffer;
344
+ try {
345
+ // Если binary данные из aggregatedItem.binary, используем имя свойства напрямую
346
+ if (binaryKeyFromAggregated) {
347
+ dataBuffer = await this.helpers.getBinaryDataBuffer(0, binaryKeyFromAggregated);
348
+ }
349
+ else {
350
+ // Используем хелпер getBinaryDataBuffer для правильной обработки binary данных
351
+ // Передаем itemIndex = 0 (aggregatedItem) и IBinaryData объект
352
+ dataBuffer = await this.helpers.getBinaryDataBuffer(0, binaryItem);
353
+ }
354
+ }
355
+ catch (error) {
356
+ // Если не получилось через хелпер, пробуем напрямую (для обратной совместимости)
357
+ if (binaryItem.data) {
358
+ dataBuffer = Buffer.from(binaryItem.data, 'base64');
359
+ }
360
+ else {
361
+ continue; // Пропускаем файлы без данных
362
+ }
363
+ }
364
+ // Используем fileName из метаданных элемента списка, если доступно
337
365
  const fileName = binaryItem.fileName ||
338
- `${binaryPropertyName}_${listIndex}_${i}.${getFileExtensionFromMimeType(binaryItem.mimeType || '')}`;
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 || '')}`);
339
372
  formData.append('files', dataBuffer, {
340
373
  filename: fileName,
341
374
  contentType: binaryItem.mimeType || 'application/octet-stream',
@@ -375,11 +408,29 @@ class Gotenberg {
375
408
  }
376
409
  return itemInfo;
377
410
  });
411
+ // Собираем информацию о binary данных в aggregatedItem
412
+ const aggregatedBinaryInfo = {
413
+ keys: Object.keys(aggregatedItem.binary || {}),
414
+ sampleBinaryData: {},
415
+ };
416
+ // Показываем структуру первых нескольких binary данных
417
+ const binaryKeysToShow = Object.keys(aggregatedItem.binary || {}).slice(0, 3);
418
+ for (const key of binaryKeysToShow) {
419
+ const binaryData = aggregatedItem.binary[key];
420
+ if (binaryData) {
421
+ aggregatedBinaryInfo.sampleBinaryData[key] = {
422
+ hasData: !!binaryData.data,
423
+ dataLength: binaryData.data ? binaryData.data.length : 0,
424
+ mimeType: binaryData.mimeType,
425
+ fileName: binaryData.fileName,
426
+ };
427
+ }
428
+ }
378
429
  // Форматируем структуру данных для отладки
379
430
  const formattedStructure = formatDataStructure({
380
431
  aggregatedItem: {
381
432
  jsonKeys: Object.keys(aggregatedItem.json || {}),
382
- binaryKeys: Object.keys(aggregatedItem.binary || {}),
433
+ binaryInfo: aggregatedBinaryInfo,
383
434
  jsonSample: formatDataStructure(aggregatedItem.json),
384
435
  },
385
436
  listDataInfo: {
@@ -390,17 +441,34 @@ class Gotenberg {
390
441
  configuration: {
391
442
  binaryPropertyName,
392
443
  listPropertyName: listPropertyName || '(root array)',
444
+ expectedBinaryKeys: (() => {
445
+ const keys = [];
446
+ for (let i = 0; i < Math.min(listData.length, 10); i++) {
447
+ keys.push(i === 0 ? binaryPropertyName : `${binaryPropertyName}_${i}`);
448
+ }
449
+ return keys;
450
+ })(),
393
451
  },
394
452
  });
395
453
  const structureInfo = JSON.stringify(formattedStructure, null, 2);
454
+ // Проверяем, есть ли binary данные в aggregatedItem.binary
455
+ const aggregatedBinaryKeys = Object.keys(aggregatedItem.binary || {});
456
+ const expectedBinaryKeys = [];
457
+ for (let i = 0; i < Math.min(listData.length, 10); i++) {
458
+ expectedBinaryKeys.push(i === 0 ? binaryPropertyName : `${binaryPropertyName}_${i}`);
459
+ }
396
460
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), `No valid binary files found in any items. Please make sure binary data exists.\n\n` +
397
461
  `Input data structure:\n${structureInfo}\n\n` +
462
+ `Binary data search locations:\n` +
463
+ `1. In list item: item.binary["${binaryPropertyName}"] or item["${binaryPropertyName}"]\n` +
464
+ `2. In aggregated item: aggregatedItem.binary["${binaryPropertyName}"] (for first item)\n` +
465
+ `3. In aggregated item: aggregatedItem.binary["${binaryPropertyName}_${1}"] (for second item), etc.\n\n` +
466
+ `Found binary keys in aggregatedItem.binary: ${aggregatedBinaryKeys.length > 0 ? aggregatedBinaryKeys.join(', ') : 'none'}\n` +
467
+ `Expected binary keys for first 10 items: ${expectedBinaryKeys.join(', ')}\n\n` +
398
468
  `Tips:\n` +
399
- `- Check that binaryPropertyName "${binaryPropertyName}" matches the property name in your items\n` +
400
- `- Verify that binary data exists in the items (check the "binary" property or the specified property)\n` +
401
- `- Make sure binary data has a "data" field with base64 content\n` +
402
- `- If items have a "binary" property, binary data should be in binary["${binaryPropertyName}"]\n` +
403
- `- If items have binary data directly, it should be in item["${binaryPropertyName}"]`);
469
+ `- Check that binaryPropertyName "${binaryPropertyName}" matches the property name\n` +
470
+ `- 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`);
404
472
  }
405
473
  // Добавляем опции в зависимости от операции
406
474
  if (operation === 'office' || operation === 'html' || operation === 'markdown') {
@@ -447,13 +515,63 @@ class Gotenberg {
447
515
  }
448
516
  // Отправляем запрос в Gotenberg
449
517
  const url = `${gotenbergUrl.replace(/\/$/, '')}${endpoint}`;
450
- const response = await this.helpers.httpRequest({
451
- method: 'POST',
452
- url,
453
- body: formData,
454
- returnFullResponse: true,
455
- headers: formData.getHeaders(),
456
- });
518
+ let response;
519
+ try {
520
+ response = await this.helpers.httpRequest({
521
+ method: 'POST',
522
+ url,
523
+ body: formData,
524
+ returnFullResponse: true,
525
+ headers: formData.getHeaders(),
526
+ });
527
+ }
528
+ catch (error) {
529
+ // Улучшенная обработка ошибок подключения
530
+ if (error.code === 'ECONNREFUSED') {
531
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Cannot connect to Gotenberg server at ${url}. ` +
532
+ `Connection refused. Please check:\n` +
533
+ `- Is Gotenberg server running?\n` +
534
+ `- Is the URL correct? (current: ${gotenbergUrl})\n` +
535
+ `- Is the port correct? (current endpoint: ${url})\n` +
536
+ `- If using IPv6 (::1), try using 127.0.0.1 or localhost instead`);
537
+ }
538
+ else if (error.code === 'ENOTFOUND' || error.code === 'EAI_AGAIN') {
539
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Cannot resolve Gotenberg server hostname. ` +
540
+ `Please check that the URL is correct: ${gotenbergUrl}`);
541
+ }
542
+ else if (error.code === 'ETIMEDOUT' || error.code === 'ECONNABORTED') {
543
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Connection to Gotenberg server timed out at ${url}. ` +
544
+ `Please check that the server is accessible and not overloaded.`);
545
+ }
546
+ else if (error.response) {
547
+ // Ошибка от сервера (HTTP ошибка)
548
+ const statusCode = error.response.status || error.response.statusCode;
549
+ let errorText;
550
+ if (error.response.data) {
551
+ if (Buffer.isBuffer(error.response.data)) {
552
+ errorText = error.response.data.toString('utf-8');
553
+ }
554
+ else if (typeof error.response.data === 'string') {
555
+ errorText = error.response.data;
556
+ }
557
+ else {
558
+ errorText = JSON.stringify(error.response.data);
559
+ }
560
+ }
561
+ else {
562
+ errorText = error.message || 'Unknown error';
563
+ }
564
+ throw new n8n_workflow_2.NodeApiError(this.getNode(), {
565
+ message: `Gotenberg API error: ${statusCode}`,
566
+ description: errorText,
567
+ });
568
+ }
569
+ else {
570
+ // Другие ошибки
571
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Error connecting to Gotenberg server: ${error.message || String(error)}. ` +
572
+ `URL: ${url}`);
573
+ }
574
+ }
457
575
  // Проверяем статус ответа
458
576
  if (response.statusCode && response.statusCode >= 400) {
459
577
  let errorText;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-sotoros-gotenberg",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "n8n custom node for Gotenberg integration with binary data support",
5
5
  "keywords": [
6
6
  "n8n-community-node",