n8n-nodes-nvk-browser 1.0.113 → 1.0.114
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.
|
@@ -56,6 +56,12 @@ exports.nvkBrowserFields = [
|
|
|
56
56
|
description: 'Stop a running browser profile',
|
|
57
57
|
action: 'Stop a profile',
|
|
58
58
|
},
|
|
59
|
+
{
|
|
60
|
+
name: 'Update Profile',
|
|
61
|
+
value: 'updateProfile',
|
|
62
|
+
description: 'Update an existing browser profile',
|
|
63
|
+
action: 'Update a profile',
|
|
64
|
+
},
|
|
59
65
|
],
|
|
60
66
|
default: 'createProfile',
|
|
61
67
|
},
|
|
@@ -289,6 +295,21 @@ exports.nvkBrowserFields = [
|
|
|
289
295
|
},
|
|
290
296
|
},
|
|
291
297
|
},
|
|
298
|
+
{
|
|
299
|
+
displayName: 'Initial URL',
|
|
300
|
+
name: 'initialUrl',
|
|
301
|
+
type: 'string',
|
|
302
|
+
required: false,
|
|
303
|
+
default: '',
|
|
304
|
+
placeholder: 'https://example.com',
|
|
305
|
+
description: 'URL to navigate to when profile starts (optional)',
|
|
306
|
+
displayOptions: {
|
|
307
|
+
show: {
|
|
308
|
+
resource: ['profileManagement'],
|
|
309
|
+
operation: ['startProfile'],
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
},
|
|
292
313
|
// ===== Profile Management: Stop Profile =====
|
|
293
314
|
{
|
|
294
315
|
displayName: 'Profile ID',
|
|
@@ -304,6 +325,78 @@ exports.nvkBrowserFields = [
|
|
|
304
325
|
},
|
|
305
326
|
},
|
|
306
327
|
},
|
|
328
|
+
// ===== Profile Management: Update Profile =====
|
|
329
|
+
{
|
|
330
|
+
displayName: 'Profile ID',
|
|
331
|
+
name: 'profileId',
|
|
332
|
+
type: 'string',
|
|
333
|
+
required: true,
|
|
334
|
+
default: '',
|
|
335
|
+
description: 'ID of the profile to update',
|
|
336
|
+
displayOptions: {
|
|
337
|
+
show: {
|
|
338
|
+
resource: ['profileManagement'],
|
|
339
|
+
operation: ['updateProfile'],
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
displayName: 'Profile Name',
|
|
345
|
+
name: 'profileName',
|
|
346
|
+
type: 'string',
|
|
347
|
+
required: false,
|
|
348
|
+
default: '',
|
|
349
|
+
description: 'Name of the profile (optional)',
|
|
350
|
+
displayOptions: {
|
|
351
|
+
show: {
|
|
352
|
+
resource: ['profileManagement'],
|
|
353
|
+
operation: ['updateProfile'],
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
displayName: 'Proxy',
|
|
359
|
+
name: 'proxy',
|
|
360
|
+
type: 'string',
|
|
361
|
+
default: '',
|
|
362
|
+
placeholder: 'http:127.0.0.1:8080:user:pass hoặc socks5:127.0.0.1:1080:user:pass',
|
|
363
|
+
description: 'Proxy format: http://myip:port:user:pass or socks5://myip:port:user:pass (hoặc không có ://)',
|
|
364
|
+
displayOptions: {
|
|
365
|
+
show: {
|
|
366
|
+
resource: ['profileManagement'],
|
|
367
|
+
operation: ['updateProfile'],
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
displayName: 'Note',
|
|
373
|
+
name: 'note',
|
|
374
|
+
type: 'string',
|
|
375
|
+
default: '',
|
|
376
|
+
description: 'Optional note for this profile',
|
|
377
|
+
displayOptions: {
|
|
378
|
+
show: {
|
|
379
|
+
resource: ['profileManagement'],
|
|
380
|
+
operation: ['updateProfile'],
|
|
381
|
+
},
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
displayName: 'Extensions',
|
|
386
|
+
name: 'extensions',
|
|
387
|
+
type: 'string',
|
|
388
|
+
typeOptions: {
|
|
389
|
+
rows: 4,
|
|
390
|
+
},
|
|
391
|
+
default: '',
|
|
392
|
+
description: 'Chrome Store extension IDs or local paths, one per line',
|
|
393
|
+
displayOptions: {
|
|
394
|
+
show: {
|
|
395
|
+
resource: ['profileManagement'],
|
|
396
|
+
operation: ['updateProfile'],
|
|
397
|
+
},
|
|
398
|
+
},
|
|
399
|
+
},
|
|
307
400
|
// ===== Page Interaction: Move And Click =====
|
|
308
401
|
{
|
|
309
402
|
displayName: 'Profile ID',
|
|
@@ -30,6 +30,7 @@ exports.NvkBrowser = void 0;
|
|
|
30
30
|
const BrowserManager_1 = require("../../utils/BrowserManager");
|
|
31
31
|
const ProfileManager_1 = require("../../utils/ProfileManager");
|
|
32
32
|
const ExtensionHandler_1 = require("../../utils/ExtensionHandler");
|
|
33
|
+
const ProxyHandler_1 = require("../../utils/ProxyHandler");
|
|
33
34
|
const NvkBrowser_description_1 = require("./NvkBrowser.description");
|
|
34
35
|
const path = __importStar(require("path"));
|
|
35
36
|
const fs = __importStar(require("fs"));
|
|
@@ -85,6 +86,9 @@ class NvkBrowser {
|
|
|
85
86
|
case 'stopProfile':
|
|
86
87
|
await executeStopProfile.call(this, i, browserManager, returnData);
|
|
87
88
|
break;
|
|
89
|
+
case 'updateProfile':
|
|
90
|
+
await executeUpdateProfile.call(this, i, profileManager, returnData);
|
|
91
|
+
break;
|
|
88
92
|
default:
|
|
89
93
|
throw new Error(`Unknown operation: ${operation}`);
|
|
90
94
|
}
|
|
@@ -412,6 +416,7 @@ async function executeStartProfile(i, browserManager, returnData) {
|
|
|
412
416
|
const winPosition = this.getNodeParameter('winPosition', i);
|
|
413
417
|
const winSize = this.getNodeParameter('winSize', i);
|
|
414
418
|
const headless = this.getNodeParameter('headless', i);
|
|
419
|
+
const initialUrl = this.getNodeParameter('initialUrl', i);
|
|
415
420
|
const windowConfig = {};
|
|
416
421
|
if (winScale !== undefined) {
|
|
417
422
|
windowConfig.scale = winScale;
|
|
@@ -432,12 +437,27 @@ async function executeStartProfile(i, browserManager, returnData) {
|
|
|
432
437
|
windowConfig.headless = headless;
|
|
433
438
|
}
|
|
434
439
|
const instance = await browserManager.startProfile(profileId, windowConfig);
|
|
440
|
+
// Navigate to URL nếu có
|
|
441
|
+
if (initialUrl && initialUrl.trim() !== '') {
|
|
442
|
+
try {
|
|
443
|
+
const page = await browserManager.getPage(profileId, 0);
|
|
444
|
+
if (page) {
|
|
445
|
+
await page.goto(initialUrl.trim(), { waitUntil: 'networkidle2' });
|
|
446
|
+
console.log(`[NvkBrowser] Navigated to ${initialUrl}`);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
catch (navError) {
|
|
450
|
+
console.error(`[NvkBrowser] Error navigating to ${initialUrl}:`, navError);
|
|
451
|
+
// Không throw error, chỉ log vì profile đã start thành công
|
|
452
|
+
}
|
|
453
|
+
}
|
|
435
454
|
returnData.push({
|
|
436
455
|
json: {
|
|
437
456
|
success: true,
|
|
438
457
|
profileId: instance.profileId,
|
|
439
458
|
debugPort: instance.debugPort,
|
|
440
459
|
message: 'Profile started successfully',
|
|
460
|
+
initialUrl: initialUrl && initialUrl.trim() !== '' ? initialUrl.trim() : undefined,
|
|
441
461
|
},
|
|
442
462
|
});
|
|
443
463
|
}
|
|
@@ -452,6 +472,130 @@ async function executeStopProfile(i, browserManager, returnData) {
|
|
|
452
472
|
},
|
|
453
473
|
});
|
|
454
474
|
}
|
|
475
|
+
async function executeUpdateProfile(i, profileManager, returnData) {
|
|
476
|
+
const profileId = this.getNodeParameter('profileId', i);
|
|
477
|
+
// Kiểm tra profile có tồn tại không
|
|
478
|
+
const existingProfile = profileManager.getProfile(profileId);
|
|
479
|
+
if (!existingProfile) {
|
|
480
|
+
throw new Error(`Profile with ID ${profileId} not found`);
|
|
481
|
+
}
|
|
482
|
+
// Lấy các giá trị cần cập nhật (chỉ lấy nếu có giá trị)
|
|
483
|
+
const updates = {};
|
|
484
|
+
const profileName = this.getNodeParameter('profileName', i);
|
|
485
|
+
if (profileName !== undefined && profileName !== '') {
|
|
486
|
+
updates.name = profileName;
|
|
487
|
+
}
|
|
488
|
+
const proxy = this.getNodeParameter('proxy', i);
|
|
489
|
+
if (proxy !== undefined && proxy !== '') {
|
|
490
|
+
updates.proxy = proxy;
|
|
491
|
+
}
|
|
492
|
+
else if (proxy === '') {
|
|
493
|
+
// Cho phép xóa proxy bằng cách truyền chuỗi rỗng
|
|
494
|
+
updates.proxy = undefined;
|
|
495
|
+
}
|
|
496
|
+
const note = this.getNodeParameter('note', i);
|
|
497
|
+
if (note !== undefined && note !== '') {
|
|
498
|
+
updates.note = note;
|
|
499
|
+
}
|
|
500
|
+
else if (note === '') {
|
|
501
|
+
// Cho phép xóa note bằng cách truyền chuỗi rỗng
|
|
502
|
+
updates.note = undefined;
|
|
503
|
+
}
|
|
504
|
+
const extensionsInput = this.getNodeParameter('extensions', i);
|
|
505
|
+
if (extensionsInput !== undefined && extensionsInput !== '') {
|
|
506
|
+
updates.extensions = ExtensionHandler_1.ExtensionHandler.parseExtensions(extensionsInput);
|
|
507
|
+
}
|
|
508
|
+
// Cập nhật profile
|
|
509
|
+
const updatedProfile = profileManager.updateProfile(profileId, updates);
|
|
510
|
+
if (!updatedProfile) {
|
|
511
|
+
throw new Error(`Failed to update profile ${profileId}`);
|
|
512
|
+
}
|
|
513
|
+
// Xử lý proxy authentication nếu có proxy mới
|
|
514
|
+
if (updates.proxy !== undefined && updates.proxy) {
|
|
515
|
+
console.log(`[NvkBrowser] Processing proxy: ${updates.proxy}`);
|
|
516
|
+
const proxyConfig = ProxyHandler_1.ProxyHandler.parseProxyString(updates.proxy);
|
|
517
|
+
if (proxyConfig) {
|
|
518
|
+
console.log(`[NvkBrowser] Parsed proxy config successfully:`, {
|
|
519
|
+
type: proxyConfig.type,
|
|
520
|
+
host: proxyConfig.host,
|
|
521
|
+
port: proxyConfig.port,
|
|
522
|
+
username: proxyConfig.username || 'N/A',
|
|
523
|
+
password: proxyConfig.password ? '***' : 'N/A',
|
|
524
|
+
});
|
|
525
|
+
const profilePath = profileManager.getProfilePath(profileId);
|
|
526
|
+
console.log(`[NvkBrowser] Profile path: ${profilePath}`);
|
|
527
|
+
console.log(`[NvkBrowser] Calling saveProxyAuthToProfile...`);
|
|
528
|
+
try {
|
|
529
|
+
ProxyHandler_1.ProxyHandler.saveProxyAuthToProfile(profilePath, proxyConfig);
|
|
530
|
+
console.log(`[NvkBrowser] saveProxyAuthToProfile completed successfully`);
|
|
531
|
+
}
|
|
532
|
+
catch (saveError) {
|
|
533
|
+
console.error(`[NvkBrowser] ERROR in saveProxyAuthToProfile:`, saveError);
|
|
534
|
+
throw new Error(`Failed to save proxy authentication: ${saveError instanceof Error ? saveError.message : String(saveError)}`);
|
|
535
|
+
}
|
|
536
|
+
// Validation: Đọc lại file Preferences để verify
|
|
537
|
+
const prefsPath = path.join(profilePath, 'Default', 'Preferences');
|
|
538
|
+
if (fs.existsSync(prefsPath)) {
|
|
539
|
+
try {
|
|
540
|
+
const verifyContent = fs.readFileSync(prefsPath, 'utf-8');
|
|
541
|
+
const verifyPrefs = JSON.parse(verifyContent);
|
|
542
|
+
// Verify proxy_config
|
|
543
|
+
if (!verifyPrefs.proxy_config || !verifyPrefs.proxy_config.server) {
|
|
544
|
+
console.error(`[NvkBrowser] VALIDATION FAILED: proxy_config not found in Preferences`);
|
|
545
|
+
}
|
|
546
|
+
else {
|
|
547
|
+
console.log(`[NvkBrowser] ✓ Validation passed: proxy_config.server = ${verifyPrefs.proxy_config.server}`);
|
|
548
|
+
}
|
|
549
|
+
// Verify gologin.proxy
|
|
550
|
+
if (!verifyPrefs.gologin || !verifyPrefs.gologin.proxy) {
|
|
551
|
+
console.error(`[NvkBrowser] VALIDATION FAILED: gologin.proxy not found in Preferences`);
|
|
552
|
+
}
|
|
553
|
+
else {
|
|
554
|
+
const hasUsername = !!verifyPrefs.gologin.proxy.username;
|
|
555
|
+
const hasPassword = !!verifyPrefs.gologin.proxy.password;
|
|
556
|
+
if (proxyConfig.username && !hasUsername) {
|
|
557
|
+
console.error(`[NvkBrowser] VALIDATION FAILED: username not saved in gologin.proxy`);
|
|
558
|
+
}
|
|
559
|
+
if (proxyConfig.password && !hasPassword) {
|
|
560
|
+
console.error(`[NvkBrowser] VALIDATION FAILED: password not saved in gologin.proxy`);
|
|
561
|
+
}
|
|
562
|
+
if ((!proxyConfig.username || hasUsername) && (!proxyConfig.password || hasPassword)) {
|
|
563
|
+
console.log(`[NvkBrowser] ✓ Validation passed: gologin.proxy has required fields`);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
catch (verifyError) {
|
|
568
|
+
console.error(`[NvkBrowser] VALIDATION ERROR: Failed to verify Preferences file: ${verifyError instanceof Error ? verifyError.message : String(verifyError)}`);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
else {
|
|
572
|
+
console.error(`[NvkBrowser] VALIDATION FAILED: Preferences file does not exist at ${prefsPath}`);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
else {
|
|
576
|
+
console.error(`[NvkBrowser] ERROR: Failed to parse proxy string: ${updates.proxy}`);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
else if (updates.proxy === undefined && existingProfile.proxy) {
|
|
580
|
+
// Nếu proxy bị xóa (undefined), cần xử lý xóa proxy config
|
|
581
|
+
console.log(`[NvkBrowser] Proxy removed, cleaning up proxy configuration`);
|
|
582
|
+
// Có thể cần thêm logic để xóa proxy config khỏi Preferences nếu cần
|
|
583
|
+
}
|
|
584
|
+
returnData.push({
|
|
585
|
+
json: {
|
|
586
|
+
success: true,
|
|
587
|
+
profile: {
|
|
588
|
+
id: updatedProfile.id,
|
|
589
|
+
name: updatedProfile.name,
|
|
590
|
+
proxy: updatedProfile.proxy,
|
|
591
|
+
note: updatedProfile.note,
|
|
592
|
+
extensions: updatedProfile.extensions,
|
|
593
|
+
createdAt: updatedProfile.createdAt,
|
|
594
|
+
updatedAt: updatedProfile.updatedAt,
|
|
595
|
+
},
|
|
596
|
+
},
|
|
597
|
+
});
|
|
598
|
+
}
|
|
455
599
|
async function executeMoveAndClick(i, browserManager, returnData) {
|
|
456
600
|
const profileId = this.getNodeParameter('profileId', i);
|
|
457
601
|
const selector = this.getNodeParameter('selector', i);
|
|
@@ -988,10 +1132,11 @@ async function executeMoveAndClick(i, browserManager, returnData) {
|
|
|
988
1132
|
if (waitForClick > 0) {
|
|
989
1133
|
await page.waitForTimeout(waitForClick);
|
|
990
1134
|
}
|
|
991
|
-
|
|
1135
|
+
const clickOptions = {
|
|
992
1136
|
button: button,
|
|
993
1137
|
clickCount: clickCount,
|
|
994
|
-
}
|
|
1138
|
+
};
|
|
1139
|
+
await page.click(selectorTrimmed, clickOptions);
|
|
995
1140
|
}
|
|
996
1141
|
}
|
|
997
1142
|
}
|
|
@@ -1011,12 +1156,12 @@ async function executeMoveAndClick(i, browserManager, returnData) {
|
|
|
1011
1156
|
action: actionType || 'click',
|
|
1012
1157
|
};
|
|
1013
1158
|
if (executedActionsInfo && executedActionsInfo.length > 0) {
|
|
1014
|
-
const allSuccess = executedActionsInfo.every(a => a.success);
|
|
1159
|
+
const allSuccess = executedActionsInfo.every((a) => a.success);
|
|
1015
1160
|
returnJson.success = allSuccess;
|
|
1016
1161
|
returnJson.message = `Executed ${executedActionsInfo.length} action(s)`;
|
|
1017
1162
|
returnJson.actions = executedActionsInfo;
|
|
1018
1163
|
returnJson.totalActions = executedActionsInfo.length;
|
|
1019
|
-
returnJson.successfulActions = executedActionsInfo.filter(a => a.success).length;
|
|
1164
|
+
returnJson.successfulActions = executedActionsInfo.filter((a) => a.success).length;
|
|
1020
1165
|
}
|
|
1021
1166
|
else {
|
|
1022
1167
|
returnJson.message = actionType === 'fill' ? 'Fill performed successfully' : 'Click performed successfully';
|
|
@@ -1241,7 +1386,7 @@ async function executeBrowserHttpRequest(i, browserManager, returnData) {
|
|
|
1241
1386
|
throw new Error(`Could not get page for profile ${profileId}`);
|
|
1242
1387
|
}
|
|
1243
1388
|
const cookies = await page.cookies();
|
|
1244
|
-
const cookieString = cookies.map(cookie => `${cookie.name}=${cookie.value}`).join('; ');
|
|
1389
|
+
const cookieString = cookies.map((cookie) => `${cookie.name}=${cookie.value}`).join('; ');
|
|
1245
1390
|
const userAgent = await page.evaluate(() => navigator.userAgent);
|
|
1246
1391
|
const browserHeaders = {
|
|
1247
1392
|
'User-Agent': userAgent,
|
|
@@ -1327,7 +1472,7 @@ async function executeBrowserHttpRequest(i, browserManager, returnData) {
|
|
|
1327
1472
|
else if (bodyContentType === 'formData') {
|
|
1328
1473
|
const formDataParam = this.getNodeParameter('formData', i);
|
|
1329
1474
|
if (formDataParam?.parameter) {
|
|
1330
|
-
const hasBinary = formDataParam.parameter.some(p => p.parameterType === 'binary');
|
|
1475
|
+
const hasBinary = formDataParam.parameter.some((p) => p.parameterType === 'binary');
|
|
1331
1476
|
if (hasBinary) {
|
|
1332
1477
|
// Handle FormData with binary files
|
|
1333
1478
|
const formDataItems = [];
|
package/package.json
CHANGED