n8n-nodes-sudomock 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.
- package/README.md +527 -0
- package/dist/credentials/SudoMockApi.credentials.d.ts +9 -0
- package/dist/credentials/SudoMockApi.credentials.js +39 -0
- package/dist/nodes/SudoMock/SudoMock.node.d.ts +5 -0
- package/dist/nodes/SudoMock/SudoMock.node.js +809 -0
- package/dist/nodes/SudoMock/sudomock.svg +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,809 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SudoMock = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
class SudoMock {
|
|
6
|
+
description = {
|
|
7
|
+
displayName: 'SudoMock',
|
|
8
|
+
name: 'sudoMock',
|
|
9
|
+
icon: 'file:sudomock.svg',
|
|
10
|
+
group: ['transform'],
|
|
11
|
+
version: 1,
|
|
12
|
+
subtitle: '={{$parameter["operation"]}}',
|
|
13
|
+
description: 'Generate mockup images for Print-on-Demand automation. Upload PSDs, render with your designs.',
|
|
14
|
+
defaults: {
|
|
15
|
+
name: 'SudoMock',
|
|
16
|
+
},
|
|
17
|
+
inputs: ['main'],
|
|
18
|
+
outputs: ['main'],
|
|
19
|
+
credentials: [
|
|
20
|
+
{
|
|
21
|
+
name: 'sudoMockApi',
|
|
22
|
+
required: true,
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
requestDefaults: {
|
|
26
|
+
baseURL: 'https://api.sudomock.com',
|
|
27
|
+
headers: {
|
|
28
|
+
'Content-Type': 'application/json',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
properties: [
|
|
32
|
+
// ============================================
|
|
33
|
+
// OPERATION SELECT
|
|
34
|
+
// ============================================
|
|
35
|
+
{
|
|
36
|
+
displayName: 'Operation',
|
|
37
|
+
name: 'operation',
|
|
38
|
+
type: 'options',
|
|
39
|
+
noDataExpression: true,
|
|
40
|
+
options: [
|
|
41
|
+
{
|
|
42
|
+
name: 'Upload PSD',
|
|
43
|
+
value: 'uploadPsd',
|
|
44
|
+
description: 'Upload a PSD template from URL',
|
|
45
|
+
action: 'Upload a PSD template',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'Render Mockup',
|
|
49
|
+
value: 'render',
|
|
50
|
+
description: 'Render mockup with your design',
|
|
51
|
+
action: 'Render a mockup',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'Get Account Info',
|
|
55
|
+
value: 'getAccountInfo',
|
|
56
|
+
description: 'Get account details, subscription, and usage statistics',
|
|
57
|
+
action: 'Get account information',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'List Mockups',
|
|
61
|
+
value: 'listMockups',
|
|
62
|
+
description: 'List all your uploaded mockup templates',
|
|
63
|
+
action: 'List mockups',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'Get Mockup',
|
|
67
|
+
value: 'getMockup',
|
|
68
|
+
description: 'Get details of a specific mockup template',
|
|
69
|
+
action: 'Get mockup details',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'Update Mockup',
|
|
73
|
+
value: 'updateMockup',
|
|
74
|
+
description: 'Update mockup template name',
|
|
75
|
+
action: 'Update mockup name',
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'Delete Mockup',
|
|
79
|
+
value: 'deleteMockup',
|
|
80
|
+
description: 'Delete a specific mockup template',
|
|
81
|
+
action: 'Delete a mockup',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: 'Delete All Mockups',
|
|
85
|
+
value: 'deleteAllMockups',
|
|
86
|
+
description: 'Delete all your mockup templates',
|
|
87
|
+
action: 'Delete all mockups',
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
default: 'render',
|
|
91
|
+
},
|
|
92
|
+
// ============================================
|
|
93
|
+
// UPLOAD PSD PARAMETERS
|
|
94
|
+
// ============================================
|
|
95
|
+
{
|
|
96
|
+
displayName: 'PSD File URL',
|
|
97
|
+
name: 'psdFileUrl',
|
|
98
|
+
type: 'string',
|
|
99
|
+
required: true,
|
|
100
|
+
displayOptions: {
|
|
101
|
+
show: {
|
|
102
|
+
operation: ['uploadPsd'],
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
default: '',
|
|
106
|
+
placeholder: 'https://your-storage.com/mockup-template.psd',
|
|
107
|
+
description: 'Public URL to your PSD file (max 300MB). Use S3, GCS, or any public URL.',
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
displayName: 'Template Name',
|
|
111
|
+
name: 'psdName',
|
|
112
|
+
type: 'string',
|
|
113
|
+
displayOptions: {
|
|
114
|
+
show: {
|
|
115
|
+
operation: ['uploadPsd'],
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
default: '',
|
|
119
|
+
placeholder: 'T-Shirt Mockup Front',
|
|
120
|
+
description: 'Human-readable name for the template. Auto-generated from filename if empty.',
|
|
121
|
+
},
|
|
122
|
+
// ============================================
|
|
123
|
+
// RENDER PARAMETERS
|
|
124
|
+
// ============================================
|
|
125
|
+
{
|
|
126
|
+
displayName: 'Mockup UUID',
|
|
127
|
+
name: 'mockupUuid',
|
|
128
|
+
type: 'string',
|
|
129
|
+
required: true,
|
|
130
|
+
displayOptions: {
|
|
131
|
+
show: {
|
|
132
|
+
operation: ['render'],
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
default: '',
|
|
136
|
+
placeholder: 'c315f78f-d2c7-4541-b240-a9372842de94',
|
|
137
|
+
description: 'UUID of the uploaded mockup template (from Upload PSD response)',
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
displayName: 'Smart Objects',
|
|
141
|
+
name: 'smartObjects',
|
|
142
|
+
type: 'fixedCollection',
|
|
143
|
+
typeOptions: {
|
|
144
|
+
multipleValues: true,
|
|
145
|
+
},
|
|
146
|
+
displayOptions: {
|
|
147
|
+
show: {
|
|
148
|
+
operation: ['render'],
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
default: {},
|
|
152
|
+
placeholder: 'Add Smart Object',
|
|
153
|
+
description: 'Configure which smart objects to fill with your designs',
|
|
154
|
+
options: [
|
|
155
|
+
{
|
|
156
|
+
name: 'items',
|
|
157
|
+
displayName: 'Smart Object',
|
|
158
|
+
values: [
|
|
159
|
+
{
|
|
160
|
+
displayName: 'Smart Object UUID',
|
|
161
|
+
name: 'uuid',
|
|
162
|
+
type: 'string',
|
|
163
|
+
default: '',
|
|
164
|
+
required: true,
|
|
165
|
+
description: 'UUID of the smart object (from Upload PSD response)',
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
displayName: 'Design URL',
|
|
169
|
+
name: 'assetUrl',
|
|
170
|
+
type: 'string',
|
|
171
|
+
default: '',
|
|
172
|
+
required: true,
|
|
173
|
+
placeholder: 'https://cdn.example.com/design.png',
|
|
174
|
+
description: 'URL to your design image (PNG, JPG, WebP)',
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
displayName: 'Fit Mode',
|
|
178
|
+
name: 'fit',
|
|
179
|
+
type: 'options',
|
|
180
|
+
options: [
|
|
181
|
+
{
|
|
182
|
+
name: 'Fill',
|
|
183
|
+
value: 'fill',
|
|
184
|
+
description: 'Stretch to fill entire area',
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
name: 'Contain',
|
|
188
|
+
value: 'contain',
|
|
189
|
+
description: 'Fit inside, may leave space',
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: 'Cover',
|
|
193
|
+
value: 'cover',
|
|
194
|
+
description: 'Fill area, may crop edges (recommended)',
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
default: 'cover',
|
|
198
|
+
description: 'How to fit the design in the smart object bounds',
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
displayName: 'Additional Options',
|
|
202
|
+
name: 'additionalOptions',
|
|
203
|
+
type: 'collection',
|
|
204
|
+
placeholder: 'Add Option',
|
|
205
|
+
default: {},
|
|
206
|
+
options: [
|
|
207
|
+
{
|
|
208
|
+
displayName: 'Rotation',
|
|
209
|
+
name: 'rotate',
|
|
210
|
+
type: 'number',
|
|
211
|
+
typeOptions: {
|
|
212
|
+
minValue: -360,
|
|
213
|
+
maxValue: 360,
|
|
214
|
+
},
|
|
215
|
+
default: 0,
|
|
216
|
+
description: 'Rotation angle in degrees',
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
displayName: 'Color Overlay (Hex)',
|
|
220
|
+
name: 'colorHex',
|
|
221
|
+
type: 'string',
|
|
222
|
+
default: '',
|
|
223
|
+
placeholder: '#FF5733',
|
|
224
|
+
description: 'Apply color overlay to the design',
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
displayName: 'Color Blend Mode',
|
|
228
|
+
name: 'colorBlendMode',
|
|
229
|
+
type: 'options',
|
|
230
|
+
options: [
|
|
231
|
+
{ name: 'Normal', value: 'normal' },
|
|
232
|
+
{ name: 'Multiply', value: 'multiply' },
|
|
233
|
+
{ name: 'Screen', value: 'screen' },
|
|
234
|
+
{ name: 'Overlay', value: 'overlay' },
|
|
235
|
+
{ name: 'Darken', value: 'darken' },
|
|
236
|
+
{ name: 'Lighten', value: 'lighten' },
|
|
237
|
+
{ name: 'Color Dodge', value: 'color-dodge' },
|
|
238
|
+
{ name: 'Color Burn', value: 'color-burn' },
|
|
239
|
+
{ name: 'Hard Light', value: 'hard-light' },
|
|
240
|
+
{ name: 'Soft Light', value: 'soft-light' },
|
|
241
|
+
],
|
|
242
|
+
default: 'multiply',
|
|
243
|
+
description: 'Blend mode for color overlay',
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
displayName: 'Brightness',
|
|
247
|
+
name: 'brightness',
|
|
248
|
+
type: 'number',
|
|
249
|
+
typeOptions: {
|
|
250
|
+
minValue: -150,
|
|
251
|
+
maxValue: 150,
|
|
252
|
+
},
|
|
253
|
+
default: 0,
|
|
254
|
+
description: 'Brightness adjustment (-150 to 150)',
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
displayName: 'Contrast',
|
|
258
|
+
name: 'contrast',
|
|
259
|
+
type: 'number',
|
|
260
|
+
typeOptions: {
|
|
261
|
+
minValue: -100,
|
|
262
|
+
maxValue: 100,
|
|
263
|
+
},
|
|
264
|
+
default: 0,
|
|
265
|
+
description: 'Contrast adjustment (-100 to 100)',
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
displayName: 'Opacity',
|
|
269
|
+
name: 'opacity',
|
|
270
|
+
type: 'number',
|
|
271
|
+
typeOptions: {
|
|
272
|
+
minValue: 0,
|
|
273
|
+
maxValue: 100,
|
|
274
|
+
},
|
|
275
|
+
default: 100,
|
|
276
|
+
description: 'Layer opacity (0-100)',
|
|
277
|
+
},
|
|
278
|
+
],
|
|
279
|
+
},
|
|
280
|
+
],
|
|
281
|
+
},
|
|
282
|
+
],
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
displayName: 'Export Options',
|
|
286
|
+
name: 'exportOptions',
|
|
287
|
+
type: 'collection',
|
|
288
|
+
placeholder: 'Add Export Option',
|
|
289
|
+
displayOptions: {
|
|
290
|
+
show: {
|
|
291
|
+
operation: ['render'],
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
default: {},
|
|
295
|
+
options: [
|
|
296
|
+
{
|
|
297
|
+
displayName: 'Image Format',
|
|
298
|
+
name: 'imageFormat',
|
|
299
|
+
type: 'options',
|
|
300
|
+
options: [
|
|
301
|
+
{
|
|
302
|
+
name: 'WebP (Recommended)',
|
|
303
|
+
value: 'webp',
|
|
304
|
+
description: '~30% smaller than PNG with similar quality',
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
name: 'PNG',
|
|
308
|
+
value: 'png',
|
|
309
|
+
description: 'Lossless, supports transparency',
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
name: 'JPEG',
|
|
313
|
+
value: 'jpg',
|
|
314
|
+
description: 'Smaller file size, no transparency',
|
|
315
|
+
},
|
|
316
|
+
],
|
|
317
|
+
default: 'webp',
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
displayName: 'Image Size (Width)',
|
|
321
|
+
name: 'imageSize',
|
|
322
|
+
type: 'number',
|
|
323
|
+
typeOptions: {
|
|
324
|
+
minValue: 100,
|
|
325
|
+
maxValue: 8000,
|
|
326
|
+
},
|
|
327
|
+
default: 1920,
|
|
328
|
+
description: 'Output width in pixels (100-8000). Height scales proportionally.',
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
displayName: 'Quality',
|
|
332
|
+
name: 'quality',
|
|
333
|
+
type: 'number',
|
|
334
|
+
typeOptions: {
|
|
335
|
+
minValue: 1,
|
|
336
|
+
maxValue: 100,
|
|
337
|
+
},
|
|
338
|
+
default: 95,
|
|
339
|
+
description: 'Quality for JPG/WebP output (1-100)',
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
displayName: 'Export Label',
|
|
343
|
+
name: 'exportLabel',
|
|
344
|
+
type: 'string',
|
|
345
|
+
default: '',
|
|
346
|
+
description: 'Optional label for the output file naming',
|
|
347
|
+
},
|
|
348
|
+
],
|
|
349
|
+
},
|
|
350
|
+
// ============================================
|
|
351
|
+
// LIST MOCKUPS PARAMETERS
|
|
352
|
+
// ============================================
|
|
353
|
+
{
|
|
354
|
+
displayName: 'Return All',
|
|
355
|
+
name: 'returnAll',
|
|
356
|
+
type: 'boolean',
|
|
357
|
+
displayOptions: {
|
|
358
|
+
show: {
|
|
359
|
+
operation: ['listMockups'],
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
default: false,
|
|
363
|
+
description: 'Whether to return all results or only up to a given limit',
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
displayName: 'Limit',
|
|
367
|
+
name: 'limit',
|
|
368
|
+
type: 'number',
|
|
369
|
+
displayOptions: {
|
|
370
|
+
show: {
|
|
371
|
+
operation: ['listMockups'],
|
|
372
|
+
returnAll: [false],
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
typeOptions: {
|
|
376
|
+
minValue: 1,
|
|
377
|
+
maxValue: 100,
|
|
378
|
+
},
|
|
379
|
+
default: 20,
|
|
380
|
+
description: 'Max number of results to return (1-100)',
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
displayName: 'Additional Options',
|
|
384
|
+
name: 'additionalOptions',
|
|
385
|
+
type: 'collection',
|
|
386
|
+
placeholder: 'Add Option',
|
|
387
|
+
displayOptions: {
|
|
388
|
+
show: {
|
|
389
|
+
operation: ['listMockups'],
|
|
390
|
+
},
|
|
391
|
+
},
|
|
392
|
+
default: {},
|
|
393
|
+
options: [
|
|
394
|
+
{
|
|
395
|
+
displayName: 'Filter by Name',
|
|
396
|
+
name: 'name',
|
|
397
|
+
type: 'string',
|
|
398
|
+
default: '',
|
|
399
|
+
description: 'Filter mockups by exact name match',
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
displayName: 'Created After',
|
|
403
|
+
name: 'created_after',
|
|
404
|
+
type: 'dateTime',
|
|
405
|
+
default: '',
|
|
406
|
+
description: 'Filter mockups created after this date',
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
displayName: 'Created Before',
|
|
410
|
+
name: 'created_before',
|
|
411
|
+
type: 'dateTime',
|
|
412
|
+
default: '',
|
|
413
|
+
description: 'Filter mockups created before this date',
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
displayName: 'Sort By',
|
|
417
|
+
name: 'sort',
|
|
418
|
+
type: 'options',
|
|
419
|
+
options: [
|
|
420
|
+
{ name: 'Created At', value: 'created_at' },
|
|
421
|
+
{ name: 'Updated At', value: 'updated_at' },
|
|
422
|
+
{ name: 'Name', value: 'name' },
|
|
423
|
+
],
|
|
424
|
+
default: 'created_at',
|
|
425
|
+
description: 'Field to sort results by',
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
displayName: 'Sort Order',
|
|
429
|
+
name: 'order',
|
|
430
|
+
type: 'options',
|
|
431
|
+
options: [
|
|
432
|
+
{ name: 'Ascending', value: 'asc' },
|
|
433
|
+
{ name: 'Descending', value: 'desc' },
|
|
434
|
+
],
|
|
435
|
+
default: 'desc',
|
|
436
|
+
description: 'Sort order for results',
|
|
437
|
+
},
|
|
438
|
+
],
|
|
439
|
+
},
|
|
440
|
+
// ============================================
|
|
441
|
+
// GET MOCKUP PARAMETERS
|
|
442
|
+
// ============================================
|
|
443
|
+
{
|
|
444
|
+
displayName: 'Mockup UUID',
|
|
445
|
+
name: 'getMockupUuid',
|
|
446
|
+
type: 'string',
|
|
447
|
+
required: true,
|
|
448
|
+
displayOptions: {
|
|
449
|
+
show: {
|
|
450
|
+
operation: ['getMockup'],
|
|
451
|
+
},
|
|
452
|
+
},
|
|
453
|
+
default: '',
|
|
454
|
+
placeholder: 'c315f78f-d2c7-4541-b240-a9372842de94',
|
|
455
|
+
description: 'UUID of the mockup template to retrieve',
|
|
456
|
+
},
|
|
457
|
+
// ============================================
|
|
458
|
+
// UPDATE MOCKUP PARAMETERS
|
|
459
|
+
// ============================================
|
|
460
|
+
{
|
|
461
|
+
displayName: 'Mockup UUID',
|
|
462
|
+
name: 'updateMockupUuid',
|
|
463
|
+
type: 'string',
|
|
464
|
+
required: true,
|
|
465
|
+
displayOptions: {
|
|
466
|
+
show: {
|
|
467
|
+
operation: ['updateMockup'],
|
|
468
|
+
},
|
|
469
|
+
},
|
|
470
|
+
default: '',
|
|
471
|
+
placeholder: 'c315f78f-d2c7-4541-b240-a9372842de94',
|
|
472
|
+
description: 'UUID of the mockup template to update',
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
displayName: 'New Name',
|
|
476
|
+
name: 'newName',
|
|
477
|
+
type: 'string',
|
|
478
|
+
required: true,
|
|
479
|
+
displayOptions: {
|
|
480
|
+
show: {
|
|
481
|
+
operation: ['updateMockup'],
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
default: '',
|
|
485
|
+
placeholder: 'Updated Mockup Name',
|
|
486
|
+
description: 'New name for the mockup template',
|
|
487
|
+
},
|
|
488
|
+
// ============================================
|
|
489
|
+
// DELETE MOCKUP PARAMETERS
|
|
490
|
+
// ============================================
|
|
491
|
+
{
|
|
492
|
+
displayName: 'Mockup UUID',
|
|
493
|
+
name: 'deleteMockupUuid',
|
|
494
|
+
type: 'string',
|
|
495
|
+
required: true,
|
|
496
|
+
displayOptions: {
|
|
497
|
+
show: {
|
|
498
|
+
operation: ['deleteMockup'],
|
|
499
|
+
},
|
|
500
|
+
},
|
|
501
|
+
default: '',
|
|
502
|
+
description: 'UUID of the mockup template to delete',
|
|
503
|
+
},
|
|
504
|
+
],
|
|
505
|
+
};
|
|
506
|
+
async execute() {
|
|
507
|
+
const items = this.getInputData();
|
|
508
|
+
const returnData = [];
|
|
509
|
+
const operation = this.getNodeParameter('operation', 0);
|
|
510
|
+
for (let i = 0; i < items.length; i++) {
|
|
511
|
+
try {
|
|
512
|
+
// ========================================
|
|
513
|
+
// UPLOAD PSD
|
|
514
|
+
// ========================================
|
|
515
|
+
if (operation === 'uploadPsd') {
|
|
516
|
+
const psdFileUrl = this.getNodeParameter('psdFileUrl', i);
|
|
517
|
+
const psdName = this.getNodeParameter('psdName', i);
|
|
518
|
+
const body = {
|
|
519
|
+
psd_file_url: psdFileUrl,
|
|
520
|
+
};
|
|
521
|
+
if (psdName) {
|
|
522
|
+
body.psd_name = psdName;
|
|
523
|
+
}
|
|
524
|
+
const response = await this.helpers.httpRequestWithAuthentication.call(this, 'sudoMockApi', {
|
|
525
|
+
method: 'POST',
|
|
526
|
+
url: 'https://api.sudomock.com/api/v1/psd/upload',
|
|
527
|
+
body,
|
|
528
|
+
json: true,
|
|
529
|
+
});
|
|
530
|
+
// Response: { success: true, data: { uuid, name, thumbnail, smart_objects, ... } }
|
|
531
|
+
returnData.push({
|
|
532
|
+
json: response,
|
|
533
|
+
pairedItem: { item: i },
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
// ========================================
|
|
537
|
+
// RENDER MOCKUP
|
|
538
|
+
// ========================================
|
|
539
|
+
else if (operation === 'render') {
|
|
540
|
+
const mockupUuid = this.getNodeParameter('mockupUuid', i);
|
|
541
|
+
const smartObjectsData = this.getNodeParameter('smartObjects.items', i, []);
|
|
542
|
+
const exportOptions = this.getNodeParameter('exportOptions', i, {});
|
|
543
|
+
// Convert smart objects array to API format
|
|
544
|
+
const smartObjects = smartObjectsData.map((so) => {
|
|
545
|
+
const smartObject = {
|
|
546
|
+
uuid: so.uuid,
|
|
547
|
+
asset: {
|
|
548
|
+
url: so.assetUrl,
|
|
549
|
+
fit: so.fit,
|
|
550
|
+
},
|
|
551
|
+
};
|
|
552
|
+
// Add additional options if present
|
|
553
|
+
if (so.additionalOptions) {
|
|
554
|
+
const opts = so.additionalOptions;
|
|
555
|
+
// Rotation
|
|
556
|
+
if (opts.rotate !== undefined && opts.rotate !== 0) {
|
|
557
|
+
smartObject.asset.rotate = opts.rotate;
|
|
558
|
+
}
|
|
559
|
+
// Color overlay
|
|
560
|
+
if (opts.colorHex) {
|
|
561
|
+
smartObject.color = {
|
|
562
|
+
hex: opts.colorHex,
|
|
563
|
+
blending_mode: opts.colorBlendMode || 'multiply',
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
// Adjustment layers
|
|
567
|
+
const adjustments = {};
|
|
568
|
+
if (opts.brightness !== undefined && opts.brightness !== 0) {
|
|
569
|
+
adjustments.brightness = opts.brightness;
|
|
570
|
+
}
|
|
571
|
+
if (opts.contrast !== undefined && opts.contrast !== 0) {
|
|
572
|
+
adjustments.contrast = opts.contrast;
|
|
573
|
+
}
|
|
574
|
+
if (opts.opacity !== undefined && opts.opacity !== 100) {
|
|
575
|
+
adjustments.opacity = opts.opacity;
|
|
576
|
+
}
|
|
577
|
+
if (Object.keys(adjustments).length > 0) {
|
|
578
|
+
smartObject.adjustment_layers = adjustments;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return smartObject;
|
|
582
|
+
});
|
|
583
|
+
// Request body
|
|
584
|
+
const body = {
|
|
585
|
+
mockup_uuid: mockupUuid,
|
|
586
|
+
smart_objects: smartObjects,
|
|
587
|
+
};
|
|
588
|
+
// Export options
|
|
589
|
+
if (Object.keys(exportOptions).length > 0) {
|
|
590
|
+
const expOpts = {};
|
|
591
|
+
if (exportOptions.imageFormat) {
|
|
592
|
+
expOpts.image_format = exportOptions.imageFormat;
|
|
593
|
+
}
|
|
594
|
+
if (exportOptions.imageSize) {
|
|
595
|
+
expOpts.image_size = exportOptions.imageSize;
|
|
596
|
+
}
|
|
597
|
+
if (exportOptions.quality) {
|
|
598
|
+
expOpts.quality = exportOptions.quality;
|
|
599
|
+
}
|
|
600
|
+
if (Object.keys(expOpts).length > 0) {
|
|
601
|
+
body.export_options = expOpts;
|
|
602
|
+
}
|
|
603
|
+
if (exportOptions.exportLabel) {
|
|
604
|
+
body.export_label = exportOptions.exportLabel;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
const response = await this.helpers.httpRequestWithAuthentication.call(this, 'sudoMockApi', {
|
|
608
|
+
method: 'POST',
|
|
609
|
+
url: 'https://api.sudomock.com/api/v1/renders',
|
|
610
|
+
body,
|
|
611
|
+
json: true,
|
|
612
|
+
});
|
|
613
|
+
// Response: { success: true, data: { print_files: [{ export_path, smart_object_uuid }] } }
|
|
614
|
+
// Make export paths more easily accessible
|
|
615
|
+
const outputJson = { ...response };
|
|
616
|
+
if (response.data?.print_files?.length > 0) {
|
|
617
|
+
// Extract first rendered image URL to top level
|
|
618
|
+
outputJson.renderedImageUrl = response.data.print_files[0].export_path;
|
|
619
|
+
// Also add all URLs as an array
|
|
620
|
+
outputJson.allRenderedUrls = response.data.print_files.map((pf) => pf.export_path);
|
|
621
|
+
}
|
|
622
|
+
returnData.push({
|
|
623
|
+
json: outputJson,
|
|
624
|
+
pairedItem: { item: i },
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
// ========================================
|
|
628
|
+
// GET ACCOUNT INFO
|
|
629
|
+
// ========================================
|
|
630
|
+
else if (operation === 'getAccountInfo') {
|
|
631
|
+
const response = await this.helpers.httpRequestWithAuthentication.call(this, 'sudoMockApi', {
|
|
632
|
+
method: 'GET',
|
|
633
|
+
url: 'https://api.sudomock.com/api/v1/me',
|
|
634
|
+
json: true,
|
|
635
|
+
});
|
|
636
|
+
returnData.push({
|
|
637
|
+
json: response,
|
|
638
|
+
pairedItem: { item: i },
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
// ========================================
|
|
642
|
+
// LIST MOCKUPS
|
|
643
|
+
// ========================================
|
|
644
|
+
else if (operation === 'listMockups') {
|
|
645
|
+
const returnAll = this.getNodeParameter('returnAll', i);
|
|
646
|
+
const additionalOptions = this.getNodeParameter('additionalOptions', i, {});
|
|
647
|
+
let allMockups = [];
|
|
648
|
+
let offset = 0;
|
|
649
|
+
const limit = returnAll ? 100 : this.getNodeParameter('limit', i);
|
|
650
|
+
do {
|
|
651
|
+
// Build query parameters
|
|
652
|
+
const queryParams = {
|
|
653
|
+
limit: limit.toString(),
|
|
654
|
+
offset: offset.toString(),
|
|
655
|
+
};
|
|
656
|
+
if (additionalOptions.name) {
|
|
657
|
+
queryParams.name = additionalOptions.name;
|
|
658
|
+
}
|
|
659
|
+
if (additionalOptions.created_after) {
|
|
660
|
+
queryParams.created_after = additionalOptions.created_after;
|
|
661
|
+
}
|
|
662
|
+
if (additionalOptions.created_before) {
|
|
663
|
+
queryParams.created_before = additionalOptions.created_before;
|
|
664
|
+
}
|
|
665
|
+
if (additionalOptions.sort) {
|
|
666
|
+
queryParams.sort = additionalOptions.sort;
|
|
667
|
+
}
|
|
668
|
+
if (additionalOptions.order) {
|
|
669
|
+
queryParams.order = additionalOptions.order;
|
|
670
|
+
}
|
|
671
|
+
const response = await this.helpers.httpRequestWithAuthentication.call(this, 'sudoMockApi', {
|
|
672
|
+
method: 'GET',
|
|
673
|
+
url: 'https://api.sudomock.com/api/v1/mockups',
|
|
674
|
+
qs: queryParams,
|
|
675
|
+
json: true,
|
|
676
|
+
});
|
|
677
|
+
const mockups = response.data?.mockups || [];
|
|
678
|
+
allMockups = allMockups.concat(mockups);
|
|
679
|
+
if (!returnAll || mockups.length < limit) {
|
|
680
|
+
break;
|
|
681
|
+
}
|
|
682
|
+
offset += limit;
|
|
683
|
+
} while (returnAll);
|
|
684
|
+
// Return all mockups as separate items
|
|
685
|
+
allMockups.forEach((mockup) => {
|
|
686
|
+
returnData.push({
|
|
687
|
+
json: mockup,
|
|
688
|
+
pairedItem: { item: i },
|
|
689
|
+
});
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
// ========================================
|
|
693
|
+
// GET MOCKUP
|
|
694
|
+
// ========================================
|
|
695
|
+
else if (operation === 'getMockup') {
|
|
696
|
+
const mockupUuid = this.getNodeParameter('getMockupUuid', i);
|
|
697
|
+
const response = await this.helpers.httpRequestWithAuthentication.call(this, 'sudoMockApi', {
|
|
698
|
+
method: 'GET',
|
|
699
|
+
url: `https://api.sudomock.com/api/v1/mockups/${mockupUuid}`,
|
|
700
|
+
json: true,
|
|
701
|
+
});
|
|
702
|
+
returnData.push({
|
|
703
|
+
json: response,
|
|
704
|
+
pairedItem: { item: i },
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
// ========================================
|
|
708
|
+
// UPDATE MOCKUP
|
|
709
|
+
// ========================================
|
|
710
|
+
else if (operation === 'updateMockup') {
|
|
711
|
+
const mockupUuid = this.getNodeParameter('updateMockupUuid', i);
|
|
712
|
+
const newName = this.getNodeParameter('newName', i);
|
|
713
|
+
const response = await this.helpers.httpRequestWithAuthentication.call(this, 'sudoMockApi', {
|
|
714
|
+
method: 'PATCH',
|
|
715
|
+
url: `https://api.sudomock.com/api/v1/mockups/${mockupUuid}`,
|
|
716
|
+
body: {
|
|
717
|
+
name: newName,
|
|
718
|
+
},
|
|
719
|
+
json: true,
|
|
720
|
+
});
|
|
721
|
+
returnData.push({
|
|
722
|
+
json: response,
|
|
723
|
+
pairedItem: { item: i },
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
// ========================================
|
|
727
|
+
// DELETE MOCKUP
|
|
728
|
+
// ========================================
|
|
729
|
+
else if (operation === 'deleteMockup') {
|
|
730
|
+
const mockupUuid = this.getNodeParameter('deleteMockupUuid', i);
|
|
731
|
+
const response = await this.helpers.httpRequestWithAuthentication.call(this, 'sudoMockApi', {
|
|
732
|
+
method: 'DELETE',
|
|
733
|
+
url: `https://api.sudomock.com/api/v1/psd/${mockupUuid}`,
|
|
734
|
+
json: true,
|
|
735
|
+
});
|
|
736
|
+
returnData.push({
|
|
737
|
+
json: response,
|
|
738
|
+
pairedItem: { item: i },
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
// ========================================
|
|
742
|
+
// DELETE ALL MOCKUPS
|
|
743
|
+
// ========================================
|
|
744
|
+
else if (operation === 'deleteAllMockups') {
|
|
745
|
+
const response = await this.helpers.httpRequestWithAuthentication.call(this, 'sudoMockApi', {
|
|
746
|
+
method: 'DELETE',
|
|
747
|
+
url: 'https://api.sudomock.com/api/v1/mockups/all',
|
|
748
|
+
json: true,
|
|
749
|
+
});
|
|
750
|
+
returnData.push({
|
|
751
|
+
json: response,
|
|
752
|
+
pairedItem: { item: i },
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
catch (error) {
|
|
757
|
+
// Enhanced rate limit error handling
|
|
758
|
+
if (error.statusCode === 429) {
|
|
759
|
+
const headers = error.response?.headers || {};
|
|
760
|
+
const retryAfter = headers['retry-after'] || headers['Retry-After'] || '60';
|
|
761
|
+
const rateLimitReset = headers['ratelimit-reset'] || headers['RateLimit-Reset'];
|
|
762
|
+
const errorBody = error.response?.body?.error || {};
|
|
763
|
+
const errorType = errorBody.type;
|
|
764
|
+
// Construct user-friendly error message
|
|
765
|
+
let errorMessage = '';
|
|
766
|
+
if (errorType === 'concurrent_limit_exceeded') {
|
|
767
|
+
const resource = errorBody.resource?.replace('concurrent-', '') || 'request';
|
|
768
|
+
errorMessage = `Concurrent ${resource} limit reached (${errorBody.current}/${errorBody.limit}). Please wait ${retryAfter} seconds and try again.`;
|
|
769
|
+
}
|
|
770
|
+
else {
|
|
771
|
+
errorMessage = `Rate limit exceeded (${errorBody.limit} requests/minute). Please retry after ${retryAfter} seconds.`;
|
|
772
|
+
}
|
|
773
|
+
if (this.continueOnFail()) {
|
|
774
|
+
returnData.push({
|
|
775
|
+
json: {
|
|
776
|
+
error: errorMessage,
|
|
777
|
+
operation,
|
|
778
|
+
statusCode: 429,
|
|
779
|
+
retryAfter: parseInt(retryAfter),
|
|
780
|
+
rateLimitReset: rateLimitReset ? parseInt(rateLimitReset) : undefined,
|
|
781
|
+
errorType: errorType || 'rate_limit_exceeded',
|
|
782
|
+
errorDetails: errorBody,
|
|
783
|
+
},
|
|
784
|
+
pairedItem: { item: i },
|
|
785
|
+
});
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), errorMessage, { itemIndex: i });
|
|
789
|
+
}
|
|
790
|
+
// Handle other errors
|
|
791
|
+
if (this.continueOnFail()) {
|
|
792
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
793
|
+
returnData.push({
|
|
794
|
+
json: {
|
|
795
|
+
error: errorMessage,
|
|
796
|
+
operation,
|
|
797
|
+
statusCode: error.statusCode,
|
|
798
|
+
},
|
|
799
|
+
pairedItem: { item: i },
|
|
800
|
+
});
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
803
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), error instanceof Error ? error : new Error('Unknown error'), { itemIndex: i });
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
return [returnData];
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
exports.SudoMock = SudoMock;
|