n8n-nodes-tareno 1.3.2 → 1.5.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.
@@ -4,7 +4,6 @@ export declare class Tareno implements INodeType {
4
4
  methods: {
5
5
  loadOptions: {
6
6
  getAccounts(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
7
- getPinterestBoards(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
8
7
  };
9
8
  };
10
9
  execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
@@ -11,33 +11,28 @@ class Tareno {
11
11
  version: 1,
12
12
  subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
13
13
  description: 'Publish and manage social media posts via Tareno',
14
- defaults: {
15
- name: 'Tareno',
16
- },
14
+ defaults: { name: 'Tareno' },
17
15
  inputs: ['main'],
18
16
  outputs: ['main'],
19
- credentials: [
20
- {
21
- name: 'tarenoApi',
22
- required: true,
23
- },
24
- ],
17
+ credentials: [{ name: 'tarenoApi', required: true }],
25
18
  properties: [
26
- // Resource Selection
19
+ // ========================
20
+ // RESOURCE SELECTION
21
+ // ========================
27
22
  {
28
23
  displayName: 'Resource',
29
24
  name: 'resource',
30
25
  type: 'options',
31
26
  noDataExpression: true,
32
27
  options: [
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' },
28
+ { name: 'Post', value: 'post', description: 'Create a social media post' },
29
+ { name: 'Media Library', value: 'media', description: 'Upload to Tareno Media Library' },
30
+ { name: 'Account', value: 'account', description: 'Get connected accounts' },
36
31
  ],
37
32
  default: 'post',
38
33
  },
39
34
  // ========================
40
- // POST OPERATIONS
35
+ // POST RESOURCE
41
36
  // ========================
42
37
  {
43
38
  displayName: 'Operation',
@@ -46,19 +41,19 @@ class Tareno {
46
41
  noDataExpression: true,
47
42
  displayOptions: { show: { resource: ['post'] } },
48
43
  options: [
49
- { name: 'Publish', value: 'publish', action: 'Publish a post' },
50
- { name: 'Schedule', value: 'schedule', action: 'Schedule a post' },
44
+ { name: 'Publish Now', value: 'publish', action: 'Publish immediately' },
45
+ { name: 'Schedule', value: 'schedule', action: 'Schedule for later' },
51
46
  ],
52
47
  default: 'publish',
53
48
  },
54
- // Platform Filter (Helper for UI)
49
+ // Account Filter
55
50
  {
56
- displayName: 'Platform (UI Filter)',
51
+ displayName: 'Filter Accounts by Platform',
57
52
  name: 'platformFilter',
58
53
  type: 'options',
59
54
  displayOptions: { show: { resource: ['post'] } },
60
55
  options: [
61
- { name: 'All / Auto-detect', value: 'all' },
56
+ { name: 'Show All', value: 'all' },
62
57
  { name: 'Instagram', value: 'instagram' },
63
58
  { name: 'Facebook', value: 'facebook' },
64
59
  { name: 'YouTube', value: 'youtube' },
@@ -68,9 +63,9 @@ class Tareno {
68
63
  { name: 'Threads', value: 'threads' },
69
64
  ],
70
65
  default: 'all',
71
- description: 'Filter accounts and show specific fields for this platform',
66
+ description: 'Filter the account dropdown (one post = one account)',
72
67
  },
73
- // Account Selection (Dynamic)
68
+ // Account Selection
74
69
  {
75
70
  displayName: 'Account',
76
71
  name: 'accountId',
@@ -82,75 +77,52 @@ class Tareno {
82
77
  },
83
78
  displayOptions: { show: { resource: ['post'] } },
84
79
  default: '',
80
+ description: 'Select ONE account to post to',
85
81
  },
86
- // Format Selection (Only for Meta)
87
- {
88
- displayName: 'Format',
89
- name: 'format',
90
- type: 'options',
91
- displayOptions: {
92
- show: {
93
- resource: ['post'],
94
- platformFilter: ['instagram', 'facebook', 'all']
95
- }
96
- },
97
- options: [
98
- { name: 'Feed Post', value: 'post' },
99
- { name: 'Story', value: 'story' },
100
- { name: 'Reel', value: 'reel' },
101
- ],
102
- default: 'post',
103
- description: 'Format for Instagram/Facebook (ignored for others)',
104
- },
105
- // Pinterest Board (Only for Pinterest)
106
- {
107
- displayName: 'Pinterest Board',
108
- name: 'pinterestBoard',
109
- type: 'options',
110
- typeOptions: {
111
- loadOptionsMethod: 'getPinterestBoards',
112
- loadOptionsDependsOn: ['accountId'],
113
- },
114
- displayOptions: {
115
- show: {
116
- resource: ['post'],
117
- platformFilter: ['pinterest', 'all']
118
- }
119
- },
120
- default: '',
121
- description: 'Select a Pinterest board',
122
- },
82
+ // Text/Caption
123
83
  {
124
- displayName: 'Text/Caption',
84
+ displayName: 'Text / Caption',
125
85
  name: 'text',
126
86
  type: 'string',
127
87
  typeOptions: { rows: 4 },
128
88
  displayOptions: { show: { resource: ['post'] } },
129
89
  default: '',
90
+ description: 'The text content of your post',
130
91
  },
131
- // Video Title (Only for YouTube)
92
+ // Media Source Selection
132
93
  {
133
- displayName: 'Video Title',
134
- name: 'videoTitle',
94
+ displayName: 'Media Source',
95
+ name: 'mediaSource',
96
+ type: 'options',
97
+ displayOptions: { show: { resource: ['post'] } },
98
+ options: [
99
+ { name: 'No Media', value: 'none', description: 'Text-only post' },
100
+ { name: 'Binary File', value: 'binary', description: 'From previous node (e.g., Google Drive)' },
101
+ { name: 'URL(s)', value: 'url', description: 'Already hosted media URLs' },
102
+ ],
103
+ default: 'none',
104
+ },
105
+ // Binary Property (when source is binary)
106
+ {
107
+ displayName: 'Binary Property',
108
+ name: 'binaryPropertyName',
135
109
  type: 'string',
136
- displayOptions: {
137
- show: {
138
- resource: ['post'],
139
- platformFilter: ['youtube']
140
- }
141
- },
142
- default: '',
143
- placeholder: 'My Great Video',
110
+ default: 'data',
111
+ displayOptions: { show: { resource: ['post'], mediaSource: ['binary'] } },
112
+ description: 'Name of the binary property containing the media file',
144
113
  },
114
+ // Media URLs (when source is url)
145
115
  {
146
116
  displayName: 'Media URLs',
147
117
  name: 'mediaUrls',
148
118
  type: 'string',
149
119
  typeOptions: { rows: 2 },
150
- displayOptions: { show: { resource: ['post'] } },
120
+ displayOptions: { show: { resource: ['post'], mediaSource: ['url'] } },
151
121
  default: '',
152
- description: 'Comma-separated list of media URLs',
122
+ placeholder: 'https://example.com/image.jpg, https://example.com/video.mp4',
123
+ description: 'Comma-separated URLs of media files',
153
124
  },
125
+ // Schedule Time
154
126
  {
155
127
  displayName: 'Schedule Time',
156
128
  name: 'scheduledAt',
@@ -159,6 +131,25 @@ class Tareno {
159
131
  displayOptions: { show: { resource: ['post'], operation: ['schedule'] } },
160
132
  default: '',
161
133
  },
134
+ // Format (Only for Instagram/Facebook)
135
+ {
136
+ displayName: 'Format (Instagram/Facebook)',
137
+ name: 'format',
138
+ type: 'options',
139
+ displayOptions: {
140
+ show: {
141
+ resource: ['post'],
142
+ platformFilter: ['instagram', 'facebook']
143
+ }
144
+ },
145
+ options: [
146
+ { name: 'Feed Post', value: 'post' },
147
+ { name: 'Story', value: 'story' },
148
+ { name: 'Reel', value: 'reel' },
149
+ ],
150
+ default: 'post',
151
+ },
152
+ // Additional Options
162
153
  {
163
154
  displayName: 'Additional Options',
164
155
  name: 'additionalOptions',
@@ -167,13 +158,22 @@ class Tareno {
167
158
  displayOptions: { show: { resource: ['post'] } },
168
159
  default: {},
169
160
  options: [
161
+ { displayName: 'YouTube Title', name: 'youtubeTitle', type: 'string', default: '' },
170
162
  { displayName: 'YouTube Description', name: 'youtubeDescription', type: 'string', typeOptions: { rows: 3 }, default: '' },
163
+ { displayName: 'Pinterest Board ID', name: 'pinterestBoard', type: 'string', default: '' },
171
164
  { displayName: 'Pinterest Link', name: 'pinterestLink', type: 'string', default: '' },
165
+ {
166
+ displayName: 'TikTok Privacy', name: 'tiktokPrivacy', type: 'options', options: [
167
+ { name: 'Public', value: 'PUBLIC_TO_EVERYONE' },
168
+ { name: 'Friends', value: 'MUTUAL_FOLLOW_FRIENDS' },
169
+ { name: 'Private', value: 'SELF_ONLY' },
170
+ ], default: 'PUBLIC_TO_EVERYONE'
171
+ },
172
172
  { displayName: 'Disable Comments', name: 'disableComments', type: 'boolean', default: false },
173
173
  ],
174
174
  },
175
175
  // ========================
176
- // MEDIA LIBRARY OPERATIONS
176
+ // MEDIA LIBRARY RESOURCE
177
177
  // ========================
178
178
  {
179
179
  displayName: 'Operation',
@@ -182,7 +182,8 @@ class Tareno {
182
182
  noDataExpression: true,
183
183
  displayOptions: { show: { resource: ['media'] } },
184
184
  options: [
185
- { name: 'Upload', value: 'upload', action: 'Upload to media library' },
185
+ { name: 'Upload', value: 'upload', action: 'Upload file to library' },
186
+ { name: 'List', value: 'list', action: 'List files in library' },
186
187
  ],
187
188
  default: 'upload',
188
189
  },
@@ -190,34 +191,34 @@ class Tareno {
190
191
  displayName: 'Upload Mode',
191
192
  name: 'uploadMode',
192
193
  type: 'options',
193
- displayOptions: { show: { resource: ['media'] } },
194
+ displayOptions: { show: { resource: ['media'], operation: ['upload'] } },
194
195
  options: [
195
- { name: 'File (Binary)', value: 'file' },
196
- { name: 'URL', value: 'url' },
196
+ { name: 'Binary File', value: 'file', description: 'From previous node' },
197
+ { name: 'URL', value: 'url', description: 'Download from URL' },
197
198
  ],
198
199
  default: 'file',
199
200
  },
200
201
  {
201
- displayName: 'Input Data Field Name',
202
- name: 'binaryPropertyName',
202
+ displayName: 'Binary Property',
203
+ name: 'mediaBinaryPropertyName',
203
204
  type: 'string',
204
205
  default: 'data',
205
206
  required: true,
206
- displayOptions: { show: { resource: ['media'], uploadMode: ['file'] } },
207
+ displayOptions: { show: { resource: ['media'], operation: ['upload'], uploadMode: ['file'] } },
207
208
  },
208
209
  {
209
210
  displayName: 'Media URL',
210
211
  name: 'mediaUrl',
211
212
  type: 'string',
212
213
  required: true,
213
- displayOptions: { show: { resource: ['media'], uploadMode: ['url'] } },
214
+ displayOptions: { show: { resource: ['media'], operation: ['upload'], uploadMode: ['url'] } },
214
215
  default: '',
215
216
  },
216
217
  {
217
218
  displayName: 'File Name',
218
219
  name: 'fileName',
219
220
  type: 'string',
220
- displayOptions: { show: { resource: ['media'] } },
221
+ displayOptions: { show: { resource: ['media'], operation: ['upload'] } },
221
222
  default: '',
222
223
  },
223
224
  ],
@@ -236,11 +237,11 @@ class Tareno {
236
237
  json: true,
237
238
  });
238
239
  if (response.accounts && Array.isArray(response.accounts)) {
239
- let filtered = response.accounts;
240
+ let accounts = response.accounts;
240
241
  if (platformFilter !== 'all') {
241
- filtered = response.accounts.filter((a) => a.platform === platformFilter);
242
+ accounts = accounts.filter((a) => a.platform === platformFilter);
242
243
  }
243
- return filtered.map((account) => ({
244
+ return accounts.map((account) => ({
244
245
  name: `${account.platform.toUpperCase()} - ${account.username || account.displayName || 'Unknown'}`,
245
246
  value: account.id,
246
247
  }));
@@ -248,29 +249,7 @@ class Tareno {
248
249
  return [];
249
250
  }
250
251
  catch (error) {
251
- return [];
252
- }
253
- },
254
- async getPinterestBoards() {
255
- const credentials = await this.getCredentials('tarenoApi');
256
- const baseUrl = credentials.baseUrl || 'https://tareno.co';
257
- const accountId = this.getNodeParameter('accountId', '');
258
- if (!accountId)
259
- return [];
260
- try {
261
- const response = await this.helpers.httpRequest({
262
- method: 'GET',
263
- url: `${baseUrl}/api/external/pinterest/boards?accountId=${accountId}`,
264
- headers: { 'X-Tareno-API-Key': credentials.apiKey },
265
- json: true,
266
- });
267
- if (response.boards && Array.isArray(response.boards)) {
268
- return response.boards.map((board) => ({ name: board.name, value: board.id }));
269
- }
270
- return [];
271
- }
272
- catch (error) {
273
- return [];
252
+ return [{ name: 'Error loading accounts', value: '' }];
274
253
  }
275
254
  },
276
255
  },
@@ -286,29 +265,61 @@ class Tareno {
286
265
  const resource = this.getNodeParameter('resource', i);
287
266
  const operation = this.getNodeParameter('operation', i);
288
267
  let responseData;
268
+ // ========================
269
+ // POST
270
+ // ========================
289
271
  if (resource === 'post') {
290
272
  const accountId = this.getNodeParameter('accountId', i);
291
- const format = this.getNodeParameter('format', i);
292
- const pinterestBoard = this.getNodeParameter('pinterestBoard', i, '');
293
273
  const text = this.getNodeParameter('text', i);
294
- const videoTitle = this.getNodeParameter('videoTitle', i, '');
295
- const mediaUrlsRaw = this.getNodeParameter('mediaUrls', i, '');
296
- const additionalOptions = this.getNodeParameter('additionalOptions', i);
297
- const mediaUrls = mediaUrlsRaw ? mediaUrlsRaw.split(',').map(url => url.trim()).filter(Boolean) : [];
274
+ const mediaSource = this.getNodeParameter('mediaSource', i);
275
+ const additionalOptions = this.getNodeParameter('additionalOptions', i, {});
276
+ let format = 'post';
277
+ try {
278
+ format = this.getNodeParameter('format', i);
279
+ }
280
+ catch (e) { }
281
+ let mediaUrls = [];
282
+ // Handle binary upload - first upload to media library, then get URL
283
+ if (mediaSource === 'binary') {
284
+ const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i);
285
+ const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName);
286
+ const binaryBuffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
287
+ // Upload to media library first
288
+ const uploadResponse = await this.helpers.httpRequest({
289
+ method: 'POST',
290
+ url: `${baseUrl}/api/external/media`,
291
+ headers: {
292
+ 'X-Tareno-API-Key': credentials.apiKey,
293
+ 'Content-Type': 'application/octet-stream',
294
+ 'X-File-Name': binaryData.fileName || 'upload',
295
+ 'X-Content-Type': binaryData.mimeType,
296
+ },
297
+ body: binaryBuffer,
298
+ });
299
+ let parsed = uploadResponse;
300
+ if (typeof uploadResponse === 'string') {
301
+ try {
302
+ parsed = JSON.parse(uploadResponse);
303
+ }
304
+ catch (e) { }
305
+ }
306
+ if (parsed.url) {
307
+ mediaUrls = [parsed.url];
308
+ }
309
+ }
310
+ else if (mediaSource === 'url') {
311
+ const mediaUrlsRaw = this.getNodeParameter('mediaUrls', i, '');
312
+ mediaUrls = mediaUrlsRaw ? mediaUrlsRaw.split(',').map(url => url.trim()).filter(Boolean) : [];
313
+ }
298
314
  const body = {
299
315
  accountId,
300
316
  text,
301
317
  mediaUrls,
302
- metadata: {
303
- format,
304
- videoTitle,
305
- ...additionalOptions
306
- }
318
+ metadata: { format, ...additionalOptions }
307
319
  };
308
- if (pinterestBoard)
309
- body.metadata.pinterestBoard = pinterestBoard;
310
- if (operation === 'schedule')
320
+ if (operation === 'schedule') {
311
321
  body.scheduledAt = this.getNodeParameter('scheduledAt', i);
322
+ }
312
323
  responseData = await this.helpers.httpRequest({
313
324
  method: 'POST',
314
325
  url: `${baseUrl}/api/external/publish`,
@@ -317,41 +328,65 @@ class Tareno {
317
328
  json: true,
318
329
  });
319
330
  }
320
- else if (resource === 'media' && operation === 'upload') {
321
- const uploadMode = this.getNodeParameter('uploadMode', i);
322
- const fileName = this.getNodeParameter('fileName', i);
323
- if (uploadMode === 'url') {
324
- const mediaUrl = this.getNodeParameter('mediaUrl', i);
331
+ // ========================
332
+ // MEDIA LIBRARY
333
+ // ========================
334
+ if (resource === 'media') {
335
+ if (operation === 'upload') {
336
+ const uploadMode = this.getNodeParameter('uploadMode', i);
337
+ const fileName = this.getNodeParameter('fileName', i, '');
338
+ if (uploadMode === 'url') {
339
+ const mediaUrl = this.getNodeParameter('mediaUrl', i);
340
+ responseData = await this.helpers.httpRequest({
341
+ method: 'POST',
342
+ url: `${baseUrl}/api/external/media`,
343
+ headers: { 'X-Tareno-API-Key': credentials.apiKey },
344
+ body: { mediaUrl, fileName: fileName || undefined },
345
+ json: true,
346
+ });
347
+ }
348
+ else {
349
+ const binaryPropertyName = this.getNodeParameter('mediaBinaryPropertyName', i);
350
+ const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName);
351
+ const binaryBuffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
352
+ responseData = await this.helpers.httpRequest({
353
+ method: 'POST',
354
+ url: `${baseUrl}/api/external/media`,
355
+ headers: {
356
+ 'X-Tareno-API-Key': credentials.apiKey,
357
+ 'Content-Type': 'application/octet-stream',
358
+ 'X-File-Name': fileName || binaryData.fileName || 'upload',
359
+ 'X-Content-Type': binaryData.mimeType,
360
+ },
361
+ body: binaryBuffer,
362
+ });
363
+ if (typeof responseData === 'string') {
364
+ try {
365
+ responseData = JSON.parse(responseData);
366
+ }
367
+ catch (e) { }
368
+ }
369
+ }
370
+ }
371
+ else if (operation === 'list') {
325
372
  responseData = await this.helpers.httpRequest({
326
- method: 'POST',
373
+ method: 'GET',
327
374
  url: `${baseUrl}/api/external/media`,
328
375
  headers: { 'X-Tareno-API-Key': credentials.apiKey },
329
- body: { mediaUrl, fileName: fileName || undefined },
330
376
  json: true,
331
377
  });
332
378
  }
333
- else {
334
- const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i);
335
- const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName);
336
- const binaryBuffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
337
- responseData = await this.helpers.httpRequest({
338
- method: 'POST',
339
- url: `${baseUrl}/api/external/media`,
340
- headers: {
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,
345
- },
346
- body: binaryBuffer,
347
- });
348
- if (typeof responseData === 'string') {
349
- try {
350
- responseData = JSON.parse(responseData);
351
- }
352
- catch (e) { }
353
- }
354
- }
379
+ }
380
+ // ========================
381
+ // ACCOUNTS
382
+ // ========================
383
+ if (resource === 'account') {
384
+ responseData = await this.helpers.httpRequest({
385
+ method: 'GET',
386
+ url: `${baseUrl}/api/external/accounts`,
387
+ headers: { 'X-Tareno-API-Key': credentials.apiKey },
388
+ json: true,
389
+ });
355
390
  }
356
391
  if (responseData)
357
392
  returnData.push({ json: responseData });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-tareno",
3
- "version": "1.3.2",
3
+ "version": "1.5.0",
4
4
  "description": "N8N community nodes for Tareno - Social Media Management Platform",
5
5
  "license": "MIT",
6
6
  "homepage": "https://tareno.co",