n8n-nodes-tareno 1.0.0 → 1.2.0

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.
@@ -1,5 +1,11 @@
1
- import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
1
+ import { IExecuteFunctions, ILoadOptionsFunctions, INodeExecutionData, INodePropertyOptions, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
2
  export declare class Tareno implements INodeType {
3
3
  description: INodeTypeDescription;
4
+ methods: {
5
+ loadOptions: {
6
+ getAccounts(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
7
+ getPinterestBoards(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
8
+ };
9
+ };
4
10
  execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
11
  }
@@ -35,6 +35,11 @@ class Tareno {
35
35
  value: 'post',
36
36
  description: 'Create and manage social media posts',
37
37
  },
38
+ {
39
+ name: 'Media Library',
40
+ value: 'media',
41
+ description: 'Send content to Tareno Media Library',
42
+ },
38
43
  {
39
44
  name: 'Account',
40
45
  value: 'account',
@@ -77,42 +82,57 @@ class Tareno {
77
82
  ],
78
83
  default: 'publish',
79
84
  },
80
- // Platform Selection
85
+ // Account Selection (Dynamic from API)
81
86
  {
82
- displayName: 'Platform',
83
- name: 'platform',
87
+ displayName: 'Account',
88
+ name: 'accountId',
84
89
  type: 'options',
85
90
  required: true,
91
+ typeOptions: {
92
+ loadOptionsMethod: 'getAccounts',
93
+ },
94
+ displayOptions: {
95
+ show: {
96
+ resource: ['post'],
97
+ },
98
+ },
99
+ default: '',
100
+ description: 'Select a connected social media account',
101
+ },
102
+ // Format Selection for Instagram/Facebook
103
+ {
104
+ displayName: 'Format',
105
+ name: 'format',
106
+ type: 'options',
86
107
  displayOptions: {
87
108
  show: {
88
109
  resource: ['post'],
89
110
  },
90
111
  },
91
112
  options: [
92
- { name: 'Instagram', value: 'instagram' },
93
- { name: 'Facebook', value: 'facebook' },
94
- { name: 'YouTube', value: 'youtube' },
95
- { name: 'TikTok', value: 'tiktok' },
96
- { name: 'Twitter/X', value: 'twitter' },
97
- { name: 'Threads', value: 'threads' },
98
- { name: 'Pinterest', value: 'pinterest' },
113
+ { name: 'Feed Post', value: 'post' },
114
+ { name: 'Story', value: 'story' },
115
+ { name: 'Reel', value: 'reel' },
99
116
  ],
100
- default: 'instagram',
101
- description: 'The platform to post to',
117
+ default: 'post',
118
+ description: 'Content format (applies to Instagram/Facebook)',
102
119
  },
103
- // Account ID
120
+ // Pinterest Board Selection
104
121
  {
105
- displayName: 'Account ID',
106
- name: 'accountId',
107
- type: 'string',
108
- required: true,
122
+ displayName: 'Pinterest Board',
123
+ name: 'pinterestBoard',
124
+ type: 'options',
125
+ typeOptions: {
126
+ loadOptionsMethod: 'getPinterestBoards',
127
+ loadOptionsDependsOn: ['accountId'],
128
+ },
109
129
  displayOptions: {
110
130
  show: {
111
131
  resource: ['post'],
112
132
  },
113
133
  },
114
134
  default: '',
115
- description: 'The ID of your connected social account. Use "Get Accounts" to find this.',
135
+ description: 'Select a Pinterest board (only for Pinterest accounts)',
116
136
  },
117
137
  // Post Text
118
138
  {
@@ -189,13 +209,6 @@ class Tareno {
189
209
  default: '',
190
210
  description: 'Description for YouTube videos',
191
211
  },
192
- {
193
- displayName: 'Pinterest Board',
194
- name: 'pinterestBoard',
195
- type: 'string',
196
- default: '',
197
- description: 'Pinterest board name or ID',
198
- },
199
212
  {
200
213
  displayName: 'Pinterest Link',
201
214
  name: 'pinterestLink',
@@ -206,6 +219,56 @@ class Tareno {
206
219
  ],
207
220
  },
208
221
  // ========================
222
+ // MEDIA LIBRARY OPERATIONS
223
+ // ========================
224
+ {
225
+ displayName: 'Operation',
226
+ name: 'operation',
227
+ type: 'options',
228
+ noDataExpression: true,
229
+ displayOptions: {
230
+ show: {
231
+ resource: ['media'],
232
+ },
233
+ },
234
+ options: [
235
+ {
236
+ name: 'Upload',
237
+ value: 'upload',
238
+ description: 'Upload media to Tareno library',
239
+ action: 'Upload to media library',
240
+ },
241
+ ],
242
+ default: 'upload',
243
+ },
244
+ // Media URL for library
245
+ {
246
+ displayName: 'Media URL',
247
+ name: 'mediaUrl',
248
+ type: 'string',
249
+ required: true,
250
+ displayOptions: {
251
+ show: {
252
+ resource: ['media'],
253
+ },
254
+ },
255
+ default: '',
256
+ description: 'URL of the media to upload to your library',
257
+ },
258
+ // Media name for library
259
+ {
260
+ displayName: 'File Name',
261
+ name: 'fileName',
262
+ type: 'string',
263
+ displayOptions: {
264
+ show: {
265
+ resource: ['media'],
266
+ },
267
+ },
268
+ default: '',
269
+ description: 'Optional name for the file',
270
+ },
271
+ // ========================
209
272
  // ACCOUNT OPERATIONS
210
273
  // ========================
211
274
  {
@@ -253,6 +316,75 @@ class Tareno {
253
316
  },
254
317
  ],
255
318
  };
319
+ this.methods = {
320
+ loadOptions: {
321
+ // Dynamically load connected accounts from API
322
+ async getAccounts() {
323
+ const credentials = await this.getCredentials('tarenoApi');
324
+ const baseUrl = credentials.baseUrl || 'https://tareno.co';
325
+ try {
326
+ const response = await this.helpers.httpRequest({
327
+ method: 'GET',
328
+ url: `${baseUrl}/api/external/accounts`,
329
+ headers: {
330
+ 'X-Tareno-API-Key': credentials.apiKey,
331
+ },
332
+ json: true,
333
+ });
334
+ if (response.accounts && Array.isArray(response.accounts)) {
335
+ return response.accounts.map((account) => {
336
+ const platformName = account.platform.charAt(0).toUpperCase() + account.platform.slice(1);
337
+ const displayName = account.username || account.displayName || 'Unknown';
338
+ return {
339
+ name: `${platformName} - ${displayName}`,
340
+ value: account.id,
341
+ description: `Platform: ${account.platform}`,
342
+ };
343
+ });
344
+ }
345
+ return [];
346
+ }
347
+ catch (error) {
348
+ console.error('Failed to load accounts:', error);
349
+ return [
350
+ {
351
+ name: 'Error loading accounts - check API key',
352
+ value: '',
353
+ },
354
+ ];
355
+ }
356
+ },
357
+ // Dynamically load Pinterest boards
358
+ async getPinterestBoards() {
359
+ const credentials = await this.getCredentials('tarenoApi');
360
+ const baseUrl = credentials.baseUrl || 'https://tareno.co';
361
+ const accountId = this.getNodeParameter('accountId', '');
362
+ if (!accountId) {
363
+ return [{ name: 'Select an account first', value: '' }];
364
+ }
365
+ try {
366
+ const response = await this.helpers.httpRequest({
367
+ method: 'GET',
368
+ url: `${baseUrl}/api/external/pinterest/boards?accountId=${accountId}`,
369
+ headers: {
370
+ 'X-Tareno-API-Key': credentials.apiKey,
371
+ },
372
+ json: true,
373
+ });
374
+ if (response.boards && Array.isArray(response.boards)) {
375
+ return response.boards.map((board) => ({
376
+ name: board.name,
377
+ value: board.id,
378
+ }));
379
+ }
380
+ return [{ name: 'No boards found (or not a Pinterest account)', value: '' }];
381
+ }
382
+ catch (error) {
383
+ return [{ name: 'Not a Pinterest account', value: '' }];
384
+ }
385
+ },
386
+ },
387
+ };
256
388
  }
257
389
  async execute() {
258
390
  const items = this.getInputData();
@@ -268,8 +400,9 @@ class Tareno {
268
400
  // POST RESOURCE
269
401
  // ========================
270
402
  if (resource === 'post') {
271
- const platform = this.getNodeParameter('platform', i);
272
403
  const accountId = this.getNodeParameter('accountId', i);
404
+ const format = this.getNodeParameter('format', i);
405
+ const pinterestBoard = this.getNodeParameter('pinterestBoard', i);
273
406
  const text = this.getNodeParameter('text', i);
274
407
  const mediaUrlsRaw = this.getNodeParameter('mediaUrls', i);
275
408
  const additionalOptions = this.getNodeParameter('additionalOptions', i);
@@ -277,20 +410,23 @@ class Tareno {
277
410
  ? mediaUrlsRaw.split(',').map(url => url.trim()).filter(Boolean)
278
411
  : [];
279
412
  const body = {
280
- platform,
281
413
  accountId,
282
414
  text,
283
415
  mediaUrls,
416
+ metadata: {
417
+ format,
418
+ ...additionalOptions,
419
+ },
284
420
  };
421
+ // Add Pinterest board if selected
422
+ if (pinterestBoard) {
423
+ body.metadata.pinterestBoard = pinterestBoard;
424
+ }
285
425
  // Add schedule time if scheduling
286
426
  if (operation === 'schedule') {
287
427
  const scheduledAt = this.getNodeParameter('scheduledAt', i);
288
428
  body.scheduledAt = scheduledAt;
289
429
  }
290
- // Add metadata for platform-specific options
291
- if (Object.keys(additionalOptions).length > 0) {
292
- body.metadata = additionalOptions;
293
- }
294
430
  responseData = await this.helpers.httpRequest({
295
431
  method: 'POST',
296
432
  url: `${baseUrl}/api/external/publish`,
@@ -303,6 +439,26 @@ class Tareno {
303
439
  });
304
440
  }
305
441
  // ========================
442
+ // MEDIA LIBRARY RESOURCE
443
+ // ========================
444
+ if (resource === 'media' && operation === 'upload') {
445
+ const mediaUrl = this.getNodeParameter('mediaUrl', i);
446
+ const fileName = this.getNodeParameter('fileName', i);
447
+ responseData = await this.helpers.httpRequest({
448
+ method: 'POST',
449
+ url: `${baseUrl}/api/external/media`,
450
+ headers: {
451
+ 'Content-Type': 'application/json',
452
+ 'X-Tareno-API-Key': credentials.apiKey,
453
+ },
454
+ body: {
455
+ mediaUrl,
456
+ fileName: fileName || undefined,
457
+ },
458
+ json: true,
459
+ });
460
+ }
461
+ // ========================
306
462
  // ACCOUNT RESOURCE
307
463
  // ========================
308
464
  if (resource === 'account' && operation === 'getAll') {
@@ -1,6 +1,15 @@
1
1
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="none">
2
- <rect width="100" height="100" rx="20" fill="#10B981"/>
3
- <path d="M30 35h40v5H30zM30 47h35v5H30zM30 59h40v5H30z" fill="white"/>
4
- <circle cx="70" cy="70" r="15" fill="white"/>
5
- <path d="M65 70l5 5 8-10" stroke="#10B981" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
2
+ <defs>
3
+ <linearGradient id="tarenoGradient" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" style="stop-color:#10B981"/>
5
+ <stop offset="50%" style="stop-color:#059669"/>
6
+ <stop offset="100%" style="stop-color:#047857"/>
7
+ </linearGradient>
8
+ </defs>
9
+ <!-- Background -->
10
+ <rect width="100" height="100" rx="20" fill="url(#tarenoGradient)"/>
11
+ <!-- T letter stylized -->
12
+ <path d="M25 25 H75 V35 H55 V75 H45 V35 H25 Z" fill="white"/>
13
+ <!-- Decorative check/arrow -->
14
+ <path d="M60 55 L70 65 L85 45" stroke="white" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" fill="none" opacity="0.9"/>
6
15
  </svg>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-tareno",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "N8N community nodes for Tareno - Social Media Management Platform",
5
5
  "license": "MIT",
6
6
  "homepage": "https://tareno.co",