n8n-nodes-multi-upload-tool 0.1.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.
@@ -0,0 +1,1105 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MultiUploadTool = void 0;
4
+ const BASE_URL = 'https://api.multi-upload-tool.com/api/v1';
5
+ const PLATFORMS = [
6
+ { name: 'All', value: '' },
7
+ { name: 'Bluesky', value: 'bluesky' },
8
+ { name: 'Facebook', value: 'facebook' },
9
+ { name: 'Instagram', value: 'instagram' },
10
+ { name: 'LinkedIn', value: 'linkedin' },
11
+ { name: 'Pinterest', value: 'pinterest' },
12
+ { name: 'Telegram', value: 'telegram' },
13
+ { name: 'TikTok', value: 'tiktok' },
14
+ { name: 'YouTube', value: 'youtube' },
15
+ ];
16
+ class MultiUploadTool {
17
+ constructor() {
18
+ this.description = {
19
+ displayName: 'Multi Upload Tool',
20
+ name: 'multiUploadTool',
21
+ icon: 'file:multiUploadTool.svg',
22
+ group: ['transform'],
23
+ version: 1,
24
+ subtitle: '={{$parameter["operation"] + " › " + $parameter["resource"]}}',
25
+ description: 'Post videos and images to TikTok, YouTube, Instagram, Facebook, Pinterest, LinkedIn, Bluesky and Telegram',
26
+ defaults: { name: 'Multi Upload Tool' },
27
+ inputs: ['main'],
28
+ outputs: ['main'],
29
+ usableAsTool: true,
30
+ credentials: [
31
+ {
32
+ name: 'multiUploadToolApi',
33
+ required: true,
34
+ },
35
+ ],
36
+ properties: [
37
+ // ─── RESOURCE ──────────────────────────────────────────────────────────
38
+ {
39
+ displayName: 'Resource',
40
+ name: 'resource',
41
+ type: 'options',
42
+ noDataExpression: true,
43
+ options: [
44
+ { name: 'Account', value: 'account' },
45
+ { name: 'Pinterest', value: 'pinterest' },
46
+ { name: 'Short Link', value: 'shortLink' },
47
+ { name: 'Upload', value: 'upload' },
48
+ { name: 'Webhook', value: 'webhook' },
49
+ ],
50
+ default: 'upload',
51
+ },
52
+ // ─── UPLOAD: OPERATIONS ────────────────────────────────────────────────
53
+ {
54
+ displayName: 'Operation',
55
+ name: 'operation',
56
+ type: 'options',
57
+ noDataExpression: true,
58
+ displayOptions: { show: { resource: ['upload'] } },
59
+ options: [
60
+ {
61
+ name: 'Create',
62
+ value: 'create',
63
+ description: 'Upload a video or image to a single connected account',
64
+ action: 'Create an upload',
65
+ },
66
+ {
67
+ name: 'Bulk Create',
68
+ value: 'bulkCreate',
69
+ description: 'Upload to multiple connected accounts at once',
70
+ action: 'Bulk create uploads',
71
+ },
72
+ {
73
+ name: 'Get',
74
+ value: 'get',
75
+ description: 'Retrieve an upload by its ID',
76
+ action: 'Get an upload',
77
+ },
78
+ {
79
+ name: 'Get Many',
80
+ value: 'list',
81
+ description: 'Retrieve a list of uploads',
82
+ action: 'Get many uploads',
83
+ },
84
+ {
85
+ name: 'Update',
86
+ value: 'update',
87
+ description: 'Update the metadata of an existing upload',
88
+ action: 'Update an upload',
89
+ },
90
+ ],
91
+ default: 'create',
92
+ },
93
+ // ─── ACCOUNT: OPERATIONS ──────────────────────────────────────────────
94
+ {
95
+ displayName: 'Operation',
96
+ name: 'operation',
97
+ type: 'options',
98
+ noDataExpression: true,
99
+ displayOptions: { show: { resource: ['account'] } },
100
+ options: [
101
+ {
102
+ name: 'Delete',
103
+ value: 'delete',
104
+ description: 'Disconnect a connected account',
105
+ action: 'Delete an account',
106
+ },
107
+ {
108
+ name: 'Get',
109
+ value: 'get',
110
+ description: 'Retrieve a connected account by its ID',
111
+ action: 'Get an account',
112
+ },
113
+ {
114
+ name: 'Get Many',
115
+ value: 'list',
116
+ description: 'Retrieve a list of connected accounts',
117
+ action: 'Get many accounts',
118
+ },
119
+ {
120
+ name: 'Get LinkedIn Pages',
121
+ value: 'linkedinPages',
122
+ description: 'List LinkedIn Pages/Organizations for a connected account',
123
+ action: 'Get LinkedIn pages',
124
+ },
125
+ ],
126
+ default: 'list',
127
+ },
128
+ // ─── PINTEREST: OPERATIONS ─────────────────────────────────────────────
129
+ {
130
+ displayName: 'Operation',
131
+ name: 'operation',
132
+ type: 'options',
133
+ noDataExpression: true,
134
+ displayOptions: { show: { resource: ['pinterest'] } },
135
+ options: [
136
+ {
137
+ name: 'Get Boards',
138
+ value: 'getBoards',
139
+ description: 'Retrieve boards for a connected Pinterest account',
140
+ action: 'Get pinterest boards',
141
+ },
142
+ ],
143
+ default: 'getBoards',
144
+ },
145
+ // ─── WEBHOOK: OPERATIONS ──────────────────────────────────────────────
146
+ {
147
+ displayName: 'Operation',
148
+ name: 'operation',
149
+ type: 'options',
150
+ noDataExpression: true,
151
+ displayOptions: { show: { resource: ['webhook'] } },
152
+ options: [
153
+ {
154
+ name: 'Create',
155
+ value: 'create',
156
+ description: 'Register a new webhook endpoint',
157
+ action: 'Create a webhook',
158
+ },
159
+ {
160
+ name: 'Delete',
161
+ value: 'delete',
162
+ description: 'Delete a webhook',
163
+ action: 'Delete a webhook',
164
+ },
165
+ {
166
+ name: 'Get Many',
167
+ value: 'list',
168
+ description: 'Retrieve all registered webhooks',
169
+ action: 'Get many webhooks',
170
+ },
171
+ {
172
+ name: 'Test',
173
+ value: 'test',
174
+ description: 'Send a test payload to a webhook',
175
+ action: 'Test a webhook',
176
+ },
177
+ ],
178
+ default: 'list',
179
+ },
180
+ // ─── SHORT LINK: OPERATIONS ────────────────────────────────────────────
181
+ {
182
+ displayName: 'Operation',
183
+ name: 'operation',
184
+ type: 'options',
185
+ noDataExpression: true,
186
+ displayOptions: { show: { resource: ['shortLink'] } },
187
+ options: [
188
+ {
189
+ name: 'Create',
190
+ value: 'create',
191
+ description: 'Create a new short link',
192
+ action: 'Create a short link',
193
+ },
194
+ {
195
+ name: 'Delete',
196
+ value: 'delete',
197
+ description: 'Delete a short link',
198
+ action: 'Delete a short link',
199
+ },
200
+ {
201
+ name: 'Get',
202
+ value: 'get',
203
+ description: 'Retrieve a short link by its ID',
204
+ action: 'Get a short link',
205
+ },
206
+ {
207
+ name: 'Get Many',
208
+ value: 'list',
209
+ description: 'Retrieve all short links',
210
+ action: 'Get many short links',
211
+ },
212
+ {
213
+ name: 'Update',
214
+ value: 'update',
215
+ description: 'Update a short link',
216
+ action: 'Update a short link',
217
+ },
218
+ ],
219
+ default: 'create',
220
+ },
221
+ // ═══════════════════════════════════════════════════════════════════════
222
+ // UPLOAD › CREATE
223
+ // ═══════════════════════════════════════════════════════════════════════
224
+ {
225
+ displayName: 'Account ID',
226
+ name: 'accountId',
227
+ type: 'string',
228
+ required: true,
229
+ displayOptions: { show: { resource: ['upload'], operation: ['create'] } },
230
+ default: '',
231
+ description: 'ID of the connected account to post to',
232
+ },
233
+ {
234
+ displayName: 'Account IDs',
235
+ name: 'accountIds',
236
+ type: 'string',
237
+ required: true,
238
+ displayOptions: { show: { resource: ['upload'], operation: ['bulkCreate'] } },
239
+ default: '',
240
+ description: 'Comma-separated list of connected account IDs (e.g. <code>12,34,56</code>)',
241
+ },
242
+ {
243
+ displayName: 'Binary Property',
244
+ name: 'binaryPropertyName',
245
+ type: 'string',
246
+ displayOptions: {
247
+ show: { resource: ['upload'], operation: ['create', 'bulkCreate'] },
248
+ },
249
+ default: 'data',
250
+ description: 'Name of the binary property from a previous node that contains the file to upload. Leave empty to post without a file.',
251
+ },
252
+ {
253
+ displayName: 'Media Type',
254
+ name: 'mediaType',
255
+ type: 'options',
256
+ displayOptions: {
257
+ show: { resource: ['upload'], operation: ['create', 'bulkCreate'] },
258
+ },
259
+ options: [
260
+ {
261
+ name: 'Auto Detect',
262
+ value: 'auto',
263
+ description: 'Detect from MIME type (recommended)',
264
+ },
265
+ { name: 'Video', value: 'VIDEO' },
266
+ { name: 'Image', value: 'IMAGE' },
267
+ ],
268
+ default: 'auto',
269
+ },
270
+ {
271
+ displayName: 'Additional Fields',
272
+ name: 'additionalFields',
273
+ type: 'collection',
274
+ placeholder: 'Add Field',
275
+ displayOptions: {
276
+ show: { resource: ['upload'], operation: ['create', 'bulkCreate'] },
277
+ },
278
+ default: {},
279
+ options: [
280
+ {
281
+ displayName: 'Caption / Description',
282
+ name: 'description',
283
+ type: 'string',
284
+ typeOptions: { rows: 4 },
285
+ default: '',
286
+ description: 'Caption or description for the post (Instagram, TikTok, Bluesky, LinkedIn, etc.)',
287
+ },
288
+ {
289
+ displayName: 'Title',
290
+ name: 'title',
291
+ type: 'string',
292
+ default: '',
293
+ description: 'Title of the content (YouTube, Pinterest)',
294
+ },
295
+ {
296
+ displayName: 'Tags',
297
+ name: 'tags',
298
+ type: 'string',
299
+ default: '',
300
+ description: 'Comma-separated hashtags or tags (YouTube, TikTok). Do not include the # symbol.',
301
+ },
302
+ {
303
+ displayName: 'Privacy Status',
304
+ name: 'privacyStatus',
305
+ type: 'options',
306
+ options: [
307
+ { name: 'Public', value: 'public' },
308
+ { name: 'Private', value: 'private' },
309
+ { name: 'Unlisted', value: 'unlisted' },
310
+ ],
311
+ default: 'public',
312
+ description: 'Privacy level of the post (YouTube)',
313
+ },
314
+ {
315
+ displayName: 'YouTube Category ID',
316
+ name: 'categoryId',
317
+ type: 'string',
318
+ default: '',
319
+ description: 'YouTube category ID (e.g. 22 for People & Blogs). See the YouTube API for the full list.',
320
+ },
321
+ {
322
+ displayName: 'Schedule Date',
323
+ name: 'schedule_date',
324
+ type: 'dateTime',
325
+ default: '',
326
+ description: 'Schedule the post for a future date/time (TikTok, Instagram, YouTube)',
327
+ },
328
+ {
329
+ displayName: 'TikTok Privacy Level',
330
+ name: 'privacy_level',
331
+ type: 'options',
332
+ options: [
333
+ { name: 'Public to Everyone', value: 'PUBLIC_TO_EVERYONE' },
334
+ { name: 'Mutual Follow Friends', value: 'MUTUAL_FOLLOW_FRIENDS' },
335
+ { name: 'Follower of Creator', value: 'FOLLOWER_OF_CREATOR' },
336
+ { name: 'Self Only', value: 'SELF_ONLY' },
337
+ ],
338
+ default: 'PUBLIC_TO_EVERYONE',
339
+ description: 'Audience visibility (TikTok only)',
340
+ },
341
+ {
342
+ displayName: 'LinkedIn Visibility',
343
+ name: 'visibility',
344
+ type: 'options',
345
+ options: [
346
+ { name: 'Public', value: 'PUBLIC' },
347
+ { name: 'Connections Only', value: 'CONNECTIONS' },
348
+ ],
349
+ default: 'PUBLIC',
350
+ description: 'Post visibility (LinkedIn only)',
351
+ },
352
+ {
353
+ displayName: 'Pinterest Board ID',
354
+ name: 'board_id',
355
+ type: 'string',
356
+ default: '',
357
+ description: 'Board ID to pin to — required for Pinterest posts',
358
+ },
359
+ {
360
+ displayName: 'Facebook / Pinterest Link',
361
+ name: 'link',
362
+ type: 'string',
363
+ default: '',
364
+ description: 'URL to attach to a Facebook link post or Pinterest pin',
365
+ },
366
+ ],
367
+ },
368
+ // ═══════════════════════════════════════════════════════════════════════
369
+ // UPLOAD › GET / UPDATE
370
+ // ═══════════════════════════════════════════════════════════════════════
371
+ {
372
+ displayName: 'Upload ID',
373
+ name: 'uploadId',
374
+ type: 'string',
375
+ required: true,
376
+ displayOptions: { show: { resource: ['upload'], operation: ['get', 'update'] } },
377
+ default: '',
378
+ description: 'ID of the upload',
379
+ },
380
+ {
381
+ displayName: 'Update Fields',
382
+ name: 'updateFields',
383
+ type: 'collection',
384
+ placeholder: 'Add Field',
385
+ displayOptions: { show: { resource: ['upload'], operation: ['update'] } },
386
+ default: {},
387
+ options: [
388
+ {
389
+ displayName: 'Title',
390
+ name: 'title',
391
+ type: 'string',
392
+ default: '',
393
+ },
394
+ {
395
+ displayName: 'Description',
396
+ name: 'description',
397
+ type: 'string',
398
+ typeOptions: { rows: 4 },
399
+ default: '',
400
+ },
401
+ {
402
+ displayName: 'Schedule For',
403
+ name: 'scheduledFor',
404
+ type: 'dateTime',
405
+ default: '',
406
+ },
407
+ ],
408
+ },
409
+ // ═══════════════════════════════════════════════════════════════════════
410
+ // UPLOAD › LIST
411
+ // ═══════════════════════════════════════════════════════════════════════
412
+ {
413
+ displayName: 'Filters',
414
+ name: 'filters',
415
+ type: 'collection',
416
+ placeholder: 'Add Filter',
417
+ displayOptions: { show: { resource: ['upload'], operation: ['list'] } },
418
+ default: {},
419
+ options: [
420
+ {
421
+ displayName: 'Platform',
422
+ name: 'platform',
423
+ type: 'options',
424
+ options: PLATFORMS,
425
+ default: '',
426
+ },
427
+ {
428
+ displayName: 'Status',
429
+ name: 'status',
430
+ type: 'options',
431
+ options: [
432
+ { name: 'All', value: '' },
433
+ { name: 'Completed', value: 'completed' },
434
+ { name: 'Failed', value: 'failed' },
435
+ { name: 'Pending', value: 'pending' },
436
+ { name: 'Processing', value: 'processing' },
437
+ ],
438
+ default: '',
439
+ },
440
+ {
441
+ displayName: 'Search',
442
+ name: 'search',
443
+ type: 'string',
444
+ default: '',
445
+ },
446
+ {
447
+ displayName: 'Page',
448
+ name: 'page',
449
+ type: 'number',
450
+ default: 1,
451
+ },
452
+ {
453
+ displayName: 'Limit',
454
+ name: 'limit',
455
+ type: 'number',
456
+ typeOptions: { maxValue: 100 },
457
+ default: 50,
458
+ },
459
+ ],
460
+ },
461
+ // ═══════════════════════════════════════════════════════════════════════
462
+ // ACCOUNT › GET
463
+ // ═══════════════════════════════════════════════════════════════════════
464
+ {
465
+ displayName: 'Account ID',
466
+ name: 'accountId',
467
+ type: 'string',
468
+ required: true,
469
+ displayOptions: { show: { resource: ['account'], operation: ['get', 'delete', 'linkedinPages'] } },
470
+ default: '',
471
+ description: 'ID of the connected account',
472
+ },
473
+ {
474
+ displayName: 'Filters',
475
+ name: 'filters',
476
+ type: 'collection',
477
+ placeholder: 'Add Filter',
478
+ displayOptions: { show: { resource: ['account'], operation: ['list'] } },
479
+ default: {},
480
+ options: [
481
+ {
482
+ displayName: 'Platform',
483
+ name: 'platform',
484
+ type: 'options',
485
+ options: PLATFORMS,
486
+ default: '',
487
+ },
488
+ {
489
+ displayName: 'Status',
490
+ name: 'status',
491
+ type: 'options',
492
+ options: [
493
+ { name: 'All', value: '' },
494
+ { name: 'Active', value: 'active' },
495
+ { name: 'Revoked', value: 'revoked' },
496
+ ],
497
+ default: '',
498
+ },
499
+ ],
500
+ },
501
+ // ═══════════════════════════════════════════════════════════════════════
502
+ // PINTEREST › GET BOARDS
503
+ // ═══════════════════════════════════════════════════════════════════════
504
+ {
505
+ displayName: 'Account ID',
506
+ name: 'accountId',
507
+ type: 'string',
508
+ required: true,
509
+ displayOptions: { show: { resource: ['pinterest'], operation: ['getBoards'] } },
510
+ default: '',
511
+ description: 'ID of the Pinterest connected account',
512
+ },
513
+ // ═══════════════════════════════════════════════════════════════════════
514
+ // WEBHOOK › CREATE
515
+ // ═══════════════════════════════════════════════════════════════════════
516
+ {
517
+ displayName: 'URL',
518
+ name: 'url',
519
+ type: 'string',
520
+ required: true,
521
+ displayOptions: { show: { resource: ['webhook'], operation: ['create'] } },
522
+ default: '',
523
+ description: 'The HTTPS endpoint that will receive webhook events',
524
+ },
525
+ {
526
+ displayName: 'Events',
527
+ name: 'events',
528
+ type: 'multiOptions',
529
+ required: true,
530
+ displayOptions: { show: { resource: ['webhook'], operation: ['create'] } },
531
+ options: [
532
+ { name: 'Account Connected', value: 'account.connected' },
533
+ { name: 'Account Revoked', value: 'account.revoked' },
534
+ { name: 'Upload Completed', value: 'upload.completed' },
535
+ { name: 'Upload Created', value: 'upload.created' },
536
+ { name: 'Upload Failed', value: 'upload.failed' },
537
+ ],
538
+ default: ['upload.completed', 'upload.failed'],
539
+ description: 'Events that will trigger a delivery to the webhook URL',
540
+ },
541
+ {
542
+ displayName: 'Description',
543
+ name: 'description',
544
+ type: 'string',
545
+ displayOptions: { show: { resource: ['webhook'], operation: ['create'] } },
546
+ default: '',
547
+ description: 'Optional label to identify this webhook',
548
+ },
549
+ // ─── WEBHOOK › DELETE / TEST ───────────────────────────────────────────
550
+ {
551
+ displayName: 'Webhook ID',
552
+ name: 'webhookId',
553
+ type: 'string',
554
+ required: true,
555
+ displayOptions: {
556
+ show: { resource: ['webhook'], operation: ['delete', 'test'] },
557
+ },
558
+ default: '',
559
+ description: 'ID of the webhook',
560
+ },
561
+ // ═══════════════════════════════════════════════════════════════════════
562
+ // SHORT LINK › CREATE
563
+ // ═══════════════════════════════════════════════════════════════════════
564
+ {
565
+ displayName: 'Destination URL',
566
+ name: 'url',
567
+ type: 'string',
568
+ required: true,
569
+ displayOptions: { show: { resource: ['shortLink'], operation: ['create'] } },
570
+ default: '',
571
+ description: 'The long URL that the short link will redirect to',
572
+ },
573
+ {
574
+ displayName: 'Rules',
575
+ name: 'rules',
576
+ type: 'fixedCollection',
577
+ typeOptions: {
578
+ multipleValues: true,
579
+ },
580
+ displayOptions: { show: { resource: ['shortLink'], operation: ['create', 'update'] } },
581
+ default: {},
582
+ options: [
583
+ {
584
+ name: 'rule',
585
+ displayName: 'Rule',
586
+ values: [
587
+ {
588
+ displayName: 'Type',
589
+ name: 'type',
590
+ type: 'options',
591
+ options: [
592
+ { name: 'Geo (Country)', value: 'geo' },
593
+ { name: 'Device', value: 'device' },
594
+ { name: 'Language', value: 'language' },
595
+ { name: 'Time', value: 'time' },
596
+ { name: 'Referer', value: 'referer' },
597
+ ],
598
+ default: 'geo',
599
+ },
600
+ {
601
+ displayName: 'Destination URL',
602
+ name: 'destination',
603
+ type: 'string',
604
+ default: '',
605
+ description: 'Redirect to this URL if conditions are met',
606
+ },
607
+ {
608
+ displayName: 'Country Codes',
609
+ name: 'countryCodes',
610
+ type: 'string',
611
+ displayOptions: { show: { type: ['geo'] } },
612
+ default: '',
613
+ description: 'Comma-separated ISO country codes (e.g. US, FR)',
614
+ },
615
+ {
616
+ displayName: 'Languages',
617
+ name: 'languages',
618
+ type: 'string',
619
+ displayOptions: { show: { type: ['language'] } },
620
+ default: '',
621
+ description: 'Comma-separated language codes (e.g. en, fr)',
622
+ },
623
+ {
624
+ displayName: 'Devices',
625
+ name: 'devices',
626
+ type: 'string',
627
+ displayOptions: { show: { type: ['device'] } },
628
+ default: '',
629
+ description: 'Comma-separated devices (e.g. mobile, desktop, tablet)',
630
+ },
631
+ {
632
+ displayName: 'Referers',
633
+ name: 'referers',
634
+ type: 'string',
635
+ displayOptions: { show: { type: ['referer'] } },
636
+ default: '',
637
+ description: 'Comma-separated referer domains/patterns',
638
+ },
639
+ {
640
+ displayName: 'Start Time',
641
+ name: 'startTime',
642
+ type: 'string',
643
+ displayOptions: { show: { type: ['time'] } },
644
+ default: '',
645
+ description: 'Start time (HH:mm)',
646
+ },
647
+ {
648
+ displayName: 'End Time',
649
+ name: 'endTime',
650
+ type: 'string',
651
+ displayOptions: { show: { type: ['time'] } },
652
+ default: '',
653
+ description: 'End time (HH:mm)',
654
+ },
655
+ {
656
+ displayName: 'Days',
657
+ name: 'days',
658
+ type: 'string',
659
+ displayOptions: { show: { type: ['time'] } },
660
+ default: '',
661
+ description: 'Comma-separated days (0=Sunday, 1=Monday, etc.)',
662
+ },
663
+ {
664
+ displayName: 'Timezone',
665
+ name: 'timezone',
666
+ type: 'string',
667
+ displayOptions: { show: { type: ['time'] } },
668
+ default: '',
669
+ description: 'Timezone (e.g. Europe/Paris)',
670
+ },
671
+ {
672
+ displayName: 'Priority',
673
+ name: 'priority',
674
+ type: 'number',
675
+ default: 0,
676
+ description: 'Higher number = higher priority',
677
+ },
678
+ {
679
+ displayName: 'Is Active',
680
+ name: 'isActive',
681
+ type: 'boolean',
682
+ default: true,
683
+ },
684
+ ],
685
+ },
686
+ ],
687
+ },
688
+ {
689
+ displayName: 'Additional Fields',
690
+ name: 'additionalFields',
691
+ type: 'collection',
692
+ placeholder: 'Add Field',
693
+ displayOptions: { show: { resource: ['shortLink'], operation: ['create'] } },
694
+ default: {},
695
+ options: [
696
+ {
697
+ displayName: 'Slug',
698
+ name: 'slug',
699
+ type: 'string',
700
+ default: '',
701
+ description: 'Custom alias (e.g. "my-video"). Must be unique.',
702
+ },
703
+ {
704
+ displayName: 'Title',
705
+ name: 'title',
706
+ type: 'string',
707
+ default: '',
708
+ },
709
+ {
710
+ displayName: 'Description',
711
+ name: 'description',
712
+ type: 'string',
713
+ default: '',
714
+ },
715
+ {
716
+ displayName: 'Image URL',
717
+ name: 'imageUrl',
718
+ type: 'string',
719
+ default: '',
720
+ description: 'Preview image shown in link previews',
721
+ },
722
+ {
723
+ displayName: 'Use Deeplink',
724
+ name: 'useDeeplink',
725
+ type: 'boolean',
726
+ default: false,
727
+ description: 'Whether to enable app deep linking on mobile',
728
+ },
729
+ {
730
+ displayName: 'Bot Protection',
731
+ name: 'botProtection',
732
+ type: 'boolean',
733
+ default: false,
734
+ description: 'Whether to redirect bots to the Safe URL instead',
735
+ },
736
+ {
737
+ displayName: 'Safe URL (for bots)',
738
+ name: 'safeUrl',
739
+ type: 'string',
740
+ default: '',
741
+ description: 'URL bots are redirected to when Bot Protection is enabled',
742
+ },
743
+ ],
744
+ },
745
+ // ─── SHORT LINK › GET / DELETE / UPDATE ────────────────────────────────
746
+ {
747
+ displayName: 'Short Link ID',
748
+ name: 'linkId',
749
+ type: 'string',
750
+ required: true,
751
+ displayOptions: {
752
+ show: { resource: ['shortLink'], operation: ['get', 'delete', 'update'] },
753
+ },
754
+ default: '',
755
+ description: 'ID of the short link',
756
+ },
757
+ {
758
+ displayName: 'Update Fields',
759
+ name: 'updateFields',
760
+ type: 'collection',
761
+ placeholder: 'Add Field',
762
+ displayOptions: { show: { resource: ['shortLink'], operation: ['update'] } },
763
+ default: {},
764
+ options: [
765
+ {
766
+ displayName: 'Destination URL',
767
+ name: 'url',
768
+ type: 'string',
769
+ default: '',
770
+ },
771
+ {
772
+ displayName: 'Slug',
773
+ name: 'slug',
774
+ type: 'string',
775
+ default: '',
776
+ },
777
+ {
778
+ displayName: 'Title',
779
+ name: 'title',
780
+ type: 'string',
781
+ default: '',
782
+ },
783
+ {
784
+ displayName: 'Description',
785
+ name: 'description',
786
+ type: 'string',
787
+ default: '',
788
+ },
789
+ {
790
+ displayName: 'Image URL',
791
+ name: 'imageUrl',
792
+ type: 'string',
793
+ default: '',
794
+ description: 'Preview image shown in link previews',
795
+ },
796
+ {
797
+ displayName: 'Use Deeplink',
798
+ name: 'useDeeplink',
799
+ type: 'boolean',
800
+ default: false,
801
+ description: 'Whether to enable app deep linking on mobile',
802
+ },
803
+ {
804
+ displayName: 'Bot Protection',
805
+ name: 'botProtection',
806
+ type: 'boolean',
807
+ default: false,
808
+ description: 'Whether to redirect bots to the Safe URL instead',
809
+ },
810
+ {
811
+ displayName: 'Safe URL (for bots)',
812
+ name: 'safeUrl',
813
+ type: 'string',
814
+ default: '',
815
+ description: 'URL bots are redirected to when Bot Protection is enabled',
816
+ },
817
+ ],
818
+ },
819
+ ],
820
+ };
821
+ }
822
+ async execute() {
823
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
824
+ const items = this.getInputData();
825
+ const returnData = [];
826
+ const credentials = await this.getCredentials('multiUploadToolApi');
827
+ const apiToken = credentials.apiToken;
828
+ const defaultHeaders = {
829
+ 'x-api-key': apiToken,
830
+ 'User-Agent': 'n8n-nodes-multi-upload-tool/0.1.0',
831
+ };
832
+ // ── JSON request helper ───────────────────────────────────────────────────
833
+ const apiRequest = async (method, endpoint, body, qs) => {
834
+ const opts = {
835
+ method,
836
+ url: `${BASE_URL}/${endpoint.replace(/^\/+/, '')}`,
837
+ headers: { ...defaultHeaders, 'Content-Type': 'application/json' },
838
+ returnFullResponse: false,
839
+ json: true,
840
+ };
841
+ if (body && Object.keys(body).length)
842
+ opts.body = body;
843
+ if (qs && Object.keys(qs).length)
844
+ opts.qs = qs;
845
+ return this.helpers.httpRequest(opts);
846
+ };
847
+ for (let i = 0; i < items.length; i++) {
848
+ try {
849
+ const resource = this.getNodeParameter('resource', i);
850
+ const operation = this.getNodeParameter('operation', i);
851
+ let responseData = {};
852
+ // ══════════════════════════════════════════════════════════════════
853
+ // UPLOAD
854
+ // ══════════════════════════════════════════════════════════════════
855
+ if (resource === 'upload') {
856
+ if (operation === 'create' || operation === 'bulkCreate') {
857
+ const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i, 'data');
858
+ const mediaType = this.getNodeParameter('mediaType', i);
859
+ const additionalFields = this.getNodeParameter('additionalFields', i, {});
860
+ // ── Resolve file from binary data ─────────────────────────────
861
+ let fileBuffer;
862
+ let fileName = 'file';
863
+ let mimeType = 'application/octet-stream';
864
+ if (binaryPropertyName && ((_a = items[i].binary) === null || _a === void 0 ? void 0 : _a[binaryPropertyName])) {
865
+ const binaryItem = items[i].binary[binaryPropertyName];
866
+ fileBuffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
867
+ fileName = (_b = binaryItem.fileName) !== null && _b !== void 0 ? _b : 'file';
868
+ mimeType = (_c = binaryItem.mimeType) !== null && _c !== void 0 ? _c : 'application/octet-stream';
869
+ }
870
+ const isImage = mediaType === 'IMAGE' ||
871
+ (mediaType === 'auto' && mimeType.startsWith('image/'));
872
+ const fieldName = isImage ? 'photo' : 'video';
873
+ // ── Build form data ───────────────────────────────────────────
874
+ const formData = new FormData();
875
+ if (operation === 'create') {
876
+ const accountId = this.getNodeParameter('accountId', i);
877
+ formData.set('accountId', accountId);
878
+ }
879
+ else {
880
+ const accountIdsRaw = this.getNodeParameter('accountIds', i);
881
+ const ids = accountIdsRaw
882
+ .split(',')
883
+ .map((s) => s.trim())
884
+ .filter(Boolean);
885
+ formData.set('accounts', JSON.stringify(ids));
886
+ }
887
+ if (fileBuffer) {
888
+ const blob = new Blob([fileBuffer], { type: mimeType });
889
+ formData.set(fieldName, blob, fileName);
890
+ }
891
+ for (const [key, value] of Object.entries(additionalFields)) {
892
+ if (value !== undefined && value !== '') {
893
+ formData.set(key, String(value));
894
+ // YouTube uses scheduledDate while TikTok/Instagram use schedule_date
895
+ if (key === 'schedule_date')
896
+ formData.set('scheduledDate', String(value));
897
+ }
898
+ }
899
+ const endpoint = operation === 'create' ? '/upload' : '/upload/bulk';
900
+ responseData = (await this.helpers.httpRequest({
901
+ method: 'POST',
902
+ url: `${BASE_URL}${endpoint}`,
903
+ headers: { 'x-api-key': apiToken },
904
+ body: formData,
905
+ json: true,
906
+ }));
907
+ }
908
+ else if (operation === 'list') {
909
+ const filters = this.getNodeParameter('filters', i, {});
910
+ const qs = {};
911
+ if (filters.platform)
912
+ qs.platform = filters.platform;
913
+ if (filters.status)
914
+ qs.status = filters.status;
915
+ if (filters.search)
916
+ qs.search = filters.search;
917
+ if (filters.page)
918
+ qs.page = filters.page;
919
+ if (filters.limit)
920
+ qs.limit = filters.limit;
921
+ const res = await apiRequest('GET', '/upload', undefined, qs);
922
+ responseData = (_d = res.data) !== null && _d !== void 0 ? _d : [];
923
+ }
924
+ else if (operation === 'get') {
925
+ const uploadId = this.getNodeParameter('uploadId', i);
926
+ const res = await apiRequest('GET', `/upload/${uploadId}`);
927
+ responseData = (_e = res.data) !== null && _e !== void 0 ? _e : {};
928
+ }
929
+ else if (operation === 'update') {
930
+ const uploadId = this.getNodeParameter('uploadId', i);
931
+ const updateFields = this.getNodeParameter('updateFields', i, {});
932
+ const body = {};
933
+ if (updateFields.title !== undefined)
934
+ body.title = updateFields.title;
935
+ if (updateFields.description !== undefined)
936
+ body.description = updateFields.description;
937
+ if (updateFields.scheduledFor !== undefined)
938
+ body.scheduledFor = updateFields.scheduledFor;
939
+ const res = await apiRequest('PATCH', `/upload/${uploadId}`, body);
940
+ responseData = (_f = res.data) !== null && _f !== void 0 ? _f : {};
941
+ }
942
+ }
943
+ // ══════════════════════════════════════════════════════════════════
944
+ // PINTEREST
945
+ // ══════════════════════════════════════════════════════════════════
946
+ else if (resource === 'pinterest') {
947
+ if (operation === 'getBoards') {
948
+ const accountId = this.getNodeParameter('accountId', i);
949
+ // Endpoint: /pinterest/boards (returns raw data or data wrapper)
950
+ const res = await apiRequest('GET', '/pinterest/boards', undefined, { accountId });
951
+ responseData = (_g = res.data) !== null && _g !== void 0 ? _g : [];
952
+ }
953
+ }
954
+ // ══════════════════════════════════════════════════════════════════
955
+ // ACCOUNT
956
+ // ══════════════════════════════════════════════════════════════════
957
+ else if (resource === 'account') {
958
+ if (operation === 'list') {
959
+ const filters = this.getNodeParameter('filters', i, {});
960
+ const qs = {};
961
+ if (filters.platform)
962
+ qs.platform = filters.platform;
963
+ if (filters.status)
964
+ qs.status = filters.status;
965
+ const res = await apiRequest('GET', '/accounts', undefined, qs);
966
+ responseData = (_h = res.data) !== null && _h !== void 0 ? _h : [];
967
+ }
968
+ else if (operation === 'get') {
969
+ const accountId = this.getNodeParameter('accountId', i);
970
+ const res = await apiRequest('GET', `/accounts/${accountId}`);
971
+ responseData = (_j = res.data) !== null && _j !== void 0 ? _j : {};
972
+ }
973
+ else if (operation === 'delete') {
974
+ const accountId = this.getNodeParameter('accountId', i);
975
+ responseData = await apiRequest('DELETE', `/accounts/${accountId}`);
976
+ }
977
+ else if (operation === 'linkedinPages') {
978
+ const accountId = this.getNodeParameter('accountId', i);
979
+ const res = await apiRequest('GET', '/accounts/linkedin/pages', undefined, { accountId });
980
+ responseData = (_k = res.pages) !== null && _k !== void 0 ? _k : [];
981
+ }
982
+ }
983
+ // ══════════════════════════════════════════════════════════════════
984
+ // WEBHOOK
985
+ // ══════════════════════════════════════════════════════════════════
986
+ else if (resource === 'webhook') {
987
+ if (operation === 'create') {
988
+ const url = this.getNodeParameter('url', i);
989
+ const events = this.getNodeParameter('events', i);
990
+ const description = this.getNodeParameter('description', i, '');
991
+ const body = { url, events };
992
+ if (description)
993
+ body.description = description;
994
+ const res = await apiRequest('POST', '/webhooks', body);
995
+ responseData = (_l = res.data) !== null && _l !== void 0 ? _l : {};
996
+ }
997
+ else if (operation === 'list') {
998
+ const res = await apiRequest('GET', '/webhooks');
999
+ responseData = (_m = res.data) !== null && _m !== void 0 ? _m : [];
1000
+ }
1001
+ else if (operation === 'delete') {
1002
+ const webhookId = this.getNodeParameter('webhookId', i);
1003
+ responseData = await apiRequest('DELETE', `/webhooks/${webhookId}`);
1004
+ }
1005
+ else if (operation === 'test') {
1006
+ const webhookId = this.getNodeParameter('webhookId', i);
1007
+ responseData = await apiRequest('POST', `/webhooks/${webhookId}/test`);
1008
+ }
1009
+ }
1010
+ // ══════════════════════════════════════════════════════════════════
1011
+ // SHORT LINK
1012
+ // ══════════════════════════════════════════════════════════════════
1013
+ else if (resource === 'shortLink') {
1014
+ if (operation === 'create') {
1015
+ const url = this.getNodeParameter('url', i);
1016
+ // Handle rules
1017
+ const rulesContainer = this.getNodeParameter('rules', i, {});
1018
+ const rules = rulesContainer.rule || undefined;
1019
+ const additionalFields = this.getNodeParameter('additionalFields', i, {});
1020
+ const body = { url };
1021
+ if (rules && rules.length > 0) {
1022
+ body.rules = rules;
1023
+ }
1024
+ if (additionalFields.slug)
1025
+ body.slug = additionalFields.slug;
1026
+ if (additionalFields.title)
1027
+ body.title = additionalFields.title;
1028
+ if (additionalFields.description)
1029
+ body.description = additionalFields.description;
1030
+ if (additionalFields.imageUrl)
1031
+ body.imageUrl = additionalFields.imageUrl;
1032
+ if (additionalFields.useDeeplink !== undefined)
1033
+ body.useDeeplink = additionalFields.useDeeplink;
1034
+ if (additionalFields.botProtection !== undefined)
1035
+ body.botProtection = additionalFields.botProtection;
1036
+ if (additionalFields.safeUrl)
1037
+ body.safeUrl = additionalFields.safeUrl;
1038
+ const res = await apiRequest('POST', '/short-links', body);
1039
+ responseData = (_o = res.data) !== null && _o !== void 0 ? _o : {};
1040
+ }
1041
+ else if (operation === 'list') {
1042
+ const res = await apiRequest('GET', '/short-links');
1043
+ responseData = (_p = res.data) !== null && _p !== void 0 ? _p : [];
1044
+ }
1045
+ else if (operation === 'get') {
1046
+ const linkId = this.getNodeParameter('linkId', i);
1047
+ // Backend returns the link directly (no { data: ... } wrapper)
1048
+ responseData = await apiRequest('GET', `/short-links/${linkId}`);
1049
+ }
1050
+ else if (operation === 'update') {
1051
+ const linkId = this.getNodeParameter('linkId', i);
1052
+ // Handle rules
1053
+ const rulesContainer = this.getNodeParameter('rules', i, {});
1054
+ const rules = rulesContainer.rule || undefined;
1055
+ const updateFields = this.getNodeParameter('updateFields', i, {});
1056
+ const body = {};
1057
+ if (rules && rules.length > 0) {
1058
+ body.rules = rules;
1059
+ }
1060
+ if (updateFields.url)
1061
+ body.url = updateFields.url;
1062
+ if (updateFields.slug)
1063
+ body.slug = updateFields.slug;
1064
+ if (updateFields.title)
1065
+ body.title = updateFields.title;
1066
+ if (updateFields.description)
1067
+ body.description = updateFields.description;
1068
+ if (updateFields.imageUrl)
1069
+ body.imageUrl = updateFields.imageUrl;
1070
+ if (updateFields.useDeeplink !== undefined)
1071
+ body.useDeeplink = updateFields.useDeeplink;
1072
+ if (updateFields.botProtection !== undefined)
1073
+ body.botProtection = updateFields.botProtection;
1074
+ if (updateFields.safeUrl)
1075
+ body.safeUrl = updateFields.safeUrl;
1076
+ // Backend returns the link directly (no { data: ... } wrapper)
1077
+ responseData = await apiRequest('PUT', `/short-links/${linkId}`, body);
1078
+ }
1079
+ else if (operation === 'delete') {
1080
+ const linkId = this.getNodeParameter('linkId', i);
1081
+ responseData = await apiRequest('DELETE', `/short-links/${linkId}`);
1082
+ }
1083
+ }
1084
+ // ── Normalize array vs object responses ───────────────────────────
1085
+ const items_ = Array.isArray(responseData) ? responseData : [responseData];
1086
+ for (const item of items_) {
1087
+ returnData.push({ json: item, pairedItem: { item: i } });
1088
+ }
1089
+ }
1090
+ catch (error) {
1091
+ if (this.continueOnFail()) {
1092
+ returnData.push({
1093
+ json: { error: error.message },
1094
+ pairedItem: { item: i },
1095
+ });
1096
+ continue;
1097
+ }
1098
+ throw error;
1099
+ }
1100
+ }
1101
+ return [returnData];
1102
+ }
1103
+ }
1104
+ exports.MultiUploadTool = MultiUploadTool;
1105
+ //# sourceMappingURL=MultiUploadTool.node.js.map