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