n8n-nodes-nvk-call-api 0.0.6 → 0.0.8
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.
|
@@ -12,6 +12,20 @@ exports.nvkBrowserApiDescription = {
|
|
|
12
12
|
defaults: {
|
|
13
13
|
name: 'NVK Browser Call API',
|
|
14
14
|
},
|
|
15
|
+
codex: {
|
|
16
|
+
categories: ['Development'],
|
|
17
|
+
subcategories: {
|
|
18
|
+
Development: ['Browser Automation'],
|
|
19
|
+
},
|
|
20
|
+
resources: {
|
|
21
|
+
primaryDocumentation: [
|
|
22
|
+
{
|
|
23
|
+
url: 'https://your-docs-url.com',
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
alias: ['Run Multiple Profiles', 'requests', 'HTTP', 'HTTP requests', 'Make HTTP requests using Browser', 'HTTP requests using Browser', 'Get Network Response', 'Move And Click', 'Download Browser', 'Update Profile', 'Stop Profile', 'Delete Profile', 'Create profile', 'recaptcha', 'captcha', 'token', 'get recaptcha token', 'recaptcha solver', 'browser', 'automation', 'puppeteer'],
|
|
28
|
+
},
|
|
15
29
|
inputs: ['main'],
|
|
16
30
|
outputs: ['main'],
|
|
17
31
|
credentials: [],
|
|
@@ -65,6 +79,10 @@ exports.nvkBrowserApiDescription = {
|
|
|
65
79
|
name: 'Page Interaction',
|
|
66
80
|
value: 'pageInteraction',
|
|
67
81
|
},
|
|
82
|
+
{
|
|
83
|
+
name: 'Browser Automation',
|
|
84
|
+
value: 'browserAutomation',
|
|
85
|
+
},
|
|
68
86
|
],
|
|
69
87
|
default: 'profileManagement',
|
|
70
88
|
},
|
|
@@ -143,9 +161,166 @@ exports.nvkBrowserApiDescription = {
|
|
|
143
161
|
value: 'browserHttpRequest',
|
|
144
162
|
action: 'Make HTTP requests using Browser',
|
|
145
163
|
},
|
|
164
|
+
{
|
|
165
|
+
name: 'Get Recaptcha Token',
|
|
166
|
+
value: 'getRecaptchaToken',
|
|
167
|
+
action: 'Get Recaptcha Token',
|
|
168
|
+
},
|
|
146
169
|
],
|
|
147
170
|
default: 'moveAndClick',
|
|
148
171
|
},
|
|
172
|
+
{
|
|
173
|
+
displayName: 'Operation',
|
|
174
|
+
name: 'operation',
|
|
175
|
+
type: 'options',
|
|
176
|
+
noDataExpression: true,
|
|
177
|
+
displayOptions: {
|
|
178
|
+
show: {
|
|
179
|
+
resource: ['browserAutomation'],
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
options: [
|
|
183
|
+
{
|
|
184
|
+
name: 'Run Multiple Profiles',
|
|
185
|
+
value: 'runMultipleProfiles',
|
|
186
|
+
action: 'Run Multiple Profiles',
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
default: 'runMultipleProfiles',
|
|
190
|
+
},
|
|
191
|
+
// Run Multiple Profiles Fields
|
|
192
|
+
{
|
|
193
|
+
displayName: 'Data',
|
|
194
|
+
name: 'data',
|
|
195
|
+
type: 'string',
|
|
196
|
+
typeOptions: {
|
|
197
|
+
rows: 10,
|
|
198
|
+
},
|
|
199
|
+
displayOptions: {
|
|
200
|
+
show: {
|
|
201
|
+
resource: ['browserAutomation'],
|
|
202
|
+
operation: ['runMultipleProfiles'],
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
default: '',
|
|
206
|
+
required: true,
|
|
207
|
+
description: 'Enter data in format: line|profileId (one per line)',
|
|
208
|
+
placeholder: '1|212e2d01-0c8a-4373-9c9f-9153354de0a4\n2|212e2d01-0c8a-4373-9c9f-9153354de0a4',
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
displayName: 'Number of Threads',
|
|
212
|
+
name: 'numberOfThreads',
|
|
213
|
+
type: 'number',
|
|
214
|
+
displayOptions: {
|
|
215
|
+
show: {
|
|
216
|
+
resource: ['browserAutomation'],
|
|
217
|
+
operation: ['runMultipleProfiles'],
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
default: 1,
|
|
221
|
+
description: 'Number of parallel threads to run',
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
displayName: 'Define Columns',
|
|
225
|
+
name: 'defineColumns',
|
|
226
|
+
type: 'number',
|
|
227
|
+
displayOptions: {
|
|
228
|
+
show: {
|
|
229
|
+
resource: ['browserAutomation'],
|
|
230
|
+
operation: ['runMultipleProfiles'],
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
default: 3,
|
|
234
|
+
description: 'Number of columns in grid layout',
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
displayName: 'Define Rows',
|
|
238
|
+
name: 'defineRows',
|
|
239
|
+
type: 'number',
|
|
240
|
+
displayOptions: {
|
|
241
|
+
show: {
|
|
242
|
+
resource: ['browserAutomation'],
|
|
243
|
+
operation: ['runMultipleProfiles'],
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
default: 2,
|
|
247
|
+
description: 'Number of rows in grid layout',
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
displayName: 'Win Scale',
|
|
251
|
+
name: 'winScale',
|
|
252
|
+
type: 'number',
|
|
253
|
+
displayOptions: {
|
|
254
|
+
show: {
|
|
255
|
+
resource: ['browserAutomation'],
|
|
256
|
+
operation: ['runMultipleProfiles'],
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
default: 100,
|
|
260
|
+
description: 'Window scale percentage (100 = 100%)',
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
displayName: 'Webhook URL',
|
|
264
|
+
name: 'webhookUrl',
|
|
265
|
+
type: 'string',
|
|
266
|
+
displayOptions: {
|
|
267
|
+
show: {
|
|
268
|
+
resource: ['browserAutomation'],
|
|
269
|
+
operation: ['runMultipleProfiles'],
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
default: '',
|
|
273
|
+
description: 'Webhook URL to send results to (optional)',
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
displayName: 'Webhook Method',
|
|
277
|
+
name: 'webhookMethod',
|
|
278
|
+
type: 'options',
|
|
279
|
+
displayOptions: {
|
|
280
|
+
show: {
|
|
281
|
+
resource: ['browserAutomation'],
|
|
282
|
+
operation: ['runMultipleProfiles'],
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
options: [
|
|
286
|
+
{
|
|
287
|
+
name: 'GET',
|
|
288
|
+
value: 'GET',
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
name: 'POST',
|
|
292
|
+
value: 'POST',
|
|
293
|
+
},
|
|
294
|
+
],
|
|
295
|
+
default: 'POST',
|
|
296
|
+
description: 'HTTP method to use for webhook call',
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
displayName: 'Timeout (Milliseconds)',
|
|
300
|
+
name: 'timeout',
|
|
301
|
+
type: 'number',
|
|
302
|
+
displayOptions: {
|
|
303
|
+
show: {
|
|
304
|
+
resource: ['browserAutomation'],
|
|
305
|
+
operation: ['runMultipleProfiles'],
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
default: 100000000,
|
|
309
|
+
description: 'Maximum timeout in milliseconds',
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
displayName: 'Wait For The Previous Session To Complete',
|
|
313
|
+
name: 'waitForPrevious',
|
|
314
|
+
type: 'boolean',
|
|
315
|
+
displayOptions: {
|
|
316
|
+
show: {
|
|
317
|
+
resource: ['browserAutomation'],
|
|
318
|
+
operation: ['runMultipleProfiles'],
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
default: true,
|
|
322
|
+
description: 'Whether to wait for previous session to complete before starting next',
|
|
323
|
+
},
|
|
149
324
|
// Create Profile Fields
|
|
150
325
|
{
|
|
151
326
|
displayName: 'Profile Name',
|
|
@@ -396,7 +571,7 @@ exports.nvkBrowserApiDescription = {
|
|
|
396
571
|
operation: ['updateProfile'],
|
|
397
572
|
},
|
|
398
573
|
},
|
|
399
|
-
default: '
|
|
574
|
+
default: '',
|
|
400
575
|
description: 'New proxy configuration',
|
|
401
576
|
},
|
|
402
577
|
{
|
|
@@ -410,7 +585,7 @@ exports.nvkBrowserApiDescription = {
|
|
|
410
585
|
},
|
|
411
586
|
},
|
|
412
587
|
default: '',
|
|
413
|
-
description: '
|
|
588
|
+
description: 'New note',
|
|
414
589
|
},
|
|
415
590
|
{
|
|
416
591
|
displayName: 'Extensions',
|
|
@@ -426,7 +601,7 @@ exports.nvkBrowserApiDescription = {
|
|
|
426
601
|
},
|
|
427
602
|
},
|
|
428
603
|
default: '',
|
|
429
|
-
description: '
|
|
604
|
+
description: 'New extensions (one per line)',
|
|
430
605
|
},
|
|
431
606
|
// Move and Click Fields
|
|
432
607
|
{
|
|
@@ -444,12 +619,9 @@ exports.nvkBrowserApiDescription = {
|
|
|
444
619
|
description: 'ID of the profile',
|
|
445
620
|
},
|
|
446
621
|
{
|
|
447
|
-
displayName: 'Selector
|
|
622
|
+
displayName: 'Selector',
|
|
448
623
|
name: 'selector',
|
|
449
624
|
type: 'string',
|
|
450
|
-
typeOptions: {
|
|
451
|
-
rows: 4,
|
|
452
|
-
},
|
|
453
625
|
displayOptions: {
|
|
454
626
|
show: {
|
|
455
627
|
resource: ['pageInteraction'],
|
|
@@ -458,7 +630,7 @@ exports.nvkBrowserApiDescription = {
|
|
|
458
630
|
},
|
|
459
631
|
default: '',
|
|
460
632
|
required: true,
|
|
461
|
-
description: 'CSS selector
|
|
633
|
+
description: 'CSS selector for the element',
|
|
462
634
|
},
|
|
463
635
|
{
|
|
464
636
|
displayName: 'Click Method',
|
|
@@ -472,19 +644,19 @@ exports.nvkBrowserApiDescription = {
|
|
|
472
644
|
},
|
|
473
645
|
options: [
|
|
474
646
|
{
|
|
475
|
-
name: 'Puppeteer
|
|
647
|
+
name: 'Puppeteer',
|
|
476
648
|
value: 'puppeteer',
|
|
477
649
|
},
|
|
478
650
|
{
|
|
479
|
-
name: '
|
|
480
|
-
value: '
|
|
651
|
+
name: 'Coordinates',
|
|
652
|
+
value: 'coordinates',
|
|
481
653
|
},
|
|
482
654
|
],
|
|
483
655
|
default: 'puppeteer',
|
|
484
|
-
description: 'Method for clicking',
|
|
656
|
+
description: 'Method to use for clicking',
|
|
485
657
|
},
|
|
486
658
|
{
|
|
487
|
-
displayName: 'Timeout
|
|
659
|
+
displayName: 'Timeout',
|
|
488
660
|
name: 'timeout',
|
|
489
661
|
type: 'number',
|
|
490
662
|
displayOptions: {
|
|
@@ -494,7 +666,7 @@ exports.nvkBrowserApiDescription = {
|
|
|
494
666
|
},
|
|
495
667
|
},
|
|
496
668
|
default: 30000,
|
|
497
|
-
description: '
|
|
669
|
+
description: 'Timeout in milliseconds',
|
|
498
670
|
},
|
|
499
671
|
{
|
|
500
672
|
displayName: 'Tab Index',
|
|
@@ -523,7 +695,7 @@ exports.nvkBrowserApiDescription = {
|
|
|
523
695
|
description: 'Whether to auto-start profile if not running',
|
|
524
696
|
},
|
|
525
697
|
{
|
|
526
|
-
displayName: 'Wait
|
|
698
|
+
displayName: 'Wait For Click',
|
|
527
699
|
name: 'waitForClick',
|
|
528
700
|
type: 'number',
|
|
529
701
|
displayOptions: {
|
|
@@ -533,10 +705,10 @@ exports.nvkBrowserApiDescription = {
|
|
|
533
705
|
},
|
|
534
706
|
},
|
|
535
707
|
default: 500,
|
|
536
|
-
description: '
|
|
708
|
+
description: 'Wait time before click in milliseconds',
|
|
537
709
|
},
|
|
538
710
|
{
|
|
539
|
-
displayName: '
|
|
711
|
+
displayName: 'Button',
|
|
540
712
|
name: 'button',
|
|
541
713
|
type: 'options',
|
|
542
714
|
displayOptions: {
|
|
@@ -589,7 +761,7 @@ exports.nvkBrowserApiDescription = {
|
|
|
589
761
|
description: 'Whether to upload a binary file',
|
|
590
762
|
},
|
|
591
763
|
{
|
|
592
|
-
displayName: '
|
|
764
|
+
displayName: 'Input Data Field Name',
|
|
593
765
|
name: 'binaryPropertyName',
|
|
594
766
|
type: 'string',
|
|
595
767
|
displayOptions: {
|
|
@@ -601,7 +773,7 @@ exports.nvkBrowserApiDescription = {
|
|
|
601
773
|
},
|
|
602
774
|
default: 'data',
|
|
603
775
|
required: true,
|
|
604
|
-
description: 'Name of the binary property
|
|
776
|
+
description: 'Name of the binary property',
|
|
605
777
|
},
|
|
606
778
|
// Run JavaScript Fields
|
|
607
779
|
{
|
|
@@ -677,7 +849,7 @@ exports.nvkBrowserApiDescription = {
|
|
|
677
849
|
description: 'ID of the profile',
|
|
678
850
|
},
|
|
679
851
|
{
|
|
680
|
-
displayName: 'Request
|
|
852
|
+
displayName: 'Request Filter',
|
|
681
853
|
name: 'requestFilter',
|
|
682
854
|
type: 'string',
|
|
683
855
|
displayOptions: {
|
|
@@ -709,24 +881,16 @@ exports.nvkBrowserApiDescription = {
|
|
|
709
881
|
name: 'Exact',
|
|
710
882
|
value: 'exact',
|
|
711
883
|
},
|
|
712
|
-
{
|
|
713
|
-
name: 'Starts With',
|
|
714
|
-
value: 'startsWith',
|
|
715
|
-
},
|
|
716
|
-
{
|
|
717
|
-
name: 'Ends With',
|
|
718
|
-
value: 'endsWith',
|
|
719
|
-
},
|
|
720
884
|
{
|
|
721
885
|
name: 'Regex',
|
|
722
886
|
value: 'regex',
|
|
723
887
|
},
|
|
724
888
|
],
|
|
725
889
|
default: 'contains',
|
|
726
|
-
description: '
|
|
890
|
+
description: 'Type of matching to use',
|
|
727
891
|
},
|
|
728
892
|
{
|
|
729
|
-
displayName: 'Timeout
|
|
893
|
+
displayName: 'Timeout',
|
|
730
894
|
name: 'timeout',
|
|
731
895
|
type: 'number',
|
|
732
896
|
displayOptions: {
|
|
@@ -736,7 +900,7 @@ exports.nvkBrowserApiDescription = {
|
|
|
736
900
|
},
|
|
737
901
|
},
|
|
738
902
|
default: 30000,
|
|
739
|
-
description: 'Timeout
|
|
903
|
+
description: 'Timeout in milliseconds',
|
|
740
904
|
},
|
|
741
905
|
{
|
|
742
906
|
displayName: 'Wait For Request',
|
|
@@ -880,6 +1044,99 @@ exports.nvkBrowserApiDescription = {
|
|
|
880
1044
|
default: 'formData',
|
|
881
1045
|
description: 'Type of body content',
|
|
882
1046
|
},
|
|
1047
|
+
// Get Recaptcha Token Fields
|
|
1048
|
+
{
|
|
1049
|
+
displayName: 'Profile ID',
|
|
1050
|
+
name: 'profileId',
|
|
1051
|
+
type: 'string',
|
|
1052
|
+
displayOptions: {
|
|
1053
|
+
show: {
|
|
1054
|
+
resource: ['pageInteraction'],
|
|
1055
|
+
operation: ['getRecaptchaToken'],
|
|
1056
|
+
},
|
|
1057
|
+
},
|
|
1058
|
+
default: '',
|
|
1059
|
+
required: true,
|
|
1060
|
+
description: 'ID of the profile',
|
|
1061
|
+
},
|
|
1062
|
+
{
|
|
1063
|
+
displayName: 'Site Key',
|
|
1064
|
+
name: 'siteKey',
|
|
1065
|
+
type: 'string',
|
|
1066
|
+
displayOptions: {
|
|
1067
|
+
show: {
|
|
1068
|
+
resource: ['pageInteraction'],
|
|
1069
|
+
operation: ['getRecaptchaToken'],
|
|
1070
|
+
},
|
|
1071
|
+
},
|
|
1072
|
+
default: '',
|
|
1073
|
+
description: 'reCAPTCHA site key (auto-detect if empty)',
|
|
1074
|
+
},
|
|
1075
|
+
{
|
|
1076
|
+
displayName: 'Tab Index',
|
|
1077
|
+
name: 'tabIndex',
|
|
1078
|
+
type: 'number',
|
|
1079
|
+
displayOptions: {
|
|
1080
|
+
show: {
|
|
1081
|
+
resource: ['pageInteraction'],
|
|
1082
|
+
operation: ['getRecaptchaToken'],
|
|
1083
|
+
},
|
|
1084
|
+
},
|
|
1085
|
+
default: 0,
|
|
1086
|
+
description: 'Browser tab index',
|
|
1087
|
+
},
|
|
1088
|
+
{
|
|
1089
|
+
displayName: 'Timeout',
|
|
1090
|
+
name: 'timeout',
|
|
1091
|
+
type: 'number',
|
|
1092
|
+
displayOptions: {
|
|
1093
|
+
show: {
|
|
1094
|
+
resource: ['pageInteraction'],
|
|
1095
|
+
operation: ['getRecaptchaToken'],
|
|
1096
|
+
},
|
|
1097
|
+
},
|
|
1098
|
+
default: 10000,
|
|
1099
|
+
description: 'Timeout in milliseconds',
|
|
1100
|
+
},
|
|
1101
|
+
{
|
|
1102
|
+
displayName: 'Action',
|
|
1103
|
+
name: 'action',
|
|
1104
|
+
type: 'string',
|
|
1105
|
+
displayOptions: {
|
|
1106
|
+
show: {
|
|
1107
|
+
resource: ['pageInteraction'],
|
|
1108
|
+
operation: ['getRecaptchaToken'],
|
|
1109
|
+
},
|
|
1110
|
+
},
|
|
1111
|
+
default: 'FLOW_GENERATION',
|
|
1112
|
+
description: 'reCAPTCHA action parameter',
|
|
1113
|
+
},
|
|
1114
|
+
{
|
|
1115
|
+
displayName: 'Max Retries',
|
|
1116
|
+
name: 'maxRetries',
|
|
1117
|
+
type: 'number',
|
|
1118
|
+
displayOptions: {
|
|
1119
|
+
show: {
|
|
1120
|
+
resource: ['pageInteraction'],
|
|
1121
|
+
operation: ['getRecaptchaToken'],
|
|
1122
|
+
},
|
|
1123
|
+
},
|
|
1124
|
+
default: 3,
|
|
1125
|
+
description: 'Maximum number of retry attempts',
|
|
1126
|
+
},
|
|
1127
|
+
{
|
|
1128
|
+
displayName: 'Auto Start Profile',
|
|
1129
|
+
name: 'autoStart',
|
|
1130
|
+
type: 'boolean',
|
|
1131
|
+
displayOptions: {
|
|
1132
|
+
show: {
|
|
1133
|
+
resource: ['pageInteraction'],
|
|
1134
|
+
operation: ['getRecaptchaToken'],
|
|
1135
|
+
},
|
|
1136
|
+
},
|
|
1137
|
+
default: false,
|
|
1138
|
+
description: 'Whether to auto-start profile if not running',
|
|
1139
|
+
},
|
|
883
1140
|
// n8n Binary File - Input Data Field Name
|
|
884
1141
|
{
|
|
885
1142
|
displayName: 'Input Data Field Name',
|
|
@@ -14,6 +14,16 @@ class NvkBrowserApi {
|
|
|
14
14
|
const environment = this.getNodeParameter('environment', i);
|
|
15
15
|
const resource = this.getNodeParameter('resource', i);
|
|
16
16
|
const operation = this.getNodeParameter('operation', i);
|
|
17
|
+
// Handle Run Multiple Profiles - No API call, process locally in n8n
|
|
18
|
+
if (resource === 'browserAutomation' && operation === 'runMultipleProfiles') {
|
|
19
|
+
const result = await runMultipleProfiles.call(this, i);
|
|
20
|
+
returnData.push({
|
|
21
|
+
json: result,
|
|
22
|
+
pairedItem: { item: i },
|
|
23
|
+
});
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
// All other operations - Call API as before
|
|
17
27
|
let baseUrl;
|
|
18
28
|
if (environment === 'docker') {
|
|
19
29
|
baseUrl = 'http://host.docker.internal:3000';
|
|
@@ -151,6 +161,23 @@ class NvkBrowserApi {
|
|
|
151
161
|
};
|
|
152
162
|
break;
|
|
153
163
|
}
|
|
164
|
+
case 'getRecaptchaToken': {
|
|
165
|
+
endpoint = '/api/recaptcha/get-token';
|
|
166
|
+
body = {
|
|
167
|
+
profileId: this.getNodeParameter('profileId', i),
|
|
168
|
+
tabIndex: this.getNodeParameter('tabIndex', i, 0),
|
|
169
|
+
timeout: this.getNodeParameter('timeout', i, 10000),
|
|
170
|
+
action: this.getNodeParameter('action', i, 'FLOW_GENERATION'),
|
|
171
|
+
maxRetries: this.getNodeParameter('maxRetries', i, 3),
|
|
172
|
+
autoStart: this.getNodeParameter('autoStart', i, false),
|
|
173
|
+
};
|
|
174
|
+
// Site key là optional
|
|
175
|
+
const siteKey = this.getNodeParameter('siteKey', i, '');
|
|
176
|
+
if (siteKey && siteKey.trim() !== '') {
|
|
177
|
+
body.siteKey = siteKey.trim();
|
|
178
|
+
}
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
154
181
|
case 'browserHttpRequest': {
|
|
155
182
|
endpoint = '/api/browser/request';
|
|
156
183
|
const method = this.getNodeParameter('method', i, 'GET');
|
|
@@ -313,3 +340,227 @@ class NvkBrowserApi {
|
|
|
313
340
|
}
|
|
314
341
|
}
|
|
315
342
|
exports.NvkBrowserApi = NvkBrowserApi;
|
|
343
|
+
/**
|
|
344
|
+
* Run Multiple Profiles - Process locally in n8n without API call
|
|
345
|
+
*/
|
|
346
|
+
async function runMultipleProfiles(itemIndex) {
|
|
347
|
+
// Get parameters
|
|
348
|
+
const data = this.getNodeParameter('data', itemIndex, '');
|
|
349
|
+
const numberOfThreads = this.getNodeParameter('numberOfThreads', itemIndex, 1);
|
|
350
|
+
const defineColumns = this.getNodeParameter('defineColumns', itemIndex, 3);
|
|
351
|
+
const defineRows = this.getNodeParameter('defineRows', itemIndex, 2);
|
|
352
|
+
const winScale = this.getNodeParameter('winScale', itemIndex, 100);
|
|
353
|
+
const webhookUrl = this.getNodeParameter('webhookUrl', itemIndex, '');
|
|
354
|
+
const webhookMethod = this.getNodeParameter('webhookMethod', itemIndex, 'POST');
|
|
355
|
+
const timeout = this.getNodeParameter('timeout', itemIndex, 100000000);
|
|
356
|
+
const waitForPrevious = this.getNodeParameter('waitForPrevious', itemIndex, true);
|
|
357
|
+
// Parse data into lines
|
|
358
|
+
const lines = data.trim().split('\n').filter((line) => line.trim() !== '');
|
|
359
|
+
const totalLines = lines.length;
|
|
360
|
+
// Calculate window size and position for grid layout
|
|
361
|
+
const screenWidth = 1920; // Default screen width
|
|
362
|
+
const screenHeight = 1080; // Default screen height
|
|
363
|
+
const windowWidth = Math.floor((screenWidth / defineColumns) * (winScale / 100));
|
|
364
|
+
const windowHeight = Math.floor((screenHeight / defineRows) * (winScale / 100));
|
|
365
|
+
// Prepare tasks
|
|
366
|
+
const tasks = lines.map((line, index) => {
|
|
367
|
+
const parts = line.split('|').map((p) => p.trim());
|
|
368
|
+
// Calculate grid position
|
|
369
|
+
const row = Math.floor(index / defineColumns);
|
|
370
|
+
const col = index % defineColumns;
|
|
371
|
+
const x = col * windowWidth;
|
|
372
|
+
const y = row * windowHeight;
|
|
373
|
+
return {
|
|
374
|
+
line,
|
|
375
|
+
parts,
|
|
376
|
+
threadId: index,
|
|
377
|
+
winPosition: `${x},${y}`,
|
|
378
|
+
winSize: `${windowWidth},${windowHeight}`,
|
|
379
|
+
};
|
|
380
|
+
});
|
|
381
|
+
const results = [];
|
|
382
|
+
let completedLines = 0;
|
|
383
|
+
// Helper function to send individual result to webhook
|
|
384
|
+
const sendToWebhook = async (result, index, total) => {
|
|
385
|
+
if (!webhookUrl || webhookUrl.trim() === '') {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
const cleanWebhookUrl = webhookUrl.trim();
|
|
389
|
+
try {
|
|
390
|
+
const payload = {
|
|
391
|
+
...result,
|
|
392
|
+
index: index + 1,
|
|
393
|
+
total,
|
|
394
|
+
timestamp: new Date().toISOString(),
|
|
395
|
+
};
|
|
396
|
+
console.log(`Sending result ${index + 1}/${total} to webhook (${webhookMethod}):`, cleanWebhookUrl);
|
|
397
|
+
if (webhookMethod === 'GET') {
|
|
398
|
+
// For GET, send each field as separate query parameter
|
|
399
|
+
const params = new URLSearchParams();
|
|
400
|
+
// Add basic fields
|
|
401
|
+
params.append('line', result.line);
|
|
402
|
+
params.append('threadId', String(result.threadId));
|
|
403
|
+
params.append('winPosition', result.winPosition);
|
|
404
|
+
params.append('winSize', result.winSize);
|
|
405
|
+
params.append('winScale', String(winScale)); // Add winScale from main params
|
|
406
|
+
params.append('success', String(result.success));
|
|
407
|
+
params.append('index', String(index + 1));
|
|
408
|
+
params.append('total', String(total));
|
|
409
|
+
params.append('timestamp', new Date().toISOString());
|
|
410
|
+
// Add response as JSON string (if exists)
|
|
411
|
+
if (result.response) {
|
|
412
|
+
params.append('response', JSON.stringify(result.response));
|
|
413
|
+
}
|
|
414
|
+
// Add error if exists
|
|
415
|
+
if (result.error) {
|
|
416
|
+
params.append('error', result.error);
|
|
417
|
+
}
|
|
418
|
+
const urlWithParams = `${cleanWebhookUrl}?${params.toString()}`;
|
|
419
|
+
await this.helpers.request({
|
|
420
|
+
method: 'GET',
|
|
421
|
+
url: urlWithParams,
|
|
422
|
+
json: true,
|
|
423
|
+
headers: {
|
|
424
|
+
'User-Agent': 'n8n-nvk-browser-api',
|
|
425
|
+
},
|
|
426
|
+
timeout: 30000,
|
|
427
|
+
resolveWithFullResponse: false,
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
// For POST, send full payload in body
|
|
432
|
+
await this.helpers.request({
|
|
433
|
+
method: 'POST',
|
|
434
|
+
url: cleanWebhookUrl,
|
|
435
|
+
body: payload,
|
|
436
|
+
json: true,
|
|
437
|
+
headers: {
|
|
438
|
+
'Content-Type': 'application/json',
|
|
439
|
+
'User-Agent': 'n8n-nvk-browser-api',
|
|
440
|
+
},
|
|
441
|
+
timeout: 30000,
|
|
442
|
+
resolveWithFullResponse: false,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
console.log(`✓ Result ${index + 1}/${total} sent successfully`);
|
|
446
|
+
}
|
|
447
|
+
catch (error) {
|
|
448
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
449
|
+
console.error(`✗ Failed to send result ${index + 1}/${total}:`, errorMessage);
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
// Process tasks
|
|
453
|
+
if (waitForPrevious) {
|
|
454
|
+
// Sequential processing - wait for each to complete
|
|
455
|
+
for (let taskIndex = 0; taskIndex < tasks.length; taskIndex++) {
|
|
456
|
+
const task = tasks[taskIndex];
|
|
457
|
+
try {
|
|
458
|
+
const result = await processProfileTask(task);
|
|
459
|
+
results.push(result);
|
|
460
|
+
completedLines++;
|
|
461
|
+
// Send to webhook immediately after processing
|
|
462
|
+
await sendToWebhook(result, taskIndex, tasks.length);
|
|
463
|
+
}
|
|
464
|
+
catch (error) {
|
|
465
|
+
const errorResult = {
|
|
466
|
+
line: task.line,
|
|
467
|
+
threadId: task.threadId,
|
|
468
|
+
winPosition: task.winPosition,
|
|
469
|
+
winSize: task.winSize,
|
|
470
|
+
success: false,
|
|
471
|
+
error: error instanceof Error ? error.message : String(error),
|
|
472
|
+
};
|
|
473
|
+
results.push(errorResult);
|
|
474
|
+
completedLines++;
|
|
475
|
+
// Send error result to webhook too
|
|
476
|
+
await sendToWebhook(errorResult, taskIndex, tasks.length);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
// Parallel processing with thread limit
|
|
482
|
+
const processInBatches = async (tasks, batchSize) => {
|
|
483
|
+
for (let i = 0; i < tasks.length; i += batchSize) {
|
|
484
|
+
const batch = tasks.slice(i, i + batchSize);
|
|
485
|
+
const batchPromises = batch.map(async (task, batchIndex) => {
|
|
486
|
+
const taskIndex = i + batchIndex;
|
|
487
|
+
try {
|
|
488
|
+
const result = await processProfileTask(task);
|
|
489
|
+
results.push(result);
|
|
490
|
+
completedLines++;
|
|
491
|
+
// Send to webhook immediately after processing
|
|
492
|
+
await sendToWebhook(result, taskIndex, tasks.length);
|
|
493
|
+
return result;
|
|
494
|
+
}
|
|
495
|
+
catch (error) {
|
|
496
|
+
const errorResult = {
|
|
497
|
+
line: task.line,
|
|
498
|
+
threadId: task.threadId,
|
|
499
|
+
winPosition: task.winPosition,
|
|
500
|
+
winSize: task.winSize,
|
|
501
|
+
success: false,
|
|
502
|
+
error: error instanceof Error ? error.message : String(error),
|
|
503
|
+
};
|
|
504
|
+
results.push(errorResult);
|
|
505
|
+
completedLines++;
|
|
506
|
+
// Send error result to webhook too
|
|
507
|
+
await sendToWebhook(errorResult, taskIndex, tasks.length);
|
|
508
|
+
return errorResult;
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
await Promise.all(batchPromises);
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
await processInBatches(tasks, numberOfThreads);
|
|
515
|
+
}
|
|
516
|
+
// Prepare final response
|
|
517
|
+
const finalResponse = {
|
|
518
|
+
totalLines,
|
|
519
|
+
completedLines,
|
|
520
|
+
results,
|
|
521
|
+
timeout,
|
|
522
|
+
defineColumn: defineColumns,
|
|
523
|
+
defineRow: defineRows,
|
|
524
|
+
message: `All ${completedLines} threads completed with grid ${defineColumns}x${defineRows}, maximum timeout ${timeout}ms`,
|
|
525
|
+
};
|
|
526
|
+
// Add webhook summary if webhook was used
|
|
527
|
+
if (webhookUrl && webhookUrl.trim() !== '') {
|
|
528
|
+
finalResponse.webhookSummary = {
|
|
529
|
+
enabled: true,
|
|
530
|
+
url: webhookUrl.trim(),
|
|
531
|
+
method: webhookMethod,
|
|
532
|
+
totalSent: completedLines,
|
|
533
|
+
message: `Sent ${completedLines} individual results to webhook`,
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
finalResponse.webhookSummary = {
|
|
538
|
+
enabled: false,
|
|
539
|
+
message: 'No webhook URL provided',
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
return finalResponse;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Process a single profile task
|
|
546
|
+
*/
|
|
547
|
+
async function processProfileTask(task) {
|
|
548
|
+
// Simulate processing - parse the data
|
|
549
|
+
const responseData = [];
|
|
550
|
+
// Parse each part from the line (separated by |)
|
|
551
|
+
task.parts.forEach((part, index) => {
|
|
552
|
+
const obj = {};
|
|
553
|
+
obj[`part${index + 1}`] = part;
|
|
554
|
+
responseData.push(obj);
|
|
555
|
+
});
|
|
556
|
+
// Simulate some delay (optional)
|
|
557
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
558
|
+
return {
|
|
559
|
+
line: task.line,
|
|
560
|
+
threadId: task.threadId,
|
|
561
|
+
winPosition: task.winPosition,
|
|
562
|
+
winSize: task.winSize,
|
|
563
|
+
success: true,
|
|
564
|
+
response: responseData,
|
|
565
|
+
};
|
|
566
|
+
}
|