n8n-nodes-nvk-call-api 0.0.6 → 0.0.7
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.
|
@@ -65,6 +65,10 @@ exports.nvkBrowserApiDescription = {
|
|
|
65
65
|
name: 'Page Interaction',
|
|
66
66
|
value: 'pageInteraction',
|
|
67
67
|
},
|
|
68
|
+
{
|
|
69
|
+
name: 'Browser Automation',
|
|
70
|
+
value: 'browserAutomation',
|
|
71
|
+
},
|
|
68
72
|
],
|
|
69
73
|
default: 'profileManagement',
|
|
70
74
|
},
|
|
@@ -143,9 +147,166 @@ exports.nvkBrowserApiDescription = {
|
|
|
143
147
|
value: 'browserHttpRequest',
|
|
144
148
|
action: 'Make HTTP requests using Browser',
|
|
145
149
|
},
|
|
150
|
+
{
|
|
151
|
+
name: 'Get Recaptcha Token',
|
|
152
|
+
value: 'getRecaptchaToken',
|
|
153
|
+
action: 'Get Recaptcha Token',
|
|
154
|
+
},
|
|
146
155
|
],
|
|
147
156
|
default: 'moveAndClick',
|
|
148
157
|
},
|
|
158
|
+
{
|
|
159
|
+
displayName: 'Operation',
|
|
160
|
+
name: 'operation',
|
|
161
|
+
type: 'options',
|
|
162
|
+
noDataExpression: true,
|
|
163
|
+
displayOptions: {
|
|
164
|
+
show: {
|
|
165
|
+
resource: ['browserAutomation'],
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
options: [
|
|
169
|
+
{
|
|
170
|
+
name: 'Run Multiple Profiles',
|
|
171
|
+
value: 'runMultipleProfiles',
|
|
172
|
+
action: 'Run Multiple Profiles',
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
default: 'runMultipleProfiles',
|
|
176
|
+
},
|
|
177
|
+
// Run Multiple Profiles Fields
|
|
178
|
+
{
|
|
179
|
+
displayName: 'Data',
|
|
180
|
+
name: 'data',
|
|
181
|
+
type: 'string',
|
|
182
|
+
typeOptions: {
|
|
183
|
+
rows: 10,
|
|
184
|
+
},
|
|
185
|
+
displayOptions: {
|
|
186
|
+
show: {
|
|
187
|
+
resource: ['browserAutomation'],
|
|
188
|
+
operation: ['runMultipleProfiles'],
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
default: '',
|
|
192
|
+
required: true,
|
|
193
|
+
description: 'Enter data in format: line|profileId (one per line)',
|
|
194
|
+
placeholder: '1|212e2d01-0c8a-4373-9c9f-9153354de0a4\n2|212e2d01-0c8a-4373-9c9f-9153354de0a4',
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
displayName: 'Number of Threads',
|
|
198
|
+
name: 'numberOfThreads',
|
|
199
|
+
type: 'number',
|
|
200
|
+
displayOptions: {
|
|
201
|
+
show: {
|
|
202
|
+
resource: ['browserAutomation'],
|
|
203
|
+
operation: ['runMultipleProfiles'],
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
default: 1,
|
|
207
|
+
description: 'Number of parallel threads to run',
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
displayName: 'Define Columns',
|
|
211
|
+
name: 'defineColumns',
|
|
212
|
+
type: 'number',
|
|
213
|
+
displayOptions: {
|
|
214
|
+
show: {
|
|
215
|
+
resource: ['browserAutomation'],
|
|
216
|
+
operation: ['runMultipleProfiles'],
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
default: 3,
|
|
220
|
+
description: 'Number of columns in grid layout',
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
displayName: 'Define Rows',
|
|
224
|
+
name: 'defineRows',
|
|
225
|
+
type: 'number',
|
|
226
|
+
displayOptions: {
|
|
227
|
+
show: {
|
|
228
|
+
resource: ['browserAutomation'],
|
|
229
|
+
operation: ['runMultipleProfiles'],
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
default: 2,
|
|
233
|
+
description: 'Number of rows in grid layout',
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
displayName: 'Win Scale',
|
|
237
|
+
name: 'winScale',
|
|
238
|
+
type: 'number',
|
|
239
|
+
displayOptions: {
|
|
240
|
+
show: {
|
|
241
|
+
resource: ['browserAutomation'],
|
|
242
|
+
operation: ['runMultipleProfiles'],
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
default: 100,
|
|
246
|
+
description: 'Window scale percentage (100 = 100%)',
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
displayName: 'Webhook URL',
|
|
250
|
+
name: 'webhookUrl',
|
|
251
|
+
type: 'string',
|
|
252
|
+
displayOptions: {
|
|
253
|
+
show: {
|
|
254
|
+
resource: ['browserAutomation'],
|
|
255
|
+
operation: ['runMultipleProfiles'],
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
default: '',
|
|
259
|
+
description: 'Webhook URL to send results to (optional)',
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
displayName: 'Webhook Method',
|
|
263
|
+
name: 'webhookMethod',
|
|
264
|
+
type: 'options',
|
|
265
|
+
displayOptions: {
|
|
266
|
+
show: {
|
|
267
|
+
resource: ['browserAutomation'],
|
|
268
|
+
operation: ['runMultipleProfiles'],
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
options: [
|
|
272
|
+
{
|
|
273
|
+
name: 'GET',
|
|
274
|
+
value: 'GET',
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
name: 'POST',
|
|
278
|
+
value: 'POST',
|
|
279
|
+
},
|
|
280
|
+
],
|
|
281
|
+
default: 'POST',
|
|
282
|
+
description: 'HTTP method to use for webhook call',
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
displayName: 'Timeout (Milliseconds)',
|
|
286
|
+
name: 'timeout',
|
|
287
|
+
type: 'number',
|
|
288
|
+
displayOptions: {
|
|
289
|
+
show: {
|
|
290
|
+
resource: ['browserAutomation'],
|
|
291
|
+
operation: ['runMultipleProfiles'],
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
default: 100000000,
|
|
295
|
+
description: 'Maximum timeout in milliseconds',
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
displayName: 'Wait For The Previous Session To Complete',
|
|
299
|
+
name: 'waitForPrevious',
|
|
300
|
+
type: 'boolean',
|
|
301
|
+
displayOptions: {
|
|
302
|
+
show: {
|
|
303
|
+
resource: ['browserAutomation'],
|
|
304
|
+
operation: ['runMultipleProfiles'],
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
default: true,
|
|
308
|
+
description: 'Whether to wait for previous session to complete before starting next',
|
|
309
|
+
},
|
|
149
310
|
// Create Profile Fields
|
|
150
311
|
{
|
|
151
312
|
displayName: 'Profile Name',
|
|
@@ -396,7 +557,7 @@ exports.nvkBrowserApiDescription = {
|
|
|
396
557
|
operation: ['updateProfile'],
|
|
397
558
|
},
|
|
398
559
|
},
|
|
399
|
-
default: '
|
|
560
|
+
default: '',
|
|
400
561
|
description: 'New proxy configuration',
|
|
401
562
|
},
|
|
402
563
|
{
|
|
@@ -410,7 +571,7 @@ exports.nvkBrowserApiDescription = {
|
|
|
410
571
|
},
|
|
411
572
|
},
|
|
412
573
|
default: '',
|
|
413
|
-
description: '
|
|
574
|
+
description: 'New note',
|
|
414
575
|
},
|
|
415
576
|
{
|
|
416
577
|
displayName: 'Extensions',
|
|
@@ -426,7 +587,7 @@ exports.nvkBrowserApiDescription = {
|
|
|
426
587
|
},
|
|
427
588
|
},
|
|
428
589
|
default: '',
|
|
429
|
-
description: '
|
|
590
|
+
description: 'New extensions (one per line)',
|
|
430
591
|
},
|
|
431
592
|
// Move and Click Fields
|
|
432
593
|
{
|
|
@@ -444,12 +605,9 @@ exports.nvkBrowserApiDescription = {
|
|
|
444
605
|
description: 'ID of the profile',
|
|
445
606
|
},
|
|
446
607
|
{
|
|
447
|
-
displayName: 'Selector
|
|
608
|
+
displayName: 'Selector',
|
|
448
609
|
name: 'selector',
|
|
449
610
|
type: 'string',
|
|
450
|
-
typeOptions: {
|
|
451
|
-
rows: 4,
|
|
452
|
-
},
|
|
453
611
|
displayOptions: {
|
|
454
612
|
show: {
|
|
455
613
|
resource: ['pageInteraction'],
|
|
@@ -458,7 +616,7 @@ exports.nvkBrowserApiDescription = {
|
|
|
458
616
|
},
|
|
459
617
|
default: '',
|
|
460
618
|
required: true,
|
|
461
|
-
description: 'CSS selector
|
|
619
|
+
description: 'CSS selector for the element',
|
|
462
620
|
},
|
|
463
621
|
{
|
|
464
622
|
displayName: 'Click Method',
|
|
@@ -472,19 +630,19 @@ exports.nvkBrowserApiDescription = {
|
|
|
472
630
|
},
|
|
473
631
|
options: [
|
|
474
632
|
{
|
|
475
|
-
name: 'Puppeteer
|
|
633
|
+
name: 'Puppeteer',
|
|
476
634
|
value: 'puppeteer',
|
|
477
635
|
},
|
|
478
636
|
{
|
|
479
|
-
name: '
|
|
480
|
-
value: '
|
|
637
|
+
name: 'Coordinates',
|
|
638
|
+
value: 'coordinates',
|
|
481
639
|
},
|
|
482
640
|
],
|
|
483
641
|
default: 'puppeteer',
|
|
484
|
-
description: 'Method for clicking',
|
|
642
|
+
description: 'Method to use for clicking',
|
|
485
643
|
},
|
|
486
644
|
{
|
|
487
|
-
displayName: 'Timeout
|
|
645
|
+
displayName: 'Timeout',
|
|
488
646
|
name: 'timeout',
|
|
489
647
|
type: 'number',
|
|
490
648
|
displayOptions: {
|
|
@@ -494,7 +652,7 @@ exports.nvkBrowserApiDescription = {
|
|
|
494
652
|
},
|
|
495
653
|
},
|
|
496
654
|
default: 30000,
|
|
497
|
-
description: '
|
|
655
|
+
description: 'Timeout in milliseconds',
|
|
498
656
|
},
|
|
499
657
|
{
|
|
500
658
|
displayName: 'Tab Index',
|
|
@@ -523,7 +681,7 @@ exports.nvkBrowserApiDescription = {
|
|
|
523
681
|
description: 'Whether to auto-start profile if not running',
|
|
524
682
|
},
|
|
525
683
|
{
|
|
526
|
-
displayName: 'Wait
|
|
684
|
+
displayName: 'Wait For Click',
|
|
527
685
|
name: 'waitForClick',
|
|
528
686
|
type: 'number',
|
|
529
687
|
displayOptions: {
|
|
@@ -533,10 +691,10 @@ exports.nvkBrowserApiDescription = {
|
|
|
533
691
|
},
|
|
534
692
|
},
|
|
535
693
|
default: 500,
|
|
536
|
-
description: '
|
|
694
|
+
description: 'Wait time before click in milliseconds',
|
|
537
695
|
},
|
|
538
696
|
{
|
|
539
|
-
displayName: '
|
|
697
|
+
displayName: 'Button',
|
|
540
698
|
name: 'button',
|
|
541
699
|
type: 'options',
|
|
542
700
|
displayOptions: {
|
|
@@ -589,7 +747,7 @@ exports.nvkBrowserApiDescription = {
|
|
|
589
747
|
description: 'Whether to upload a binary file',
|
|
590
748
|
},
|
|
591
749
|
{
|
|
592
|
-
displayName: '
|
|
750
|
+
displayName: 'Input Data Field Name',
|
|
593
751
|
name: 'binaryPropertyName',
|
|
594
752
|
type: 'string',
|
|
595
753
|
displayOptions: {
|
|
@@ -601,7 +759,7 @@ exports.nvkBrowserApiDescription = {
|
|
|
601
759
|
},
|
|
602
760
|
default: 'data',
|
|
603
761
|
required: true,
|
|
604
|
-
description: 'Name of the binary property
|
|
762
|
+
description: 'Name of the binary property',
|
|
605
763
|
},
|
|
606
764
|
// Run JavaScript Fields
|
|
607
765
|
{
|
|
@@ -677,7 +835,7 @@ exports.nvkBrowserApiDescription = {
|
|
|
677
835
|
description: 'ID of the profile',
|
|
678
836
|
},
|
|
679
837
|
{
|
|
680
|
-
displayName: 'Request
|
|
838
|
+
displayName: 'Request Filter',
|
|
681
839
|
name: 'requestFilter',
|
|
682
840
|
type: 'string',
|
|
683
841
|
displayOptions: {
|
|
@@ -709,24 +867,16 @@ exports.nvkBrowserApiDescription = {
|
|
|
709
867
|
name: 'Exact',
|
|
710
868
|
value: 'exact',
|
|
711
869
|
},
|
|
712
|
-
{
|
|
713
|
-
name: 'Starts With',
|
|
714
|
-
value: 'startsWith',
|
|
715
|
-
},
|
|
716
|
-
{
|
|
717
|
-
name: 'Ends With',
|
|
718
|
-
value: 'endsWith',
|
|
719
|
-
},
|
|
720
870
|
{
|
|
721
871
|
name: 'Regex',
|
|
722
872
|
value: 'regex',
|
|
723
873
|
},
|
|
724
874
|
],
|
|
725
875
|
default: 'contains',
|
|
726
|
-
description: '
|
|
876
|
+
description: 'Type of matching to use',
|
|
727
877
|
},
|
|
728
878
|
{
|
|
729
|
-
displayName: 'Timeout
|
|
879
|
+
displayName: 'Timeout',
|
|
730
880
|
name: 'timeout',
|
|
731
881
|
type: 'number',
|
|
732
882
|
displayOptions: {
|
|
@@ -736,7 +886,7 @@ exports.nvkBrowserApiDescription = {
|
|
|
736
886
|
},
|
|
737
887
|
},
|
|
738
888
|
default: 30000,
|
|
739
|
-
description: 'Timeout
|
|
889
|
+
description: 'Timeout in milliseconds',
|
|
740
890
|
},
|
|
741
891
|
{
|
|
742
892
|
displayName: 'Wait For Request',
|
|
@@ -880,6 +1030,99 @@ exports.nvkBrowserApiDescription = {
|
|
|
880
1030
|
default: 'formData',
|
|
881
1031
|
description: 'Type of body content',
|
|
882
1032
|
},
|
|
1033
|
+
// Get Recaptcha Token Fields
|
|
1034
|
+
{
|
|
1035
|
+
displayName: 'Profile ID',
|
|
1036
|
+
name: 'profileId',
|
|
1037
|
+
type: 'string',
|
|
1038
|
+
displayOptions: {
|
|
1039
|
+
show: {
|
|
1040
|
+
resource: ['pageInteraction'],
|
|
1041
|
+
operation: ['getRecaptchaToken'],
|
|
1042
|
+
},
|
|
1043
|
+
},
|
|
1044
|
+
default: '',
|
|
1045
|
+
required: true,
|
|
1046
|
+
description: 'ID of the profile',
|
|
1047
|
+
},
|
|
1048
|
+
{
|
|
1049
|
+
displayName: 'Site Key',
|
|
1050
|
+
name: 'siteKey',
|
|
1051
|
+
type: 'string',
|
|
1052
|
+
displayOptions: {
|
|
1053
|
+
show: {
|
|
1054
|
+
resource: ['pageInteraction'],
|
|
1055
|
+
operation: ['getRecaptchaToken'],
|
|
1056
|
+
},
|
|
1057
|
+
},
|
|
1058
|
+
default: '',
|
|
1059
|
+
description: 'reCAPTCHA site key (auto-detect if empty)',
|
|
1060
|
+
},
|
|
1061
|
+
{
|
|
1062
|
+
displayName: 'Tab Index',
|
|
1063
|
+
name: 'tabIndex',
|
|
1064
|
+
type: 'number',
|
|
1065
|
+
displayOptions: {
|
|
1066
|
+
show: {
|
|
1067
|
+
resource: ['pageInteraction'],
|
|
1068
|
+
operation: ['getRecaptchaToken'],
|
|
1069
|
+
},
|
|
1070
|
+
},
|
|
1071
|
+
default: 0,
|
|
1072
|
+
description: 'Browser tab index',
|
|
1073
|
+
},
|
|
1074
|
+
{
|
|
1075
|
+
displayName: 'Timeout',
|
|
1076
|
+
name: 'timeout',
|
|
1077
|
+
type: 'number',
|
|
1078
|
+
displayOptions: {
|
|
1079
|
+
show: {
|
|
1080
|
+
resource: ['pageInteraction'],
|
|
1081
|
+
operation: ['getRecaptchaToken'],
|
|
1082
|
+
},
|
|
1083
|
+
},
|
|
1084
|
+
default: 10000,
|
|
1085
|
+
description: 'Timeout in milliseconds',
|
|
1086
|
+
},
|
|
1087
|
+
{
|
|
1088
|
+
displayName: 'Action',
|
|
1089
|
+
name: 'action',
|
|
1090
|
+
type: 'string',
|
|
1091
|
+
displayOptions: {
|
|
1092
|
+
show: {
|
|
1093
|
+
resource: ['pageInteraction'],
|
|
1094
|
+
operation: ['getRecaptchaToken'],
|
|
1095
|
+
},
|
|
1096
|
+
},
|
|
1097
|
+
default: 'FLOW_GENERATION',
|
|
1098
|
+
description: 'reCAPTCHA action parameter',
|
|
1099
|
+
},
|
|
1100
|
+
{
|
|
1101
|
+
displayName: 'Max Retries',
|
|
1102
|
+
name: 'maxRetries',
|
|
1103
|
+
type: 'number',
|
|
1104
|
+
displayOptions: {
|
|
1105
|
+
show: {
|
|
1106
|
+
resource: ['pageInteraction'],
|
|
1107
|
+
operation: ['getRecaptchaToken'],
|
|
1108
|
+
},
|
|
1109
|
+
},
|
|
1110
|
+
default: 3,
|
|
1111
|
+
description: 'Maximum number of retry attempts',
|
|
1112
|
+
},
|
|
1113
|
+
{
|
|
1114
|
+
displayName: 'Auto Start Profile',
|
|
1115
|
+
name: 'autoStart',
|
|
1116
|
+
type: 'boolean',
|
|
1117
|
+
displayOptions: {
|
|
1118
|
+
show: {
|
|
1119
|
+
resource: ['pageInteraction'],
|
|
1120
|
+
operation: ['getRecaptchaToken'],
|
|
1121
|
+
},
|
|
1122
|
+
},
|
|
1123
|
+
default: false,
|
|
1124
|
+
description: 'Whether to auto-start profile if not running',
|
|
1125
|
+
},
|
|
883
1126
|
// n8n Binary File - Input Data Field Name
|
|
884
1127
|
{
|
|
885
1128
|
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
|
+
}
|