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
- await page.click(selectorTrimmed, {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-nvk-browser",
3
- "version": "1.0.113",
3
+ "version": "1.0.114",
4
4
  "description": "n8n nodes for managing Chrome browser profiles and page interactions with Puppeteer automation",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",