n8n-nodes-cloudinary 0.0.1

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,696 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Cloudinary = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const crypto_1 = require("crypto");
6
+ function generateCloudinarySignature(params, apiSecret) {
7
+ const { signature, api_key, file, ...paramsToSign } = params;
8
+ const sortedParams = Object.keys(paramsToSign)
9
+ .sort()
10
+ .map((key) => `${key}=${paramsToSign[key]}`)
11
+ .join('&');
12
+ const stringToSign = `${sortedParams}${apiSecret}`;
13
+ return (0, crypto_1.createHash)('sha1').update(stringToSign).digest('hex');
14
+ }
15
+ class Cloudinary {
16
+ constructor() {
17
+ this.description = {
18
+ displayName: 'Cloudinary',
19
+ name: 'cloudinary',
20
+ icon: 'file:cloudinary.svg',
21
+ group: ['Cloudinary'],
22
+ version: 1,
23
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
24
+ description: 'Upload to Cloudinary',
25
+ defaults: {
26
+ name: 'Cloudinary',
27
+ },
28
+ inputs: ["main"],
29
+ outputs: ["main"],
30
+ credentials: [
31
+ {
32
+ name: 'cloudinaryApi',
33
+ required: true,
34
+ },
35
+ ],
36
+ properties: [
37
+ {
38
+ displayName: 'Resource',
39
+ name: 'resource',
40
+ type: 'options',
41
+ noDataExpression: true,
42
+ options: [
43
+ {
44
+ name: 'Upload',
45
+ value: 'upload',
46
+ },
47
+ {
48
+ name: 'Update Asset',
49
+ value: 'updateAsset',
50
+ },
51
+ {
52
+ name: 'Admin',
53
+ value: 'admin',
54
+ },
55
+ ],
56
+ default: 'upload',
57
+ },
58
+ {
59
+ displayName: 'Operation',
60
+ name: 'operation',
61
+ type: 'options',
62
+ noDataExpression: true,
63
+ displayOptions: {
64
+ show: {
65
+ resource: ['upload'],
66
+ },
67
+ },
68
+ options: [
69
+ {
70
+ name: 'Upload From URL',
71
+ value: 'uploadUrl',
72
+ description: 'Upload an asset from URL',
73
+ action: 'Upload an asset from URL',
74
+ },
75
+ {
76
+ name: 'Upload File',
77
+ value: 'uploadFile',
78
+ description: 'Upload an asset from file data',
79
+ action: 'Upload an asset from file data',
80
+ },
81
+ ],
82
+ default: 'uploadUrl',
83
+ },
84
+ {
85
+ displayName: 'Operation',
86
+ name: 'operation',
87
+ type: 'options',
88
+ noDataExpression: true,
89
+ displayOptions: {
90
+ show: {
91
+ resource: ['updateAsset'],
92
+ },
93
+ },
94
+ options: [
95
+ {
96
+ name: 'Update Asset Tags',
97
+ value: 'updateTags',
98
+ description: 'Update tags for an existing asset',
99
+ action: 'Update asset tags',
100
+ },
101
+ {
102
+ name: 'Update Asset Structured Metadata',
103
+ value: 'updateMetadata',
104
+ description: 'Update structured metadata for an existing asset',
105
+ action: 'Update asset structured metadata',
106
+ },
107
+ ],
108
+ default: 'updateTags',
109
+ },
110
+ {
111
+ displayName: 'Operation',
112
+ name: 'operation',
113
+ type: 'options',
114
+ noDataExpression: true,
115
+ displayOptions: {
116
+ show: {
117
+ resource: ['admin'],
118
+ },
119
+ },
120
+ options: [
121
+ {
122
+ name: 'Get Tags',
123
+ value: 'getTags',
124
+ description: 'Get all tags for a specific resource type',
125
+ action: 'Get tags for a resource type',
126
+ },
127
+ {
128
+ name: 'Get Metadata Fields',
129
+ value: 'getMetadataFields',
130
+ description: 'Get all metadata fields definitions',
131
+ action: 'Get metadata fields definitions',
132
+ },
133
+ ],
134
+ default: 'getTags',
135
+ },
136
+ {
137
+ displayName: 'URL',
138
+ name: 'url',
139
+ type: 'string',
140
+ default: '',
141
+ description: 'URL of the image to upload',
142
+ required: true,
143
+ displayOptions: {
144
+ show: {
145
+ resource: ['upload'],
146
+ operation: ['uploadUrl'],
147
+ },
148
+ },
149
+ },
150
+ {
151
+ displayName: 'Resource Type',
152
+ name: 'resource_type',
153
+ type: 'options',
154
+ options: [
155
+ {
156
+ name: 'Image',
157
+ value: 'image',
158
+ },
159
+ {
160
+ name: 'Video',
161
+ value: 'video',
162
+ },
163
+ {
164
+ name: 'Raw',
165
+ value: 'raw',
166
+ },
167
+ ],
168
+ default: 'image',
169
+ description: 'The type of asset to upload',
170
+ displayOptions: {
171
+ show: {
172
+ resource: ['upload'],
173
+ operation: ['uploadUrl'],
174
+ },
175
+ },
176
+ },
177
+ {
178
+ displayName: 'Additional Fields',
179
+ name: 'additionalFields',
180
+ type: 'collection',
181
+ placeholder: 'Add Field',
182
+ displayOptions: {
183
+ show: {
184
+ resource: ['upload'],
185
+ operation: ['uploadUrl'],
186
+ },
187
+ },
188
+ default: {},
189
+ options: [
190
+ {
191
+ displayName: 'Public ID',
192
+ name: 'public_id',
193
+ type: 'string',
194
+ default: '',
195
+ description: 'The public ID of the resource',
196
+ },
197
+ {
198
+ displayName: 'Folder',
199
+ name: 'folder',
200
+ type: 'string',
201
+ default: '',
202
+ description: 'Folder name where the asset will be stored',
203
+ },
204
+ {
205
+ displayName: 'Upload Preset',
206
+ name: 'upload_preset',
207
+ type: 'string',
208
+ default: '',
209
+ description: 'Name of an upload preset that you defined for your Cloudinary account',
210
+ },
211
+ ],
212
+ },
213
+ {
214
+ displayName: 'Public ID',
215
+ name: 'publicId',
216
+ type: 'string',
217
+ default: '',
218
+ description: 'The public ID of the asset to update',
219
+ required: true,
220
+ displayOptions: {
221
+ show: {
222
+ resource: ['updateAsset'],
223
+ operation: ['updateTags', 'updateMetadata'],
224
+ },
225
+ },
226
+ },
227
+ {
228
+ displayName: 'Resource Type',
229
+ name: 'resourceType',
230
+ type: 'options',
231
+ options: [
232
+ {
233
+ name: 'Image',
234
+ value: 'image',
235
+ },
236
+ {
237
+ name: 'Video',
238
+ value: 'video',
239
+ },
240
+ {
241
+ name: 'Raw',
242
+ value: 'raw',
243
+ },
244
+ ],
245
+ default: 'image',
246
+ description: 'The type of asset to update',
247
+ displayOptions: {
248
+ show: {
249
+ resource: ['updateAsset'],
250
+ operation: ['updateTags', 'updateMetadata'],
251
+ },
252
+ },
253
+ },
254
+ {
255
+ displayName: 'Type',
256
+ name: 'type',
257
+ type: 'options',
258
+ options: [
259
+ {
260
+ name: 'Upload',
261
+ value: 'upload',
262
+ },
263
+ {
264
+ name: 'Private',
265
+ value: 'private',
266
+ },
267
+ {
268
+ name: 'Authenticated',
269
+ value: 'authenticated',
270
+ },
271
+ {
272
+ name: 'Fetch',
273
+ value: 'fetch',
274
+ },
275
+ ],
276
+ default: 'upload',
277
+ description: 'The storage type of the asset',
278
+ required: true,
279
+ displayOptions: {
280
+ show: {
281
+ resource: ['updateAsset'],
282
+ operation: ['updateTags', 'updateMetadata'],
283
+ },
284
+ },
285
+ },
286
+ {
287
+ displayName: 'Tags',
288
+ name: 'tags',
289
+ type: 'string',
290
+ default: '',
291
+ description: 'A comma-separated list of tag names to assign to the asset',
292
+ required: true,
293
+ displayOptions: {
294
+ show: {
295
+ resource: ['updateAsset'],
296
+ operation: ['updateTags'],
297
+ },
298
+ },
299
+ },
300
+ {
301
+ displayName: 'Structured Metadata',
302
+ name: 'structuredMetadata',
303
+ type: 'json',
304
+ default: '{}',
305
+ description: 'Structured metadata to attach to the asset as JSON. Example: {"field1": "value1", "field2": "value2"}.',
306
+ required: true,
307
+ displayOptions: {
308
+ show: {
309
+ resource: ['updateAsset'],
310
+ operation: ['updateMetadata'],
311
+ },
312
+ },
313
+ },
314
+ {
315
+ displayName: 'Resource Type',
316
+ name: 'getTagsResourceType',
317
+ type: 'options',
318
+ options: [
319
+ {
320
+ name: 'Image',
321
+ value: 'image',
322
+ },
323
+ {
324
+ name: 'Video',
325
+ value: 'video',
326
+ },
327
+ {
328
+ name: 'Raw',
329
+ value: 'raw',
330
+ },
331
+ ],
332
+ default: 'image',
333
+ description: 'The type of resource to get tags for',
334
+ required: true,
335
+ displayOptions: {
336
+ show: {
337
+ resource: ['admin'],
338
+ operation: ['getTags'],
339
+ },
340
+ },
341
+ },
342
+ {
343
+ displayName: 'Prefix',
344
+ name: 'tagsPrefix',
345
+ type: 'string',
346
+ default: '',
347
+ description: 'Filter tags that start with this prefix',
348
+ displayOptions: {
349
+ show: {
350
+ resource: ['admin'],
351
+ operation: ['getTags'],
352
+ },
353
+ },
354
+ },
355
+ {
356
+ displayName: 'Max Results',
357
+ name: 'tagsMaxResults',
358
+ type: 'number',
359
+ default: 100,
360
+ description: 'Maximum number of tags to return (1-500)',
361
+ typeOptions: {
362
+ minValue: 1,
363
+ maxValue: 500,
364
+ },
365
+ displayOptions: {
366
+ show: {
367
+ resource: ['admin'],
368
+ operation: ['getTags'],
369
+ },
370
+ },
371
+ },
372
+ {
373
+ displayName: 'Update Fields',
374
+ name: 'updateOptions',
375
+ type: 'collection',
376
+ placeholder: 'Add Option',
377
+ displayOptions: {
378
+ show: {
379
+ resource: ['updateAsset'],
380
+ operation: ['updateTags', 'updateMetadata'],
381
+ },
382
+ },
383
+ default: {},
384
+ options: [
385
+ {
386
+ displayName: 'Invalidate CDN',
387
+ name: 'invalidate',
388
+ type: 'boolean',
389
+ default: false,
390
+ description: 'Whether to invalidate CDN cache copies of the asset',
391
+ },
392
+ ],
393
+ },
394
+ {
395
+ displayName: 'File',
396
+ name: 'file',
397
+ type: 'string',
398
+ typeOptions: {
399
+ propertyType: 'binary',
400
+ },
401
+ default: 'data',
402
+ description: 'The file to upload',
403
+ required: true,
404
+ displayOptions: {
405
+ show: {
406
+ resource: ['upload'],
407
+ operation: ['uploadFile'],
408
+ },
409
+ },
410
+ },
411
+ {
412
+ displayName: 'Resource Type',
413
+ name: 'resource_type_file',
414
+ type: 'options',
415
+ options: [
416
+ {
417
+ name: 'Image',
418
+ value: 'image',
419
+ },
420
+ {
421
+ name: 'Video',
422
+ value: 'video',
423
+ },
424
+ {
425
+ name: 'Raw',
426
+ value: 'raw',
427
+ },
428
+ ],
429
+ default: 'image',
430
+ description: 'The type of asset to upload',
431
+ displayOptions: {
432
+ show: {
433
+ resource: ['upload'],
434
+ operation: ['uploadFile'],
435
+ },
436
+ },
437
+ },
438
+ {
439
+ displayName: 'Additional Fields',
440
+ name: 'additionalFieldsFile',
441
+ type: 'collection',
442
+ placeholder: 'Add Field',
443
+ displayOptions: {
444
+ show: {
445
+ resource: ['upload'],
446
+ operation: ['uploadFile'],
447
+ },
448
+ },
449
+ default: {},
450
+ options: [
451
+ {
452
+ displayName: 'Public ID',
453
+ name: 'public_id',
454
+ type: 'string',
455
+ default: '',
456
+ description: 'The public ID of the resource',
457
+ },
458
+ {
459
+ displayName: 'Folder',
460
+ name: 'folder',
461
+ type: 'string',
462
+ default: '',
463
+ description: 'Folder name where the asset will be stored',
464
+ },
465
+ {
466
+ displayName: 'Upload Preset',
467
+ name: 'upload_preset',
468
+ type: 'string',
469
+ default: '',
470
+ description: 'Name of an upload preset that you defined for your Cloudinary account',
471
+ },
472
+ ],
473
+ },
474
+ ],
475
+ };
476
+ }
477
+ async execute() {
478
+ const items = this.getInputData();
479
+ const returnData = [];
480
+ const credentials = await this.getCredentials('cloudinaryApi');
481
+ const cloudName = credentials.cloudName;
482
+ const apiKey = credentials.apiKey;
483
+ const apiSecret = credentials.apiSecret;
484
+ for (let i = 0; i < items.length; i++) {
485
+ try {
486
+ const resource = this.getNodeParameter('resource', i);
487
+ const operation = this.getNodeParameter('operation', i);
488
+ if (resource === 'upload' && operation === 'uploadUrl') {
489
+ const url = this.getNodeParameter('url', i);
490
+ const resourceType = this.getNodeParameter('resource_type', i);
491
+ const additionalFields = this.getNodeParameter('additionalFields', i, {});
492
+ const timestamp = Math.round(new Date().getTime() / 1000);
493
+ const params = {
494
+ timestamp,
495
+ api_key: apiKey,
496
+ file: url,
497
+ ...additionalFields,
498
+ };
499
+ const signature = generateCloudinarySignature(params, apiSecret);
500
+ params.signature = signature;
501
+ const uploadUrl = `https://api.cloudinary.com/v1_1/${cloudName}/${resourceType}/upload`;
502
+ const options = {
503
+ method: 'POST',
504
+ url: uploadUrl,
505
+ body: params,
506
+ headers: {
507
+ 'Content-Type': 'application/x-www-form-urlencoded',
508
+ },
509
+ };
510
+ const response = await this.helpers.httpRequest(options);
511
+ returnData.push({
512
+ json: response,
513
+ pairedItem: i,
514
+ });
515
+ }
516
+ if (resource === 'upload' && operation === 'uploadFile') {
517
+ const fileData = this.getNodeParameter('file', i);
518
+ const resourceType = this.getNodeParameter('resource_type_file', i);
519
+ const additionalFields = this.getNodeParameter('additionalFieldsFile', i, {});
520
+ const binaryPropertyName = fileData;
521
+ const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName);
522
+ const dataBuffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
523
+ const timestamp = Math.round(new Date().getTime() / 1000);
524
+ const params = {
525
+ timestamp,
526
+ api_key: apiKey,
527
+ ...additionalFields,
528
+ };
529
+ const signature = generateCloudinarySignature(params, apiSecret);
530
+ const uploadUrl = `https://api.cloudinary.com/v1_1/${cloudName}/${resourceType}/upload`;
531
+ const FormData = require('form-data');
532
+ const formData = new FormData();
533
+ formData.append('file', dataBuffer, {
534
+ filename: binaryData.fileName || 'file',
535
+ contentType: binaryData.mimeType || 'application/octet-stream',
536
+ });
537
+ formData.append('api_key', apiKey);
538
+ formData.append('timestamp', timestamp.toString());
539
+ formData.append('signature', signature);
540
+ for (const key in additionalFields) {
541
+ formData.append(key, additionalFields[key]);
542
+ }
543
+ const options = {
544
+ method: 'POST',
545
+ url: uploadUrl,
546
+ body: formData,
547
+ headers: {
548
+ ...formData.getHeaders(),
549
+ },
550
+ };
551
+ const response = await this.helpers.httpRequest(options);
552
+ returnData.push({
553
+ json: response,
554
+ pairedItem: i,
555
+ });
556
+ }
557
+ if (resource === 'admin' && operation === 'getTags') {
558
+ const resourceType = this.getNodeParameter('getTagsResourceType', i);
559
+ const prefix = this.getNodeParameter('tagsPrefix', i, '');
560
+ const maxResults = this.getNodeParameter('tagsMaxResults', i, 100);
561
+ const tagsUrl = `https://api.cloudinary.com/v1_1/${cloudName}/tags/${resourceType}`;
562
+ const queryParams = {};
563
+ if (prefix) {
564
+ queryParams.prefix = prefix;
565
+ }
566
+ if (maxResults) {
567
+ queryParams.max_results = maxResults;
568
+ }
569
+ const options = {
570
+ method: 'GET',
571
+ url: tagsUrl,
572
+ qs: queryParams,
573
+ headers: {
574
+ 'Content-Type': 'application/json',
575
+ },
576
+ auth: {
577
+ username: apiKey,
578
+ password: apiSecret,
579
+ },
580
+ };
581
+ const response = await this.helpers.httpRequest(options);
582
+ returnData.push({
583
+ json: response,
584
+ pairedItem: i,
585
+ });
586
+ }
587
+ if (resource === 'admin' && operation === 'getMetadataFields') {
588
+ const metadataUrl = `https://api.cloudinary.com/v1_1/${cloudName}/metadata_fields`;
589
+ const options = {
590
+ method: 'GET',
591
+ url: metadataUrl,
592
+ headers: {
593
+ 'Content-Type': 'application/json',
594
+ },
595
+ auth: {
596
+ username: apiKey,
597
+ password: apiSecret,
598
+ },
599
+ };
600
+ const response = await this.helpers.httpRequest(options);
601
+ returnData.push({
602
+ json: response,
603
+ pairedItem: i,
604
+ });
605
+ }
606
+ if (resource === 'updateAsset' && operation === 'updateTags') {
607
+ const publicId = this.getNodeParameter('publicId', i);
608
+ const resourceType = this.getNodeParameter('resourceType', i);
609
+ const type = this.getNodeParameter('type', i);
610
+ const tags = this.getNodeParameter('tags', i);
611
+ const updateOptions = this.getNodeParameter('updateOptions', i, {});
612
+ const body = {
613
+ tags: tags,
614
+ ...updateOptions,
615
+ };
616
+ const updateUrl = `https://api.cloudinary.com/v1_1/${cloudName}/resources/${resourceType}/${type}/${publicId}`;
617
+ const options = {
618
+ method: 'POST',
619
+ url: updateUrl,
620
+ body: body,
621
+ headers: {
622
+ 'Content-Type': 'application/json',
623
+ },
624
+ auth: {
625
+ username: apiKey,
626
+ password: apiSecret,
627
+ },
628
+ };
629
+ const response = await this.helpers.httpRequest(options);
630
+ returnData.push({
631
+ json: response,
632
+ pairedItem: i,
633
+ });
634
+ }
635
+ if (resource === 'updateAsset' && operation === 'updateMetadata') {
636
+ const publicId = this.getNodeParameter('publicId', i);
637
+ const resourceType = this.getNodeParameter('resourceType', i);
638
+ const type = this.getNodeParameter('type', i);
639
+ const structuredMetadata = this.getNodeParameter('structuredMetadata', i);
640
+ const updateOptions = this.getNodeParameter('updateOptions', i, {});
641
+ let metadata;
642
+ try {
643
+ metadata = typeof structuredMetadata === 'object' ? structuredMetadata : JSON.parse(structuredMetadata);
644
+ }
645
+ catch (error) {
646
+ throw new n8n_workflow_1.ApplicationError('Invalid JSON for structured metadata');
647
+ }
648
+ const metadataString = Object.keys(metadata)
649
+ .map((key) => {
650
+ const value = metadata[key];
651
+ const formattedValue = Array.isArray(value) ? JSON.stringify(value) : value;
652
+ return `${key}=${formattedValue}`;
653
+ })
654
+ .join('|');
655
+ const body = {
656
+ metadata: metadataString,
657
+ ...updateOptions,
658
+ };
659
+ const updateUrl = `https://api.cloudinary.com/v1_1/${cloudName}/resources/${resourceType}/${type}/${publicId}`;
660
+ const options = {
661
+ method: 'POST',
662
+ url: updateUrl,
663
+ body: body,
664
+ headers: {
665
+ 'Content-Type': 'application/json',
666
+ },
667
+ auth: {
668
+ username: apiKey,
669
+ password: apiSecret,
670
+ },
671
+ };
672
+ const response = await this.helpers.httpRequest(options);
673
+ returnData.push({
674
+ json: response,
675
+ pairedItem: i,
676
+ });
677
+ }
678
+ }
679
+ catch (error) {
680
+ if (this.continueOnFail()) {
681
+ returnData.push({
682
+ json: {
683
+ error: error.message,
684
+ },
685
+ pairedItem: i,
686
+ });
687
+ continue;
688
+ }
689
+ throw error;
690
+ }
691
+ }
692
+ return [returnData];
693
+ }
694
+ }
695
+ exports.Cloudinary = Cloudinary;
696
+ //# sourceMappingURL=Cloudinary.node.js.map