n8n-nodes-tornado-api 1.0.2 → 1.3.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,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TornadoApi = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
4
5
  class TornadoApi {
5
6
  constructor() {
6
7
  this.description = {
@@ -38,10 +39,18 @@ class TornadoApi {
38
39
  name: 'Batch',
39
40
  value: 'batch',
40
41
  },
42
+ {
43
+ name: 'Dashboard',
44
+ value: 'dashboard',
45
+ },
41
46
  {
42
47
  name: 'Storage',
43
48
  value: 'storage',
44
49
  },
50
+ {
51
+ name: 'Notification',
52
+ value: 'notification',
53
+ },
45
54
  {
46
55
  name: 'Account',
47
56
  value: 'account',
@@ -67,12 +76,48 @@ class TornadoApi {
67
76
  description: 'Create a new download job',
68
77
  action: 'Create a download job',
69
78
  },
79
+ {
80
+ name: 'Create Bulk',
81
+ value: 'createBulk',
82
+ description: 'Create multiple download jobs at once',
83
+ action: 'Create bulk jobs',
84
+ },
70
85
  {
71
86
  name: 'Get Status',
72
87
  value: 'getStatus',
73
88
  description: 'Get the status of a job',
74
89
  action: 'Get job status',
75
90
  },
91
+ {
92
+ name: 'List',
93
+ value: 'list',
94
+ description: 'List all jobs',
95
+ action: 'List jobs',
96
+ },
97
+ {
98
+ name: 'Cancel',
99
+ value: 'cancel',
100
+ description: 'Cancel a pending job',
101
+ action: 'Cancel a job',
102
+ },
103
+ {
104
+ name: 'Retry',
105
+ value: 'retry',
106
+ description: 'Retry a failed job',
107
+ action: 'Retry a job',
108
+ },
109
+ {
110
+ name: 'Delete File',
111
+ value: 'deleteFile',
112
+ description: 'Delete a job file from storage',
113
+ action: 'Delete a job file',
114
+ },
115
+ {
116
+ name: 'Get Metadata',
117
+ value: 'getMetadata',
118
+ description: 'Get video metadata without downloading',
119
+ action: 'Get video metadata',
120
+ },
76
121
  ],
77
122
  default: 'create',
78
123
  },
@@ -190,6 +235,119 @@ class TornadoApi {
190
235
  default: '',
191
236
  description: 'URL to receive completion notification',
192
237
  },
238
+ {
239
+ displayName: 'Audio Only',
240
+ name: 'audio_only',
241
+ type: 'boolean',
242
+ default: false,
243
+ description: 'Extract audio track only (outputs mp3/aac)',
244
+ },
245
+ {
246
+ displayName: 'Download Subtitles',
247
+ name: 'download_subtitles',
248
+ type: 'boolean',
249
+ default: false,
250
+ description: 'Download subtitles if available',
251
+ },
252
+ {
253
+ displayName: 'Download Thumbnail',
254
+ name: 'download_thumbnail',
255
+ type: 'boolean',
256
+ default: false,
257
+ description: 'Download video thumbnail',
258
+ },
259
+ {
260
+ displayName: 'Quality Preset',
261
+ name: 'quality_preset',
262
+ type: 'options',
263
+ options: [
264
+ { name: 'Default', value: '' },
265
+ { name: 'Highest', value: 'highest' },
266
+ { name: 'High', value: 'high' },
267
+ { name: 'Medium', value: 'medium' },
268
+ { name: 'Low', value: 'low' },
269
+ { name: 'Lowest', value: 'lowest' },
270
+ ],
271
+ default: '',
272
+ description: 'Quality preset (overrides video_quality)',
273
+ },
274
+ {
275
+ displayName: 'Max Resolution',
276
+ name: 'max_resolution',
277
+ type: 'options',
278
+ options: [
279
+ { name: 'Best Available', value: 'best' },
280
+ { name: '4K (2160p)', value: '2160' },
281
+ { name: '2K (1440p)', value: '1440' },
282
+ { name: 'Full HD (1080p)', value: '1080' },
283
+ { name: 'HD (720p)', value: '720' },
284
+ { name: 'SD (480p)', value: '480' },
285
+ { name: 'Low (360p)', value: '360' },
286
+ ],
287
+ default: 'best',
288
+ description: 'Maximum video resolution to download',
289
+ },
290
+ {
291
+ displayName: 'Clip Start',
292
+ name: 'clip_start',
293
+ type: 'string',
294
+ default: '',
295
+ placeholder: '00:01:30 or 90',
296
+ description: 'Start timestamp for video clipping (HH:MM:SS or seconds)',
297
+ },
298
+ {
299
+ displayName: 'Clip End',
300
+ name: 'clip_end',
301
+ type: 'string',
302
+ default: '',
303
+ placeholder: '00:05:00 or 300',
304
+ description: 'End timestamp for video clipping (HH:MM:SS or seconds)',
305
+ },
306
+ {
307
+ displayName: 'Live Recording',
308
+ name: 'live_recording',
309
+ type: 'boolean',
310
+ default: false,
311
+ description: 'Whether to enable live stream recording mode',
312
+ },
313
+ {
314
+ displayName: 'Live From Start',
315
+ name: 'live_from_start',
316
+ type: 'boolean',
317
+ default: false,
318
+ description: 'Whether to record from the beginning of the live stream (VOD mode)',
319
+ },
320
+ {
321
+ displayName: 'Max Duration (Seconds)',
322
+ name: 'max_duration',
323
+ type: 'number',
324
+ typeOptions: {
325
+ minValue: 1,
326
+ },
327
+ default: 0,
328
+ description: 'Maximum recording duration in seconds (recommended for live streams)',
329
+ },
330
+ {
331
+ displayName: 'Wait for Live',
332
+ name: 'wait_for_live',
333
+ type: 'boolean',
334
+ default: false,
335
+ description: 'Whether to wait for scheduled/upcoming streams to start',
336
+ },
337
+ {
338
+ displayName: 'Paused',
339
+ name: 'paused',
340
+ type: 'boolean',
341
+ default: false,
342
+ description: 'Whether to create Spotify show batch in paused mode (allows renaming before starting)',
343
+ },
344
+ {
345
+ displayName: 'Enable Progress Webhook',
346
+ name: 'enable_progress_webhook',
347
+ type: 'boolean',
348
+ default: false,
349
+ description: 'Whether to receive progress webhooks during processing (downloading, muxing, uploading stages)',
350
+ },
193
351
  ],
194
352
  },
195
353
  // Job Get Status Fields
@@ -201,174 +359,785 @@ class TornadoApi {
201
359
  displayOptions: {
202
360
  show: {
203
361
  resource: ['job'],
204
- operation: ['getStatus'],
362
+ operation: ['getStatus', 'cancel', 'retry', 'deleteFile'],
205
363
  },
206
364
  },
207
365
  default: '',
208
366
  description: 'The UUID of the job',
209
367
  },
210
- // ==================== BATCH OPERATIONS ====================
368
+ // Job List Options
211
369
  {
212
- displayName: 'Operation',
213
- name: 'operation',
214
- type: 'options',
215
- noDataExpression: true,
370
+ displayName: 'List Options',
371
+ name: 'listOptions',
372
+ type: 'collection',
373
+ placeholder: 'Add Option',
374
+ default: {},
216
375
  displayOptions: {
217
376
  show: {
218
- resource: ['batch'],
377
+ resource: ['job'],
378
+ operation: ['list'],
219
379
  },
220
380
  },
221
381
  options: [
222
382
  {
223
- name: 'Get Status',
224
- value: 'getStatus',
225
- description: 'Get the status of a batch',
226
- action: 'Get batch status',
383
+ displayName: 'Limit',
384
+ name: 'limit',
385
+ type: 'number',
386
+ typeOptions: { minValue: 1, maxValue: 100 },
387
+ default: 20,
388
+ description: 'Number of jobs to return (max 100)',
389
+ },
390
+ {
391
+ displayName: 'Offset',
392
+ name: 'offset',
393
+ type: 'number',
394
+ typeOptions: { minValue: 0 },
395
+ default: 0,
396
+ description: 'Number of jobs to skip',
397
+ },
398
+ {
399
+ displayName: 'Status Filter',
400
+ name: 'status',
401
+ type: 'options',
402
+ options: [
403
+ { name: 'All', value: '' },
404
+ { name: 'Pending', value: 'pending' },
405
+ { name: 'Processing', value: 'processing' },
406
+ { name: 'Completed', value: 'completed' },
407
+ { name: 'Failed', value: 'failed' },
408
+ { name: 'Warning', value: 'warning' },
409
+ ],
410
+ default: '',
411
+ description: 'Filter by job status',
227
412
  },
228
413
  ],
229
- default: 'getStatus',
230
414
  },
231
- // Batch Get Status Fields
415
+ // Get Metadata URL
232
416
  {
233
- displayName: 'Batch ID',
234
- name: 'batchId',
417
+ displayName: 'URL',
418
+ name: 'metadataUrl',
235
419
  type: 'string',
236
420
  required: true,
237
421
  displayOptions: {
238
422
  show: {
239
- resource: ['batch'],
240
- operation: ['getStatus'],
423
+ resource: ['job'],
424
+ operation: ['getMetadata'],
241
425
  },
242
426
  },
243
427
  default: '',
244
- description: 'The UUID of the batch',
428
+ placeholder: 'https://www.youtube.com/watch?v=...',
429
+ description: 'The video URL to get metadata from',
245
430
  },
246
- // ==================== STORAGE OPERATIONS ====================
431
+ // Bulk Create URLs
247
432
  {
248
- displayName: 'Operation',
249
- name: 'operation',
250
- type: 'options',
251
- noDataExpression: true,
433
+ displayName: 'URLs',
434
+ name: 'bulkUrls',
435
+ type: 'fixedCollection',
436
+ typeOptions: { multipleValues: true },
437
+ required: true,
252
438
  displayOptions: {
253
439
  show: {
254
- resource: ['storage'],
440
+ resource: ['job'],
441
+ operation: ['createBulk'],
255
442
  },
256
443
  },
444
+ default: {},
257
445
  options: [
258
446
  {
259
- name: 'Configure Bucket',
260
- value: 'configureBucket',
261
- description: 'Configure your own S3/R2 bucket for file uploads',
262
- action: 'Configure S3 bucket',
263
- },
264
- {
265
- name: 'Reset to Default',
266
- value: 'resetBucket',
267
- description: 'Reset to use Tornado default storage',
268
- action: 'Reset to default storage',
447
+ name: 'urlItems',
448
+ displayName: 'URLs',
449
+ values: [
450
+ {
451
+ displayName: 'URL',
452
+ name: 'url',
453
+ type: 'string',
454
+ default: '',
455
+ description: 'Video URL to download',
456
+ },
457
+ {
458
+ displayName: 'Filename',
459
+ name: 'filename',
460
+ type: 'string',
461
+ default: '',
462
+ description: 'Optional custom filename',
463
+ },
464
+ ],
269
465
  },
270
466
  ],
271
- default: 'configureBucket',
467
+ description: 'List of URLs to download (max 100)',
272
468
  },
273
- // Storage Provider
469
+ // Bulk Create Options
274
470
  {
275
- displayName: 'Provider',
276
- name: 'provider',
277
- type: 'options',
471
+ displayName: 'Bulk Options',
472
+ name: 'bulkOptions',
473
+ type: 'collection',
474
+ placeholder: 'Add Option',
475
+ default: {},
278
476
  displayOptions: {
279
477
  show: {
280
- resource: ['storage'],
281
- operation: ['configureBucket'],
478
+ resource: ['job'],
479
+ operation: ['createBulk'],
282
480
  },
283
481
  },
284
482
  options: [
285
- { name: 'Amazon S3', value: 'aws' },
286
- { name: 'Cloudflare R2', value: 'r2' },
287
- { name: 'MinIO', value: 'minio' },
288
- { name: 'Other S3-Compatible', value: 'other' },
483
+ {
484
+ displayName: 'Folder',
485
+ name: 'folder',
486
+ type: 'string',
487
+ default: '',
488
+ description: 'S3 folder prefix for all files',
489
+ },
490
+ {
491
+ displayName: 'Format',
492
+ name: 'format',
493
+ type: 'options',
494
+ options: [
495
+ { name: 'MP4', value: 'mp4' },
496
+ { name: 'MKV', value: 'mkv' },
497
+ { name: 'WebM', value: 'webm' },
498
+ { name: 'MOV', value: 'mov' },
499
+ ],
500
+ default: 'mp4',
501
+ description: 'Output format for all jobs',
502
+ },
503
+ {
504
+ displayName: 'Video Codec',
505
+ name: 'video_codec',
506
+ type: 'options',
507
+ options: [
508
+ { name: 'Copy (No Re-encode)', value: 'copy' },
509
+ { name: 'H.264', value: 'h264' },
510
+ { name: 'H.265 (HEVC)', value: 'h265' },
511
+ { name: 'VP9', value: 'vp9' },
512
+ ],
513
+ default: 'copy',
514
+ description: 'Video codec for all jobs',
515
+ },
516
+ {
517
+ displayName: 'Audio Codec',
518
+ name: 'audio_codec',
519
+ type: 'options',
520
+ options: [
521
+ { name: 'Copy (No Re-encode)', value: 'copy' },
522
+ { name: 'AAC', value: 'aac' },
523
+ { name: 'Opus', value: 'opus' },
524
+ { name: 'MP3', value: 'mp3' },
525
+ ],
526
+ default: 'copy',
527
+ description: 'Audio codec for all jobs',
528
+ },
529
+ {
530
+ displayName: 'Audio Bitrate',
531
+ name: 'audio_bitrate',
532
+ type: 'options',
533
+ options: [
534
+ { name: '64 kbps', value: '64k' },
535
+ { name: '128 kbps', value: '128k' },
536
+ { name: '192 kbps', value: '192k' },
537
+ { name: '256 kbps', value: '256k' },
538
+ { name: '320 kbps', value: '320k' },
539
+ ],
540
+ default: '192k',
541
+ description: 'Audio bitrate for all jobs',
542
+ },
543
+ {
544
+ displayName: 'Video Quality (CRF)',
545
+ name: 'video_quality',
546
+ type: 'number',
547
+ typeOptions: {
548
+ minValue: 0,
549
+ maxValue: 51,
550
+ },
551
+ default: 23,
552
+ description: 'Video quality CRF for all jobs (0-51, lower = better)',
553
+ },
554
+ {
555
+ displayName: 'Audio Only',
556
+ name: 'audio_only',
557
+ type: 'boolean',
558
+ default: false,
559
+ description: 'Extract audio only for all jobs',
560
+ },
561
+ {
562
+ displayName: 'Download Subtitles',
563
+ name: 'download_subtitles',
564
+ type: 'boolean',
565
+ default: false,
566
+ description: 'Download subtitles for all jobs',
567
+ },
568
+ {
569
+ displayName: 'Download Thumbnail',
570
+ name: 'download_thumbnail',
571
+ type: 'boolean',
572
+ default: false,
573
+ description: 'Download thumbnails for all jobs',
574
+ },
575
+ {
576
+ displayName: 'Quality Preset',
577
+ name: 'quality_preset',
578
+ type: 'options',
579
+ options: [
580
+ { name: 'Default', value: '' },
581
+ { name: 'Highest', value: 'highest' },
582
+ { name: 'High', value: 'high' },
583
+ { name: 'Medium', value: 'medium' },
584
+ { name: 'Low', value: 'low' },
585
+ { name: 'Lowest', value: 'lowest' },
586
+ ],
587
+ default: '',
588
+ description: 'Quality preset for all jobs',
589
+ },
590
+ {
591
+ displayName: 'Max Resolution',
592
+ name: 'max_resolution',
593
+ type: 'options',
594
+ options: [
595
+ { name: 'Best Available', value: 'best' },
596
+ { name: '4K (2160p)', value: '2160' },
597
+ { name: '2K (1440p)', value: '1440' },
598
+ { name: 'Full HD (1080p)', value: '1080' },
599
+ { name: 'HD (720p)', value: '720' },
600
+ { name: 'SD (480p)', value: '480' },
601
+ { name: 'Low (360p)', value: '360' },
602
+ ],
603
+ default: 'best',
604
+ description: 'Maximum video resolution for all jobs',
605
+ },
606
+ {
607
+ displayName: 'Clip Start',
608
+ name: 'clip_start',
609
+ type: 'string',
610
+ default: '',
611
+ placeholder: '00:01:30 or 90',
612
+ description: 'Start timestamp for video clipping (all jobs)',
613
+ },
614
+ {
615
+ displayName: 'Clip End',
616
+ name: 'clip_end',
617
+ type: 'string',
618
+ default: '',
619
+ placeholder: '00:05:00 or 300',
620
+ description: 'End timestamp for video clipping (all jobs)',
621
+ },
622
+ {
623
+ displayName: 'Live Recording',
624
+ name: 'live_recording',
625
+ type: 'boolean',
626
+ default: false,
627
+ description: 'Whether to enable live stream recording mode for all jobs',
628
+ },
629
+ {
630
+ displayName: 'Live From Start',
631
+ name: 'live_from_start',
632
+ type: 'boolean',
633
+ default: false,
634
+ description: 'Whether to record from the beginning of live streams',
635
+ },
636
+ {
637
+ displayName: 'Max Duration (Seconds)',
638
+ name: 'max_duration',
639
+ type: 'number',
640
+ typeOptions: {
641
+ minValue: 1,
642
+ },
643
+ default: 0,
644
+ description: 'Maximum recording duration in seconds for all jobs',
645
+ },
646
+ {
647
+ displayName: 'Wait for Live',
648
+ name: 'wait_for_live',
649
+ type: 'boolean',
650
+ default: false,
651
+ description: 'Whether to wait for scheduled streams to start',
652
+ },
289
653
  ],
290
- default: 'aws',
291
- description: 'Your storage provider',
292
654
  },
293
- // S3 Endpoint
655
+ // ==================== BATCH OPERATIONS ====================
294
656
  {
295
- displayName: 'Endpoint URL',
296
- name: 'endpoint',
297
- type: 'string',
298
- required: true,
657
+ displayName: 'Operation',
658
+ name: 'operation',
659
+ type: 'options',
660
+ noDataExpression: true,
299
661
  displayOptions: {
300
662
  show: {
301
- resource: ['storage'],
302
- operation: ['configureBucket'],
663
+ resource: ['batch'],
303
664
  },
304
665
  },
305
- default: '',
306
- placeholder: 'https://s3.us-east-1.amazonaws.com',
307
- description: 'S3 endpoint URL. For AWS use https://s3.REGION.amazonaws.com, for R2 use https://ACCOUNT_ID.r2.cloudflarestorage.com',
666
+ options: [
667
+ {
668
+ name: 'Get Status',
669
+ value: 'getStatus',
670
+ description: 'Get the status of a batch',
671
+ action: 'Get batch status',
672
+ },
673
+ {
674
+ name: 'Rename Jobs',
675
+ value: 'renameJobs',
676
+ description: 'Rename files of jobs in a batch',
677
+ action: 'Rename batch jobs',
678
+ },
679
+ {
680
+ name: 'Start',
681
+ value: 'start',
682
+ description: 'Start processing a batch',
683
+ action: 'Start a batch',
684
+ },
685
+ ],
686
+ default: 'getStatus',
308
687
  },
309
- // Bucket Name
688
+ // Batch ID Fields
310
689
  {
311
- displayName: 'Bucket Name',
312
- name: 'bucket',
690
+ displayName: 'Batch ID',
691
+ name: 'batchId',
313
692
  type: 'string',
314
693
  required: true,
315
694
  displayOptions: {
316
695
  show: {
317
- resource: ['storage'],
318
- operation: ['configureBucket'],
696
+ resource: ['batch'],
697
+ operation: ['getStatus', 'renameJobs', 'start'],
319
698
  },
320
699
  },
321
700
  default: '',
322
- placeholder: 'my-videos-bucket',
323
- description: 'The name of your S3 bucket',
701
+ description: 'The UUID of the batch',
324
702
  },
325
- // Region
703
+ // Batch Rename Jobs Fields
326
704
  {
327
- displayName: 'Region',
328
- name: 'region',
329
- type: 'string',
705
+ displayName: 'Renames',
706
+ name: 'renames',
707
+ type: 'fixedCollection',
708
+ typeOptions: { multipleValues: true },
330
709
  required: true,
331
710
  displayOptions: {
332
711
  show: {
333
- resource: ['storage'],
334
- operation: ['configureBucket'],
712
+ resource: ['batch'],
713
+ operation: ['renameJobs'],
335
714
  },
336
715
  },
337
- default: 'us-east-1',
338
- placeholder: 'us-east-1',
339
- description: 'AWS region (use "auto" for Cloudflare R2)',
716
+ default: {},
717
+ options: [
718
+ {
719
+ name: 'renameItems',
720
+ displayName: 'Renames',
721
+ values: [
722
+ {
723
+ displayName: 'Job ID',
724
+ name: 'job_id',
725
+ type: 'string',
726
+ default: '',
727
+ description: 'The UUID of the job to rename',
728
+ },
729
+ {
730
+ displayName: 'Filename',
731
+ name: 'filename',
732
+ type: 'string',
733
+ default: '',
734
+ description: 'New filename for the job',
735
+ },
736
+ ],
737
+ },
738
+ ],
739
+ description: 'List of job ID and filename pairs to rename',
340
740
  },
341
- // Access Key
741
+ // ==================== DASHBOARD OPERATIONS ====================
342
742
  {
343
- displayName: 'Access Key ID',
344
- name: 'accessKey',
345
- type: 'string',
346
- required: true,
743
+ displayName: 'Operation',
744
+ name: 'operation',
745
+ type: 'options',
746
+ noDataExpression: true,
347
747
  displayOptions: {
348
748
  show: {
349
- resource: ['storage'],
350
- operation: ['configureBucket'],
749
+ resource: ['dashboard'],
351
750
  },
352
751
  },
752
+ options: [
753
+ {
754
+ name: 'Get Stats',
755
+ value: 'getStats',
756
+ description: 'Get aggregated statistics for your API key',
757
+ action: 'Get dashboard stats',
758
+ },
759
+ {
760
+ name: 'Get Jobs',
761
+ value: 'getJobs',
762
+ description: 'Get paginated list of jobs for the dashboard',
763
+ action: 'Get dashboard jobs',
764
+ },
765
+ {
766
+ name: 'Get Batches',
767
+ value: 'getBatches',
768
+ description: 'Get list of batch operations',
769
+ action: 'Get dashboard batches',
770
+ },
771
+ {
772
+ name: 'Get Daily Stats',
773
+ value: 'getDaily',
774
+ description: 'Get daily job statistics for the last 7 days',
775
+ action: 'Get daily stats',
776
+ },
777
+ {
778
+ name: 'Get Cluster Stats',
779
+ value: 'getCluster',
780
+ description: 'Get real-time cluster activity statistics',
781
+ action: 'Get cluster stats',
782
+ },
783
+ {
784
+ name: 'Get Billing',
785
+ value: 'getBilling',
786
+ description: 'Get current billing period usage from Stripe',
787
+ action: 'Get billing info',
788
+ },
789
+ ],
790
+ default: 'getStats',
791
+ },
792
+ // Dashboard Get Jobs Options
793
+ {
794
+ displayName: 'Options',
795
+ name: 'dashboardJobsOptions',
796
+ type: 'collection',
797
+ placeholder: 'Add Option',
798
+ default: {},
799
+ displayOptions: {
800
+ show: {
801
+ resource: ['dashboard'],
802
+ operation: ['getJobs'],
803
+ },
804
+ },
805
+ options: [
806
+ {
807
+ displayName: 'Limit',
808
+ name: 'limit',
809
+ type: 'number',
810
+ typeOptions: { minValue: 1, maxValue: 100 },
811
+ default: 50,
812
+ description: 'Number of jobs to return (max 100)',
813
+ },
814
+ {
815
+ displayName: 'Offset',
816
+ name: 'offset',
817
+ type: 'number',
818
+ typeOptions: { minValue: 0 },
819
+ default: 0,
820
+ description: 'Number of jobs to skip for pagination',
821
+ },
822
+ {
823
+ displayName: 'Status Filter',
824
+ name: 'status',
825
+ type: 'options',
826
+ options: [
827
+ { name: 'All', value: '' },
828
+ { name: 'Pending', value: 'pending' },
829
+ { name: 'Processing', value: 'processing' },
830
+ { name: 'Completed', value: 'completed' },
831
+ { name: 'Failed', value: 'failed' },
832
+ { name: 'Warning', value: 'warning' },
833
+ ],
834
+ default: '',
835
+ description: 'Filter by job status',
836
+ },
837
+ ],
838
+ },
839
+ // ==================== STORAGE OPERATIONS ====================
840
+ {
841
+ displayName: 'Operation',
842
+ name: 'operation',
843
+ type: 'options',
844
+ noDataExpression: true,
845
+ displayOptions: { show: { resource: ['storage'] } },
846
+ options: [
847
+ { name: 'Configure S3', value: 'configureS3', description: 'Configure an S3-compatible storage provider', action: 'Configure S3 storage' },
848
+ { name: 'Delete S3', value: 'deleteS3', description: 'Remove S3 storage configuration', action: 'Delete S3 configuration' },
849
+ { name: 'Configure Azure Blob', value: 'configureBlob', description: 'Configure Azure Blob Storage', action: 'Configure Azure Blob storage' },
850
+ { name: 'Delete Azure Blob', value: 'deleteBlob', description: 'Remove Azure Blob storage configuration', action: 'Delete Azure Blob configuration' },
851
+ { name: 'Configure GCS', value: 'configureGcs', description: 'Configure Google Cloud Storage', action: 'Configure GCS storage' },
852
+ { name: 'Delete GCS', value: 'deleteGcs', description: 'Remove Google Cloud Storage configuration', action: 'Delete GCS configuration' },
853
+ { name: 'Configure OSS', value: 'configureOss', description: 'Configure Alibaba Cloud OSS', action: 'Configure OSS storage' },
854
+ { name: 'Delete OSS', value: 'deleteOss', description: 'Remove Alibaba Cloud OSS configuration', action: 'Delete OSS configuration' },
855
+ { name: 'Configure Bucket (Legacy)', value: 'configureBucket', description: '[Deprecated] Configure S3 bucket using legacy endpoint', action: 'Configure bucket (legacy)' },
856
+ { name: 'Reset Bucket (Legacy)', value: 'resetBucket', description: '[Deprecated] Reset to default storage using legacy endpoint', action: 'Reset bucket (legacy)' },
857
+ ],
858
+ default: 'configureS3',
859
+ },
860
+ // S3 Fields
861
+ {
862
+ displayName: 'Endpoint URL',
863
+ name: 's3Endpoint',
864
+ type: 'string',
865
+ required: true,
866
+ displayOptions: { show: { resource: ['storage'], operation: ['configureS3'] } },
867
+ default: '',
868
+ description: 'S3 endpoint URL (e.g., https://s3.amazonaws.com for AWS, or custom for R2, MinIO, etc.)',
869
+ },
870
+ {
871
+ displayName: 'Bucket Name',
872
+ name: 's3Bucket',
873
+ type: 'string',
874
+ required: true,
875
+ displayOptions: { show: { resource: ['storage'], operation: ['configureS3'] } },
876
+ default: '',
877
+ description: 'The name of your S3 bucket',
878
+ },
879
+ {
880
+ displayName: 'Region',
881
+ name: 's3Region',
882
+ type: 'string',
883
+ required: true,
884
+ displayOptions: { show: { resource: ['storage'], operation: ['configureS3'] } },
885
+ default: 'us-east-1',
886
+ description: 'AWS region (use auto for Cloudflare R2)',
887
+ },
888
+ {
889
+ displayName: 'Access Key ID',
890
+ name: 's3AccessKey',
891
+ type: 'string',
892
+ required: true,
893
+ displayOptions: { show: { resource: ['storage'], operation: ['configureS3'] } },
353
894
  default: '',
354
895
  description: 'Your S3 Access Key ID',
355
896
  },
356
- // Secret Key
357
897
  {
358
898
  displayName: 'Secret Access Key',
359
- name: 'secretKey',
899
+ name: 's3SecretKey',
360
900
  type: 'string',
361
901
  typeOptions: { password: true },
362
902
  required: true,
363
- displayOptions: {
364
- show: {
365
- resource: ['storage'],
366
- operation: ['configureBucket'],
367
- },
368
- },
903
+ displayOptions: { show: { resource: ['storage'], operation: ['configureS3'] } },
369
904
  default: '',
370
905
  description: 'Your S3 Secret Access Key',
371
906
  },
907
+ {
908
+ displayName: 'S3 Options',
909
+ name: 's3Options',
910
+ type: 'collection',
911
+ placeholder: 'Add Option',
912
+ default: {},
913
+ displayOptions: { show: { resource: ['storage'], operation: ['configureS3'] } },
914
+ options: [
915
+ { displayName: 'Folder Prefix', name: 'folder_prefix', type: 'string', default: '', description: 'Folder prefix for uploaded files' },
916
+ { displayName: 'Folder Name', name: 'folder_name', type: 'string', default: '', description: 'Base folder name in the bucket (default: videos)' },
917
+ ],
918
+ },
919
+ // Azure Blob Fields
920
+ {
921
+ displayName: 'Account Name',
922
+ name: 'blobAccountName',
923
+ type: 'string',
924
+ required: true,
925
+ displayOptions: { show: { resource: ['storage'], operation: ['configureBlob'] } },
926
+ default: '',
927
+ description: 'Azure Storage account name',
928
+ },
929
+ {
930
+ displayName: 'Container',
931
+ name: 'blobContainer',
932
+ type: 'string',
933
+ required: true,
934
+ displayOptions: { show: { resource: ['storage'], operation: ['configureBlob'] } },
935
+ default: '',
936
+ description: 'Azure Blob container name',
937
+ },
938
+ {
939
+ displayName: 'Blob Options',
940
+ name: 'blobOptions',
941
+ type: 'collection',
942
+ placeholder: 'Add Option',
943
+ default: {},
944
+ displayOptions: { show: { resource: ['storage'], operation: ['configureBlob'] } },
945
+ options: [
946
+ { displayName: 'Account Key', name: 'account_key', type: 'string', typeOptions: { password: true }, default: '', description: 'Azure Storage account key' },
947
+ { displayName: 'SAS Token', name: 'sas_token', type: 'string', typeOptions: { password: true }, default: '', description: 'Azure SAS token (alternative to account key)' },
948
+ { displayName: 'Folder Prefix', name: 'folder_prefix', type: 'string', default: '', description: 'Folder prefix for uploaded files' },
949
+ { displayName: 'Folder Name', name: 'folder_name', type: 'string', default: '', description: 'Base folder name in the container (default: videos)' },
950
+ ],
951
+ },
952
+ // GCS Fields
953
+ {
954
+ displayName: 'Project ID',
955
+ name: 'gcsProjectId',
956
+ type: 'string',
957
+ required: true,
958
+ displayOptions: { show: { resource: ['storage'], operation: ['configureGcs'] } },
959
+ default: '',
960
+ description: 'Google Cloud project ID',
961
+ },
962
+ {
963
+ displayName: 'Bucket Name',
964
+ name: 'gcsBucket',
965
+ type: 'string',
966
+ required: true,
967
+ displayOptions: { show: { resource: ['storage'], operation: ['configureGcs'] } },
968
+ default: '',
969
+ description: 'GCS bucket name',
970
+ },
971
+ {
972
+ displayName: 'Service Account JSON',
973
+ name: 'gcsServiceAccountJson',
974
+ type: 'string',
975
+ typeOptions: { password: true },
976
+ required: true,
977
+ displayOptions: { show: { resource: ['storage'], operation: ['configureGcs'] } },
978
+ default: '',
979
+ description: 'GCS service account JSON key (stringified)',
980
+ },
981
+ {
982
+ displayName: 'GCS Options',
983
+ name: 'gcsOptions',
984
+ type: 'collection',
985
+ placeholder: 'Add Option',
986
+ default: {},
987
+ displayOptions: { show: { resource: ['storage'], operation: ['configureGcs'] } },
988
+ options: [
989
+ { displayName: 'Folder Prefix', name: 'folder_prefix', type: 'string', default: '', description: 'Folder prefix for uploaded files' },
990
+ { displayName: 'Folder Name', name: 'folder_name', type: 'string', default: '', description: 'Base folder name in the bucket (default: videos)' },
991
+ ],
992
+ },
993
+ // OSS Fields
994
+ {
995
+ displayName: 'Endpoint URL',
996
+ name: 'ossEndpoint',
997
+ type: 'string',
998
+ required: true,
999
+ displayOptions: { show: { resource: ['storage'], operation: ['configureOss'] } },
1000
+ default: '',
1001
+ description: 'Alibaba Cloud OSS endpoint URL',
1002
+ },
1003
+ {
1004
+ displayName: 'Bucket Name',
1005
+ name: 'ossBucket',
1006
+ type: 'string',
1007
+ required: true,
1008
+ displayOptions: { show: { resource: ['storage'], operation: ['configureOss'] } },
1009
+ default: '',
1010
+ description: 'Alibaba Cloud OSS bucket name',
1011
+ },
1012
+ {
1013
+ displayName: 'Access Key ID',
1014
+ name: 'ossAccessKeyId',
1015
+ type: 'string',
1016
+ required: true,
1017
+ displayOptions: { show: { resource: ['storage'], operation: ['configureOss'] } },
1018
+ default: '',
1019
+ description: 'Alibaba Cloud Access Key ID',
1020
+ },
1021
+ {
1022
+ displayName: 'Access Key Secret',
1023
+ name: 'ossAccessKeySecret',
1024
+ type: 'string',
1025
+ typeOptions: { password: true },
1026
+ required: true,
1027
+ displayOptions: { show: { resource: ['storage'], operation: ['configureOss'] } },
1028
+ default: '',
1029
+ description: 'Alibaba Cloud Access Key Secret',
1030
+ },
1031
+ {
1032
+ displayName: 'OSS Options',
1033
+ name: 'ossOptions',
1034
+ type: 'collection',
1035
+ placeholder: 'Add Option',
1036
+ default: {},
1037
+ displayOptions: { show: { resource: ['storage'], operation: ['configureOss'] } },
1038
+ options: [
1039
+ { displayName: 'Folder Prefix', name: 'folder_prefix', type: 'string', default: '', description: 'Folder prefix for uploaded files' },
1040
+ { displayName: 'Folder Name', name: 'folder_name', type: 'string', default: '', description: 'Base folder name in the bucket (default: videos)' },
1041
+ ],
1042
+ },
1043
+ // Legacy Storage Fields
1044
+ {
1045
+ displayName: 'Provider',
1046
+ name: 'provider',
1047
+ type: 'options',
1048
+ displayOptions: { show: { resource: ['storage'], operation: ['configureBucket'] } },
1049
+ options: [
1050
+ { name: 'Amazon S3', value: 'aws' },
1051
+ { name: 'Cloudflare R2', value: 'r2' },
1052
+ { name: 'MinIO', value: 'minio' },
1053
+ { name: 'Other S3-Compatible', value: 'other' },
1054
+ ],
1055
+ default: 'aws',
1056
+ description: '[Deprecated] Your storage provider',
1057
+ },
1058
+ {
1059
+ displayName: 'Endpoint URL',
1060
+ name: 'endpoint',
1061
+ type: 'string',
1062
+ required: true,
1063
+ displayOptions: { show: { resource: ['storage'], operation: ['configureBucket'] } },
1064
+ default: '',
1065
+ description: '[Deprecated] S3 endpoint URL',
1066
+ },
1067
+ {
1068
+ displayName: 'Bucket Name',
1069
+ name: 'bucket',
1070
+ type: 'string',
1071
+ required: true,
1072
+ displayOptions: { show: { resource: ['storage'], operation: ['configureBucket'] } },
1073
+ default: '',
1074
+ description: '[Deprecated] The name of your S3 bucket',
1075
+ },
1076
+ {
1077
+ displayName: 'Region',
1078
+ name: 'region',
1079
+ type: 'string',
1080
+ required: true,
1081
+ displayOptions: { show: { resource: ['storage'], operation: ['configureBucket'] } },
1082
+ default: 'us-east-1',
1083
+ description: '[Deprecated] AWS region',
1084
+ },
1085
+ {
1086
+ displayName: 'Access Key ID',
1087
+ name: 'accessKey',
1088
+ type: 'string',
1089
+ required: true,
1090
+ displayOptions: { show: { resource: ['storage'], operation: ['configureBucket'] } },
1091
+ default: '',
1092
+ description: '[Deprecated] Your S3 Access Key ID',
1093
+ },
1094
+ {
1095
+ displayName: 'Secret Access Key',
1096
+ name: 'secretKey',
1097
+ type: 'string',
1098
+ typeOptions: { password: true },
1099
+ required: true,
1100
+ displayOptions: { show: { resource: ['storage'], operation: ['configureBucket'] } },
1101
+ default: '',
1102
+ description: '[Deprecated] Your S3 Secret Access Key',
1103
+ },
1104
+ // ==================== NOTIFICATION OPERATIONS ====================
1105
+ {
1106
+ displayName: 'Operation',
1107
+ name: 'operation',
1108
+ type: 'options',
1109
+ noDataExpression: true,
1110
+ displayOptions: { show: { resource: ['notification'] } },
1111
+ options: [
1112
+ { name: 'Configure Slack', value: 'configureSlack', description: 'Configure Slack webhook notifications', action: 'Configure Slack notifications' },
1113
+ { name: 'Delete Slack', value: 'deleteSlack', description: 'Remove Slack webhook configuration', action: 'Delete Slack configuration' },
1114
+ ],
1115
+ default: 'configureSlack',
1116
+ },
1117
+ // Slack Fields
1118
+ {
1119
+ displayName: 'Webhook URL',
1120
+ name: 'slackWebhookUrl',
1121
+ type: 'string',
1122
+ required: true,
1123
+ displayOptions: { show: { resource: ['notification'], operation: ['configureSlack'] } },
1124
+ default: '',
1125
+ placeholder: 'https://hooks.slack.com/services/...',
1126
+ description: 'Slack incoming webhook URL',
1127
+ },
1128
+ {
1129
+ displayName: 'Notify Level',
1130
+ name: 'slackNotifyLevel',
1131
+ type: 'options',
1132
+ displayOptions: { show: { resource: ['notification'], operation: ['configureSlack'] } },
1133
+ options: [
1134
+ { name: 'All', value: 'all' },
1135
+ { name: 'Errors Only', value: 'errors_only' },
1136
+ { name: 'Warnings Only', value: 'warnings_only' },
1137
+ ],
1138
+ default: 'all',
1139
+ description: 'Which events trigger Slack notifications',
1140
+ },
372
1141
  // ==================== ACCOUNT OPERATIONS ====================
373
1142
  {
374
1143
  displayName: 'Operation',
@@ -426,6 +1195,32 @@ class TornadoApi {
426
1195
  body.folder = additionalOptions.folder;
427
1196
  if (additionalOptions.webhook_url)
428
1197
  body.webhook_url = additionalOptions.webhook_url;
1198
+ if (additionalOptions.audio_only)
1199
+ body.audio_only = additionalOptions.audio_only;
1200
+ if (additionalOptions.download_subtitles)
1201
+ body.download_subtitles = additionalOptions.download_subtitles;
1202
+ if (additionalOptions.download_thumbnail)
1203
+ body.download_thumbnail = additionalOptions.download_thumbnail;
1204
+ if (additionalOptions.quality_preset)
1205
+ body.quality_preset = additionalOptions.quality_preset;
1206
+ if (additionalOptions.max_resolution && additionalOptions.max_resolution !== 'best')
1207
+ body.max_resolution = additionalOptions.max_resolution;
1208
+ if (additionalOptions.clip_start)
1209
+ body.clip_start = additionalOptions.clip_start;
1210
+ if (additionalOptions.clip_end)
1211
+ body.clip_end = additionalOptions.clip_end;
1212
+ if (additionalOptions.live_recording)
1213
+ body.live_recording = additionalOptions.live_recording;
1214
+ if (additionalOptions.live_from_start)
1215
+ body.live_from_start = additionalOptions.live_from_start;
1216
+ if (additionalOptions.max_duration && additionalOptions.max_duration > 0)
1217
+ body.max_duration = additionalOptions.max_duration;
1218
+ if (additionalOptions.wait_for_live)
1219
+ body.wait_for_live = additionalOptions.wait_for_live;
1220
+ if (additionalOptions.paused)
1221
+ body.paused = additionalOptions.paused;
1222
+ if (additionalOptions.enable_progress_webhook)
1223
+ body.enable_progress_webhook = additionalOptions.enable_progress_webhook;
429
1224
  responseData = await this.helpers.httpRequest({
430
1225
  method: 'POST',
431
1226
  url: `${baseUrl}/jobs`,
@@ -448,6 +1243,124 @@ class TornadoApi {
448
1243
  },
449
1244
  });
450
1245
  }
1246
+ if (operation === 'list') {
1247
+ const listOptions = this.getNodeParameter('listOptions', i);
1248
+ const qs = {};
1249
+ if (listOptions.limit)
1250
+ qs.limit = listOptions.limit;
1251
+ if (listOptions.offset)
1252
+ qs.offset = listOptions.offset;
1253
+ if (listOptions.status)
1254
+ qs.status = listOptions.status;
1255
+ responseData = await this.helpers.httpRequest({
1256
+ method: 'GET',
1257
+ url: `${baseUrl}/jobs`,
1258
+ qs,
1259
+ json: true,
1260
+ headers: {
1261
+ 'x-api-key': credentials.apiKey,
1262
+ },
1263
+ });
1264
+ }
1265
+ if (operation === 'cancel') {
1266
+ const jobId = this.getNodeParameter('jobId', i);
1267
+ responseData = await this.helpers.httpRequest({
1268
+ method: 'DELETE',
1269
+ url: `${baseUrl}/jobs/${jobId}`,
1270
+ json: true,
1271
+ headers: {
1272
+ 'x-api-key': credentials.apiKey,
1273
+ },
1274
+ });
1275
+ }
1276
+ if (operation === 'retry') {
1277
+ const jobId = this.getNodeParameter('jobId', i);
1278
+ responseData = await this.helpers.httpRequest({
1279
+ method: 'POST',
1280
+ url: `${baseUrl}/jobs/${jobId}/retry`,
1281
+ json: true,
1282
+ headers: {
1283
+ 'x-api-key': credentials.apiKey,
1284
+ },
1285
+ });
1286
+ }
1287
+ if (operation === 'deleteFile') {
1288
+ const jobId = this.getNodeParameter('jobId', i);
1289
+ responseData = await this.helpers.httpRequest({
1290
+ method: 'DELETE',
1291
+ url: `${baseUrl}/jobs/${jobId}/file`,
1292
+ json: true,
1293
+ headers: {
1294
+ 'x-api-key': credentials.apiKey,
1295
+ },
1296
+ });
1297
+ }
1298
+ if (operation === 'getMetadata') {
1299
+ const url = this.getNodeParameter('metadataUrl', i);
1300
+ responseData = await this.helpers.httpRequest({
1301
+ method: 'POST',
1302
+ url: `${baseUrl}/metadata`,
1303
+ body: { url },
1304
+ json: true,
1305
+ headers: {
1306
+ 'x-api-key': credentials.apiKey,
1307
+ 'Content-Type': 'application/json',
1308
+ },
1309
+ });
1310
+ }
1311
+ if (operation === 'createBulk') {
1312
+ const bulkUrls = this.getNodeParameter('bulkUrls', i);
1313
+ const bulkOptions = this.getNodeParameter('bulkOptions', i);
1314
+ const jobs = (bulkUrls.urlItems || []).map((item) => ({
1315
+ url: item.url,
1316
+ filename: item.filename || undefined,
1317
+ }));
1318
+ const body = { jobs };
1319
+ if (bulkOptions.folder)
1320
+ body.folder = bulkOptions.folder;
1321
+ if (bulkOptions.format)
1322
+ body.format = bulkOptions.format;
1323
+ if (bulkOptions.video_codec)
1324
+ body.video_codec = bulkOptions.video_codec;
1325
+ if (bulkOptions.audio_codec)
1326
+ body.audio_codec = bulkOptions.audio_codec;
1327
+ if (bulkOptions.audio_bitrate)
1328
+ body.audio_bitrate = bulkOptions.audio_bitrate;
1329
+ if (bulkOptions.video_quality !== undefined)
1330
+ body.video_quality = bulkOptions.video_quality;
1331
+ if (bulkOptions.audio_only)
1332
+ body.audio_only = bulkOptions.audio_only;
1333
+ if (bulkOptions.download_subtitles)
1334
+ body.download_subtitles = bulkOptions.download_subtitles;
1335
+ if (bulkOptions.download_thumbnail)
1336
+ body.download_thumbnail = bulkOptions.download_thumbnail;
1337
+ if (bulkOptions.quality_preset)
1338
+ body.quality_preset = bulkOptions.quality_preset;
1339
+ if (bulkOptions.max_resolution && bulkOptions.max_resolution !== 'best')
1340
+ body.max_resolution = bulkOptions.max_resolution;
1341
+ if (bulkOptions.clip_start)
1342
+ body.clip_start = bulkOptions.clip_start;
1343
+ if (bulkOptions.clip_end)
1344
+ body.clip_end = bulkOptions.clip_end;
1345
+ if (bulkOptions.live_recording)
1346
+ body.live_recording = bulkOptions.live_recording;
1347
+ if (bulkOptions.live_from_start)
1348
+ body.live_from_start = bulkOptions.live_from_start;
1349
+ if (bulkOptions.max_duration && bulkOptions.max_duration > 0)
1350
+ body.max_duration = bulkOptions.max_duration;
1351
+ if (bulkOptions.wait_for_live)
1352
+ body.wait_for_live = bulkOptions.wait_for_live;
1353
+ responseData = await this.helpers.httpRequest({
1354
+ method: 'POST',
1355
+ url: `${baseUrl}/jobs/bulk`,
1356
+ body,
1357
+ json: true,
1358
+ headers: {
1359
+ 'x-api-key': credentials.apiKey,
1360
+ 'Content-Type': 'application/json',
1361
+ },
1362
+ });
1363
+ }
451
1364
  }
452
1365
  // ==================== BATCH ====================
453
1366
  if (resource === 'batch') {
@@ -462,36 +1375,101 @@ class TornadoApi {
462
1375
  },
463
1376
  });
464
1377
  }
465
- }
466
- // ==================== STORAGE ====================
467
- if (resource === 'storage') {
468
- if (operation === 'configureBucket') {
469
- const endpoint = this.getNodeParameter('endpoint', i);
470
- const bucket = this.getNodeParameter('bucket', i);
471
- const region = this.getNodeParameter('region', i);
472
- const accessKey = this.getNodeParameter('accessKey', i);
473
- const secretKey = this.getNodeParameter('secretKey', i);
1378
+ if (operation === 'renameJobs') {
1379
+ const batchId = this.getNodeParameter('batchId', i);
1380
+ const renames = this.getNodeParameter('renames', i);
1381
+ responseData = await this.helpers.httpRequest({
1382
+ method: 'PATCH',
1383
+ url: `${baseUrl}/batch/${batchId}/jobs`,
1384
+ body: { renames: renames.renameItems || [] },
1385
+ json: true,
1386
+ headers: {
1387
+ 'x-api-key': credentials.apiKey,
1388
+ 'Content-Type': 'application/json',
1389
+ },
1390
+ });
1391
+ }
1392
+ if (operation === 'start') {
1393
+ const batchId = this.getNodeParameter('batchId', i);
474
1394
  responseData = await this.helpers.httpRequest({
475
1395
  method: 'POST',
476
- url: `${baseUrl}/user/bucket`,
477
- body: {
478
- endpoint,
479
- bucket,
480
- region,
481
- access_key: accessKey,
482
- secret_key: secretKey,
1396
+ url: `${baseUrl}/batch/${batchId}/start`,
1397
+ json: true,
1398
+ headers: {
1399
+ 'x-api-key': credentials.apiKey,
483
1400
  },
1401
+ });
1402
+ }
1403
+ }
1404
+ // ==================== DASHBOARD ====================
1405
+ if (resource === 'dashboard') {
1406
+ const dashboardBaseUrl = (credentials.dashboardBaseUrl || '').replace(/\/$/, '');
1407
+ if (!dashboardBaseUrl) {
1408
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Dashboard Base URL is not configured. Please set the Dashboard Base URL in your Tornado API credentials to use Dashboard operations.');
1409
+ }
1410
+ if (operation === 'getStats') {
1411
+ responseData = await this.helpers.httpRequest({
1412
+ method: 'GET',
1413
+ url: `${dashboardBaseUrl}/dashboard/stats`,
484
1414
  json: true,
485
1415
  headers: {
486
1416
  'x-api-key': credentials.apiKey,
487
- 'Content-Type': 'application/json',
488
1417
  },
489
1418
  });
490
1419
  }
491
- if (operation === 'resetBucket') {
1420
+ if (operation === 'getBatches') {
492
1421
  responseData = await this.helpers.httpRequest({
493
- method: 'DELETE',
494
- url: `${baseUrl}/user/bucket`,
1422
+ method: 'GET',
1423
+ url: `${dashboardBaseUrl}/dashboard/batches`,
1424
+ json: true,
1425
+ headers: {
1426
+ 'x-api-key': credentials.apiKey,
1427
+ },
1428
+ });
1429
+ }
1430
+ if (operation === 'getDaily') {
1431
+ responseData = await this.helpers.httpRequest({
1432
+ method: 'GET',
1433
+ url: `${dashboardBaseUrl}/dashboard/daily`,
1434
+ json: true,
1435
+ headers: {
1436
+ 'x-api-key': credentials.apiKey,
1437
+ },
1438
+ });
1439
+ }
1440
+ if (operation === 'getCluster') {
1441
+ responseData = await this.helpers.httpRequest({
1442
+ method: 'GET',
1443
+ url: `${dashboardBaseUrl}/dashboard/cluster`,
1444
+ json: true,
1445
+ headers: {
1446
+ 'x-api-key': credentials.apiKey,
1447
+ },
1448
+ });
1449
+ }
1450
+ if (operation === 'getBilling') {
1451
+ responseData = await this.helpers.httpRequest({
1452
+ method: 'GET',
1453
+ url: `${dashboardBaseUrl}/dashboard/billing`,
1454
+ json: true,
1455
+ headers: {
1456
+ 'x-api-key': credentials.apiKey,
1457
+ },
1458
+ });
1459
+ }
1460
+ if (operation === 'getJobs') {
1461
+ const options = this.getNodeParameter('dashboardJobsOptions', i);
1462
+ const qs = {};
1463
+ if (options.limit)
1464
+ qs.limit = options.limit;
1465
+ if (options.offset)
1466
+ qs.offset = options.offset;
1467
+ if (options.status)
1468
+ qs.status = options.status;
1469
+ responseData = await this.helpers.httpRequest({
1470
+ method: 'GET',
1471
+ url: `${dashboardBaseUrl}/dashboard/jobs`,
1472
+ qs,
495
1473
  json: true,
496
1474
  headers: {
497
1475
  'x-api-key': credentials.apiKey,
@@ -499,6 +1477,100 @@ class TornadoApi {
499
1477
  });
500
1478
  }
501
1479
  }
1480
+ // ==================== STORAGE ====================
1481
+ if (resource === 'storage') {
1482
+ if (operation === 'configureS3') {
1483
+ const endpoint = this.getNodeParameter('s3Endpoint', i);
1484
+ const bucket = this.getNodeParameter('s3Bucket', i);
1485
+ const region = this.getNodeParameter('s3Region', i);
1486
+ const access_key = this.getNodeParameter('s3AccessKey', i);
1487
+ const secret_key = this.getNodeParameter('s3SecretKey', i);
1488
+ const opts = this.getNodeParameter('s3Options', i);
1489
+ const body = { endpoint, bucket, region, access_key, secret_key };
1490
+ if (opts.folder_prefix)
1491
+ body.folder_prefix = opts.folder_prefix;
1492
+ if (opts.folder_name)
1493
+ body.folder_name = opts.folder_name;
1494
+ responseData = await this.helpers.httpRequest({ method: 'POST', url: `${baseUrl}/user/s3`, body, json: true, headers: { 'x-api-key': credentials.apiKey, 'Content-Type': 'application/json' } });
1495
+ }
1496
+ if (operation === 'deleteS3') {
1497
+ responseData = await this.helpers.httpRequest({ method: 'DELETE', url: `${baseUrl}/user/s3`, json: true, headers: { 'x-api-key': credentials.apiKey } });
1498
+ }
1499
+ if (operation === 'configureBlob') {
1500
+ const account_name = this.getNodeParameter('blobAccountName', i);
1501
+ const container = this.getNodeParameter('blobContainer', i);
1502
+ const opts = this.getNodeParameter('blobOptions', i);
1503
+ const body = { account_name, container };
1504
+ if (opts.account_key)
1505
+ body.account_key = opts.account_key;
1506
+ if (opts.sas_token)
1507
+ body.sas_token = opts.sas_token;
1508
+ if (opts.folder_prefix)
1509
+ body.folder_prefix = opts.folder_prefix;
1510
+ if (opts.folder_name)
1511
+ body.folder_name = opts.folder_name;
1512
+ responseData = await this.helpers.httpRequest({ method: 'POST', url: `${baseUrl}/user/blob`, body, json: true, headers: { 'x-api-key': credentials.apiKey, 'Content-Type': 'application/json' } });
1513
+ }
1514
+ if (operation === 'deleteBlob') {
1515
+ responseData = await this.helpers.httpRequest({ method: 'DELETE', url: `${baseUrl}/user/blob`, json: true, headers: { 'x-api-key': credentials.apiKey } });
1516
+ }
1517
+ if (operation === 'configureGcs') {
1518
+ const project_id = this.getNodeParameter('gcsProjectId', i);
1519
+ const bucket = this.getNodeParameter('gcsBucket', i);
1520
+ const service_account_json = this.getNodeParameter('gcsServiceAccountJson', i);
1521
+ const opts = this.getNodeParameter('gcsOptions', i);
1522
+ const body = { project_id, bucket, service_account_json };
1523
+ if (opts.folder_prefix)
1524
+ body.folder_prefix = opts.folder_prefix;
1525
+ if (opts.folder_name)
1526
+ body.folder_name = opts.folder_name;
1527
+ responseData = await this.helpers.httpRequest({ method: 'POST', url: `${baseUrl}/user/gcs`, body, json: true, headers: { 'x-api-key': credentials.apiKey, 'Content-Type': 'application/json' } });
1528
+ }
1529
+ if (operation === 'deleteGcs') {
1530
+ responseData = await this.helpers.httpRequest({ method: 'DELETE', url: `${baseUrl}/user/gcs`, json: true, headers: { 'x-api-key': credentials.apiKey } });
1531
+ }
1532
+ if (operation === 'configureOss') {
1533
+ const endpoint = this.getNodeParameter('ossEndpoint', i);
1534
+ const bucket = this.getNodeParameter('ossBucket', i);
1535
+ const access_key_id = this.getNodeParameter('ossAccessKeyId', i);
1536
+ const access_key_secret = this.getNodeParameter('ossAccessKeySecret', i);
1537
+ const opts = this.getNodeParameter('ossOptions', i);
1538
+ const body = { endpoint, bucket, access_key_id, access_key_secret };
1539
+ if (opts.folder_prefix)
1540
+ body.folder_prefix = opts.folder_prefix;
1541
+ if (opts.folder_name)
1542
+ body.folder_name = opts.folder_name;
1543
+ responseData = await this.helpers.httpRequest({ method: 'POST', url: `${baseUrl}/user/oss`, body, json: true, headers: { 'x-api-key': credentials.apiKey, 'Content-Type': 'application/json' } });
1544
+ }
1545
+ if (operation === 'deleteOss') {
1546
+ responseData = await this.helpers.httpRequest({ method: 'DELETE', url: `${baseUrl}/user/oss`, json: true, headers: { 'x-api-key': credentials.apiKey } });
1547
+ }
1548
+ if (operation === 'configureBucket') {
1549
+ const endpoint = this.getNodeParameter('endpoint', i);
1550
+ const bucket = this.getNodeParameter('bucket', i);
1551
+ const region = this.getNodeParameter('region', i);
1552
+ const accessKey = this.getNodeParameter('accessKey', i);
1553
+ const secretKey = this.getNodeParameter('secretKey', i);
1554
+ responseData = await this.helpers.httpRequest({ method: 'POST', url: `${baseUrl}/user/bucket`, body: { endpoint, bucket, region, access_key: accessKey, secret_key: secretKey }, json: true, headers: { 'x-api-key': credentials.apiKey, 'Content-Type': 'application/json' } });
1555
+ }
1556
+ if (operation === 'resetBucket') {
1557
+ responseData = await this.helpers.httpRequest({ method: 'DELETE', url: `${baseUrl}/user/bucket`, json: true, headers: { 'x-api-key': credentials.apiKey } });
1558
+ }
1559
+ }
1560
+ // ==================== NOTIFICATION ====================
1561
+ if (resource === 'notification') {
1562
+ if (operation === 'configureSlack') {
1563
+ const webhook_url = this.getNodeParameter('slackWebhookUrl', i);
1564
+ const notify_level = this.getNodeParameter('slackNotifyLevel', i);
1565
+ const body = { webhook_url };
1566
+ if (notify_level)
1567
+ body.notify_level = notify_level;
1568
+ responseData = await this.helpers.httpRequest({ method: 'POST', url: `${baseUrl}/user/slack`, body, json: true, headers: { 'x-api-key': credentials.apiKey, 'Content-Type': 'application/json' } });
1569
+ }
1570
+ if (operation === 'deleteSlack') {
1571
+ responseData = await this.helpers.httpRequest({ method: 'DELETE', url: `${baseUrl}/user/slack`, json: true, headers: { 'x-api-key': credentials.apiKey } });
1572
+ }
1573
+ }
502
1574
  // ==================== ACCOUNT ====================
503
1575
  if (resource === 'account') {
504
1576
  if (operation === 'getUsage') {