n8n-nodes-tareno 1.3.0 → 1.3.2

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.
@@ -30,26 +30,9 @@ class Tareno {
30
30
  type: 'options',
31
31
  noDataExpression: true,
32
32
  options: [
33
- {
34
- name: 'Post',
35
- value: 'post',
36
- description: 'Create and manage social media posts',
37
- },
38
- {
39
- name: 'Media Library',
40
- value: 'media',
41
- description: 'Send content to Tareno Media Library',
42
- },
43
- {
44
- name: 'Account',
45
- value: 'account',
46
- description: 'Get connected social media accounts',
47
- },
48
- {
49
- name: 'Usage',
50
- value: 'usage',
51
- description: 'Get API usage statistics',
52
- },
33
+ { name: 'Post', value: 'post', description: 'Create and manage social media posts' },
34
+ { name: 'Media Library', value: 'media', description: 'Send content to Tareno Media Library' },
35
+ { name: 'Account', value: 'account', description: 'Get connected social media accounts' },
53
36
  ],
54
37
  default: 'post',
55
38
  },
@@ -61,28 +44,33 @@ class Tareno {
61
44
  name: 'operation',
62
45
  type: 'options',
63
46
  noDataExpression: true,
64
- displayOptions: {
65
- show: {
66
- resource: ['post'],
67
- },
68
- },
47
+ displayOptions: { show: { resource: ['post'] } },
69
48
  options: [
70
- {
71
- name: 'Publish',
72
- value: 'publish',
73
- description: 'Publish a post immediately',
74
- action: 'Publish a post',
75
- },
76
- {
77
- name: 'Schedule',
78
- value: 'schedule',
79
- description: 'Schedule a post for later',
80
- action: 'Schedule a post',
81
- },
49
+ { name: 'Publish', value: 'publish', action: 'Publish a post' },
50
+ { name: 'Schedule', value: 'schedule', action: 'Schedule a post' },
82
51
  ],
83
52
  default: 'publish',
84
53
  },
85
- // Account Selection (Dynamic from API)
54
+ // Platform Filter (Helper for UI)
55
+ {
56
+ displayName: 'Platform (UI Filter)',
57
+ name: 'platformFilter',
58
+ type: 'options',
59
+ displayOptions: { show: { resource: ['post'] } },
60
+ options: [
61
+ { name: 'All / Auto-detect', value: 'all' },
62
+ { name: 'Instagram', value: 'instagram' },
63
+ { name: 'Facebook', value: 'facebook' },
64
+ { name: 'YouTube', value: 'youtube' },
65
+ { name: 'TikTok', value: 'tiktok' },
66
+ { name: 'Pinterest', value: 'pinterest' },
67
+ { name: 'Twitter / X', value: 'twitter' },
68
+ { name: 'Threads', value: 'threads' },
69
+ ],
70
+ default: 'all',
71
+ description: 'Filter accounts and show specific fields for this platform',
72
+ },
73
+ // Account Selection (Dynamic)
86
74
  {
87
75
  displayName: 'Account',
88
76
  name: 'accountId',
@@ -90,16 +78,12 @@ class Tareno {
90
78
  required: true,
91
79
  typeOptions: {
92
80
  loadOptionsMethod: 'getAccounts',
81
+ loadOptionsDependsOn: ['platformFilter'],
93
82
  },
94
- displayOptions: {
95
- show: {
96
- resource: ['post'],
97
- },
98
- },
83
+ displayOptions: { show: { resource: ['post'] } },
99
84
  default: '',
100
- description: 'Select a connected social media account',
101
85
  },
102
- // Format Selection for Instagram/Facebook
86
+ // Format Selection (Only for Meta)
103
87
  {
104
88
  displayName: 'Format',
105
89
  name: 'format',
@@ -107,7 +91,8 @@ class Tareno {
107
91
  displayOptions: {
108
92
  show: {
109
93
  resource: ['post'],
110
- },
94
+ platformFilter: ['instagram', 'facebook', 'all']
95
+ }
111
96
  },
112
97
  options: [
113
98
  { name: 'Feed Post', value: 'post' },
@@ -115,9 +100,9 @@ class Tareno {
115
100
  { name: 'Reel', value: 'reel' },
116
101
  ],
117
102
  default: 'post',
118
- description: 'Content format (applies to Instagram/Facebook)',
103
+ description: 'Format for Instagram/Facebook (ignored for others)',
119
104
  },
120
- // Pinterest Board Selection
105
+ // Pinterest Board (Only for Pinterest)
121
106
  {
122
107
  displayName: 'Pinterest Board',
123
108
  name: 'pinterestBoard',
@@ -129,93 +114,62 @@ class Tareno {
129
114
  displayOptions: {
130
115
  show: {
131
116
  resource: ['post'],
132
- },
117
+ platformFilter: ['pinterest', 'all']
118
+ }
133
119
  },
134
120
  default: '',
135
- description: 'Select a Pinterest board (only for Pinterest accounts)',
121
+ description: 'Select a Pinterest board',
136
122
  },
137
- // Post Text
138
123
  {
139
124
  displayName: 'Text/Caption',
140
125
  name: 'text',
141
126
  type: 'string',
142
- typeOptions: {
143
- rows: 4,
144
- },
127
+ typeOptions: { rows: 4 },
128
+ displayOptions: { show: { resource: ['post'] } },
129
+ default: '',
130
+ },
131
+ // Video Title (Only for YouTube)
132
+ {
133
+ displayName: 'Video Title',
134
+ name: 'videoTitle',
135
+ type: 'string',
145
136
  displayOptions: {
146
137
  show: {
147
138
  resource: ['post'],
148
- },
139
+ platformFilter: ['youtube']
140
+ }
149
141
  },
150
142
  default: '',
151
- description: 'The text content of your post',
143
+ placeholder: 'My Great Video',
152
144
  },
153
- // Media URLs
154
145
  {
155
146
  displayName: 'Media URLs',
156
147
  name: 'mediaUrls',
157
148
  type: 'string',
158
- typeOptions: {
159
- rows: 2,
160
- },
161
- displayOptions: {
162
- show: {
163
- resource: ['post'],
164
- },
165
- },
149
+ typeOptions: { rows: 2 },
150
+ displayOptions: { show: { resource: ['post'] } },
166
151
  default: '',
167
- description: 'Comma-separated list of media URLs (images/videos)',
152
+ description: 'Comma-separated list of media URLs',
168
153
  },
169
- // Schedule Time (only for schedule operation)
170
154
  {
171
155
  displayName: 'Schedule Time',
172
156
  name: 'scheduledAt',
173
157
  type: 'dateTime',
174
158
  required: true,
175
- displayOptions: {
176
- show: {
177
- resource: ['post'],
178
- operation: ['schedule'],
179
- },
180
- },
159
+ displayOptions: { show: { resource: ['post'], operation: ['schedule'] } },
181
160
  default: '',
182
- description: 'When to publish the post (ISO 8601 format)',
183
161
  },
184
- // Additional Options
185
162
  {
186
163
  displayName: 'Additional Options',
187
164
  name: 'additionalOptions',
188
165
  type: 'collection',
189
166
  placeholder: 'Add Option',
190
- displayOptions: {
191
- show: {
192
- resource: ['post'],
193
- },
194
- },
167
+ displayOptions: { show: { resource: ['post'] } },
195
168
  default: {},
196
169
  options: [
197
- {
198
- displayName: 'YouTube Title',
199
- name: 'youtubeTitle',
200
- type: 'string',
201
- default: '',
202
- description: 'Title for YouTube videos',
203
- },
204
- {
205
- displayName: 'YouTube Description',
206
- name: 'youtubeDescription',
207
- type: 'string',
208
- typeOptions: { rows: 3 },
209
- default: '',
210
- description: 'Description for YouTube videos',
211
- },
212
- {
213
- displayName: 'Pinterest Link',
214
- name: 'pinterestLink',
215
- type: 'string',
216
- default: '',
217
- description: 'URL to link the pin to',
218
- },
170
+ { displayName: 'YouTube Description', name: 'youtubeDescription', type: 'string', typeOptions: { rows: 3 }, default: '' },
171
+ { displayName: 'Pinterest Link', name: 'pinterestLink', type: 'string', default: '' },
172
+ { displayName: 'Disable Comments', name: 'disableComments', type: 'boolean', default: false },
219
173
  ],
220
174
  },
221
175
  // ========================
@@ -226,194 +180,97 @@ class Tareno {
226
180
  name: 'operation',
227
181
  type: 'options',
228
182
  noDataExpression: true,
229
- displayOptions: {
230
- show: {
231
- resource: ['media'],
232
- },
233
- },
183
+ displayOptions: { show: { resource: ['media'] } },
234
184
  options: [
235
- {
236
- name: 'Upload',
237
- value: 'upload',
238
- description: 'Upload media to Tareno library',
239
- action: 'Upload to media library',
240
- },
185
+ { name: 'Upload', value: 'upload', action: 'Upload to media library' },
241
186
  ],
242
187
  default: 'upload',
243
188
  },
244
- // Upload Mode
245
189
  {
246
190
  displayName: 'Upload Mode',
247
191
  name: 'uploadMode',
248
192
  type: 'options',
249
- displayOptions: {
250
- show: {
251
- resource: ['media'],
252
- },
253
- },
193
+ displayOptions: { show: { resource: ['media'] } },
254
194
  options: [
255
195
  { name: 'File (Binary)', value: 'file' },
256
196
  { name: 'URL', value: 'url' },
257
197
  ],
258
198
  default: 'file',
259
- description: 'Whether to upload a binary file or provide a URL',
260
199
  },
261
- // Binary Property (when Mode is File)
262
200
  {
263
201
  displayName: 'Input Data Field Name',
264
202
  name: 'binaryPropertyName',
265
203
  type: 'string',
266
204
  default: 'data',
267
205
  required: true,
268
- displayOptions: {
269
- show: {
270
- resource: ['media'],
271
- uploadMode: ['file'],
272
- },
273
- },
274
- description: 'The name of the binary property to upload',
206
+ displayOptions: { show: { resource: ['media'], uploadMode: ['file'] } },
275
207
  },
276
- // Media URL (when Mode is URL)
277
208
  {
278
209
  displayName: 'Media URL',
279
210
  name: 'mediaUrl',
280
211
  type: 'string',
281
212
  required: true,
282
- displayOptions: {
283
- show: {
284
- resource: ['media'],
285
- uploadMode: ['url'],
286
- },
287
- },
213
+ displayOptions: { show: { resource: ['media'], uploadMode: ['url'] } },
288
214
  default: '',
289
- description: 'URL of the media to upload to your library',
290
215
  },
291
- // Media name for library
292
216
  {
293
217
  displayName: 'File Name',
294
218
  name: 'fileName',
295
219
  type: 'string',
296
- displayOptions: {
297
- show: {
298
- resource: ['media'],
299
- },
300
- },
220
+ displayOptions: { show: { resource: ['media'] } },
301
221
  default: '',
302
- description: 'Optional name for the file (e.g., example.jpg)',
303
- },
304
- // ========================
305
- // ACCOUNT OPERATIONS
306
- // ========================
307
- {
308
- displayName: 'Operation',
309
- name: 'operation',
310
- type: 'options',
311
- noDataExpression: true,
312
- displayOptions: {
313
- show: {
314
- resource: ['account'],
315
- },
316
- },
317
- options: [
318
- {
319
- name: 'Get All',
320
- value: 'getAll',
321
- description: 'Get all connected accounts',
322
- action: 'Get all accounts',
323
- },
324
- ],
325
- default: 'getAll',
326
- },
327
- // ========================
328
- // USAGE OPERATIONS
329
- // ========================
330
- {
331
- displayName: 'Operation',
332
- name: 'operation',
333
- type: 'options',
334
- noDataExpression: true,
335
- displayOptions: {
336
- show: {
337
- resource: ['usage'],
338
- },
339
- },
340
- options: [
341
- {
342
- name: 'Get Statistics',
343
- value: 'getStats',
344
- description: 'Get API usage statistics',
345
- action: 'Get usage statistics',
346
- },
347
- ],
348
- default: 'getStats',
349
222
  },
350
223
  ],
351
224
  };
352
225
  this.methods = {
353
226
  loadOptions: {
354
- // Dynamically load connected accounts from API
355
227
  async getAccounts() {
356
228
  const credentials = await this.getCredentials('tarenoApi');
357
229
  const baseUrl = credentials.baseUrl || 'https://tareno.co';
230
+ const platformFilter = this.getNodeParameter('platformFilter', 'all');
358
231
  try {
359
232
  const response = await this.helpers.httpRequest({
360
233
  method: 'GET',
361
234
  url: `${baseUrl}/api/external/accounts`,
362
- headers: {
363
- 'X-Tareno-API-Key': credentials.apiKey,
364
- },
235
+ headers: { 'X-Tareno-API-Key': credentials.apiKey },
365
236
  json: true,
366
237
  });
367
238
  if (response.accounts && Array.isArray(response.accounts)) {
368
- return response.accounts.map((account) => {
369
- const platformName = account.platform.charAt(0).toUpperCase() + account.platform.slice(1);
370
- const displayName = account.username || account.displayName || 'Unknown';
371
- return {
372
- name: `${platformName} - ${displayName}`,
373
- value: account.id,
374
- description: `Platform: ${account.platform}`,
375
- };
376
- });
239
+ let filtered = response.accounts;
240
+ if (platformFilter !== 'all') {
241
+ filtered = response.accounts.filter((a) => a.platform === platformFilter);
242
+ }
243
+ return filtered.map((account) => ({
244
+ name: `${account.platform.toUpperCase()} - ${account.username || account.displayName || 'Unknown'}`,
245
+ value: account.id,
246
+ }));
377
247
  }
378
248
  return [];
379
249
  }
380
250
  catch (error) {
381
- console.error('Failed to load accounts:', error);
382
- return [
383
- {
384
- name: 'Error loading accounts - check API key',
385
- value: '',
386
- },
387
- ];
251
+ return [];
388
252
  }
389
253
  },
390
- // Dynamically load Pinterest boards
391
254
  async getPinterestBoards() {
392
255
  const credentials = await this.getCredentials('tarenoApi');
393
256
  const baseUrl = credentials.baseUrl || 'https://tareno.co';
394
257
  const accountId = this.getNodeParameter('accountId', '');
395
- if (!accountId) {
396
- return [{ name: 'Select an account first', value: '' }];
397
- }
258
+ if (!accountId)
259
+ return [];
398
260
  try {
399
261
  const response = await this.helpers.httpRequest({
400
262
  method: 'GET',
401
263
  url: `${baseUrl}/api/external/pinterest/boards?accountId=${accountId}`,
402
- headers: {
403
- 'X-Tareno-API-Key': credentials.apiKey,
404
- },
264
+ headers: { 'X-Tareno-API-Key': credentials.apiKey },
405
265
  json: true,
406
266
  });
407
267
  if (response.boards && Array.isArray(response.boards)) {
408
- return response.boards.map((board) => ({
409
- name: board.name,
410
- value: board.id,
411
- }));
268
+ return response.boards.map((board) => ({ name: board.name, value: board.id }));
412
269
  }
413
- return [{ name: 'No boards found (or not a Pinterest account)', value: '' }];
270
+ return [];
414
271
  }
415
272
  catch (error) {
416
- return [{ name: 'Not a Pinterest account', value: '' }];
273
+ return [];
417
274
  }
418
275
  },
419
276
  },
@@ -429,52 +286,38 @@ class Tareno {
429
286
  const resource = this.getNodeParameter('resource', i);
430
287
  const operation = this.getNodeParameter('operation', i);
431
288
  let responseData;
432
- // ========================
433
- // POST RESOURCE
434
- // ========================
435
289
  if (resource === 'post') {
436
290
  const accountId = this.getNodeParameter('accountId', i);
437
291
  const format = this.getNodeParameter('format', i);
438
- const pinterestBoard = this.getNodeParameter('pinterestBoard', i);
292
+ const pinterestBoard = this.getNodeParameter('pinterestBoard', i, '');
439
293
  const text = this.getNodeParameter('text', i);
440
- const mediaUrlsRaw = this.getNodeParameter('mediaUrls', i);
294
+ const videoTitle = this.getNodeParameter('videoTitle', i, '');
295
+ const mediaUrlsRaw = this.getNodeParameter('mediaUrls', i, '');
441
296
  const additionalOptions = this.getNodeParameter('additionalOptions', i);
442
- const mediaUrls = mediaUrlsRaw
443
- ? mediaUrlsRaw.split(',').map(url => url.trim()).filter(Boolean)
444
- : [];
297
+ const mediaUrls = mediaUrlsRaw ? mediaUrlsRaw.split(',').map(url => url.trim()).filter(Boolean) : [];
445
298
  const body = {
446
299
  accountId,
447
300
  text,
448
301
  mediaUrls,
449
302
  metadata: {
450
303
  format,
451
- ...additionalOptions,
452
- },
304
+ videoTitle,
305
+ ...additionalOptions
306
+ }
453
307
  };
454
- // Add Pinterest board if selected
455
- if (pinterestBoard) {
308
+ if (pinterestBoard)
456
309
  body.metadata.pinterestBoard = pinterestBoard;
457
- }
458
- // Add schedule time if scheduling
459
- if (operation === 'schedule') {
460
- const scheduledAt = this.getNodeParameter('scheduledAt', i);
461
- body.scheduledAt = scheduledAt;
462
- }
310
+ if (operation === 'schedule')
311
+ body.scheduledAt = this.getNodeParameter('scheduledAt', i);
463
312
  responseData = await this.helpers.httpRequest({
464
313
  method: 'POST',
465
314
  url: `${baseUrl}/api/external/publish`,
466
- headers: {
467
- 'Content-Type': 'application/json',
468
- 'X-Tareno-API-Key': credentials.apiKey,
469
- },
315
+ headers: { 'X-Tareno-API-Key': credentials.apiKey },
470
316
  body,
471
317
  json: true,
472
318
  });
473
319
  }
474
- // ========================
475
- // MEDIA LIBRARY RESOURCE
476
- // ========================
477
- if (resource === 'media' && operation === 'upload') {
320
+ else if (resource === 'media' && operation === 'upload') {
478
321
  const uploadMode = this.getNodeParameter('uploadMode', i);
479
322
  const fileName = this.getNodeParameter('fileName', i);
480
323
  if (uploadMode === 'url') {
@@ -482,88 +325,40 @@ class Tareno {
482
325
  responseData = await this.helpers.httpRequest({
483
326
  method: 'POST',
484
327
  url: `${baseUrl}/api/external/media`,
485
- headers: {
486
- 'Content-Type': 'application/json',
487
- 'X-Tareno-API-Key': credentials.apiKey,
488
- },
489
- body: {
490
- mediaUrl,
491
- fileName: fileName || undefined,
492
- },
328
+ headers: { 'X-Tareno-API-Key': credentials.apiKey },
329
+ body: { mediaUrl, fileName: fileName || undefined },
493
330
  json: true,
494
331
  });
495
332
  }
496
333
  else {
497
- // Binary Upload
498
334
  const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i);
499
335
  const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName);
500
336
  const binaryBuffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
501
- // Use FormData for multipart upload
502
- // Note: N8N helpers.httpRequest has native multipart support
503
337
  responseData = await this.helpers.httpRequest({
504
338
  method: 'POST',
505
339
  url: `${baseUrl}/api/external/media`,
506
340
  headers: {
507
341
  'X-Tareno-API-Key': credentials.apiKey,
342
+ 'Content-Type': 'application/octet-stream',
343
+ 'X-File-Name': fileName || binaryData.fileName || 'upload',
344
+ 'X-Content-Type': binaryData.mimeType,
508
345
  },
509
- body: undefined, // ensure body is not set
510
- // @ts-ignore
511
- formData: {
512
- file: {
513
- value: binaryBuffer,
514
- options: {
515
- filename: fileName || binaryData.fileName || 'upload',
516
- contentType: binaryData.mimeType,
517
- },
518
- },
519
- },
520
- json: true,
346
+ body: binaryBuffer,
521
347
  });
348
+ if (typeof responseData === 'string') {
349
+ try {
350
+ responseData = JSON.parse(responseData);
351
+ }
352
+ catch (e) { }
353
+ }
522
354
  }
523
355
  }
524
- // ========================
525
- // ACCOUNT RESOURCE
526
- // ========================
527
- if (resource === 'account' && operation === 'getAll') {
528
- responseData = await this.helpers.httpRequest({
529
- method: 'GET',
530
- url: `${baseUrl}/api/external/accounts`,
531
- headers: {
532
- 'X-Tareno-API-Key': credentials.apiKey,
533
- },
534
- json: true,
535
- });
536
- }
537
- // ========================
538
- // USAGE RESOURCE
539
- // ========================
540
- if (resource === 'usage' && operation === 'getStats') {
541
- responseData = await this.helpers.httpRequest({
542
- method: 'GET',
543
- url: `${baseUrl}/api/external/usage`,
544
- headers: {
545
- 'X-Tareno-API-Key': credentials.apiKey,
546
- },
547
- json: true,
548
- });
549
- }
550
- // Add result to output
551
- if (responseData) {
552
- if (Array.isArray(responseData)) {
553
- returnData.push(...responseData.map(item => ({ json: item })));
554
- }
555
- else {
556
- returnData.push({ json: responseData });
557
- }
558
- }
356
+ if (responseData)
357
+ returnData.push({ json: responseData });
559
358
  }
560
359
  catch (error) {
561
360
  if (this.continueOnFail()) {
562
- returnData.push({
563
- json: {
564
- error: error.message,
565
- },
566
- });
361
+ returnData.push({ json: { error: error.message } });
567
362
  continue;
568
363
  }
569
364
  throw error;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-tareno",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "N8N community nodes for Tareno - Social Media Management Platform",
5
5
  "license": "MIT",
6
6
  "homepage": "https://tareno.co",