n8n-nodes-nvk-browser 1.0.39 → 1.0.40

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.
@@ -21,7 +21,8 @@ exports.createProfileFields = [
21
21
  name: 'proxy',
22
22
  type: 'string',
23
23
  default: '',
24
- description: 'Proxy format: http:127.0.0.1:8080:user:pass or socks5:127.0.0.1:1080:user:pass',
24
+ placeholder: 'http://127.0.0.1:8080:user:pass hoặc socks5:127.0.0.1:1080:user:pass',
25
+ description: 'Proxy format: http://ip:port:user:pass or socks5:ip:port:user:pass',
25
26
  displayOptions: {
26
27
  show: {
27
28
  resource: ['profile'],
@@ -43,14 +44,15 @@ exports.createProfileFields = [
43
44
  },
44
45
  },
45
46
  {
46
- displayName: 'Extensions',
47
- name: 'extensions',
47
+ displayName: 'Extension',
48
+ name: 'extension',
48
49
  type: 'string',
49
50
  typeOptions: {
50
51
  rows: 4,
51
52
  },
52
53
  default: '',
53
- description: 'Chrome Store extension IDs or local paths, one per line',
54
+ placeholder: 'Nhập ID extension Chrome Store hoặc đường dẫn tới extension local, mỗi extension một dòng',
55
+ description: 'Enter Chrome Store extension ID or path to local extension, one extension per line',
54
56
  displayOptions: {
55
57
  show: {
56
58
  resource: ['profile'],
@@ -24,6 +24,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.CreateProfile = void 0;
27
+ const n8n_workflow_1 = require("n8n-workflow");
27
28
  const path = __importStar(require("path"));
28
29
  const ProfileManager_1 = require("../../../utils/ProfileManager");
29
30
  const ExtensionHandler_1 = require("../../../utils/ExtensionHandler");
@@ -91,14 +92,24 @@ class CreateProfile {
91
92
  const profileName = this.getNodeParameter('profileName', i);
92
93
  const proxy = this.getNodeParameter('proxy', i) || undefined;
93
94
  const note = this.getNodeParameter('note', i) || undefined;
94
- const extensionsInput = this.getNodeParameter('extensions', i) || '';
95
- // Parse extensions
96
- const extensions = ExtensionHandler_1.ExtensionHandler.parseExtensions(extensionsInput);
95
+ const extensionInput = this.getNodeParameter('extension', i) || '';
96
+ if (!profileName) {
97
+ throw new n8n_workflow_1.ApplicationError('Profile Name is required');
98
+ }
99
+ // Parse extensions từ input (mỗi extension một dòng)
100
+ const extensions = ExtensionHandler_1.ExtensionHandler.parseExtensions(extensionInput);
97
101
  // Tạo profile
98
102
  const profile = profileManager.createProfile(profileName, proxy, note, extensions);
103
+ // Cài đặt extensions (Chrome Store IDs và local paths)
104
+ if (extensions.length > 0) {
105
+ const profilePath = profileManager.getProfilePath(profile.id);
106
+ ExtensionHandler_1.ExtensionHandler.installExtensions(profilePath, extensions);
107
+ }
99
108
  returnData.push({
100
109
  json: {
101
110
  success: true,
111
+ profile_id: profile.id,
112
+ message: 'Profile created successfully',
102
113
  profile: {
103
114
  id: profile.id,
104
115
  name: profile.name,
@@ -111,12 +122,16 @@ class CreateProfile {
111
122
  });
112
123
  }
113
124
  catch (error) {
114
- returnData.push({
115
- json: {
116
- success: false,
117
- error: error instanceof Error ? error.message : String(error),
118
- },
119
- });
125
+ if (this.continueOnFail()) {
126
+ returnData.push({
127
+ json: {
128
+ success: false,
129
+ error: error instanceof Error ? error.message : String(error),
130
+ },
131
+ });
132
+ continue;
133
+ }
134
+ throw error;
120
135
  }
121
136
  }
122
137
  return [returnData];
@@ -5,5 +5,8 @@ export declare class ExtensionHandler {
5
5
  path?: string;
6
6
  error?: string;
7
7
  };
8
+ static getExtensionId(extension: string): string | null;
9
+ static installExtensions(profilePath: string, extensions: string[]): void;
10
+ private static installChromeStoreExtensions;
8
11
  static getExtensionArgs(extensions: string[]): string[];
9
12
  }
@@ -40,9 +40,15 @@ class ExtensionHandler {
40
40
  }
41
41
  static validateExtension(extension) {
42
42
  // Nếu là ID từ Chrome Store (thường là 32 ký tự alphanumeric)
43
+ // Cũng hỗ trợ format URL từ Chrome Web Store
43
44
  if (/^[a-z]{32}$/i.test(extension)) {
44
45
  return { valid: true };
45
46
  }
47
+ // Nếu là URL Chrome Web Store, extract ID
48
+ const chromeStoreMatch = extension.match(/chrome\.google\.com\/webstore\/detail\/[^/]+\/([a-z]{32})/i);
49
+ if (chromeStoreMatch) {
50
+ return { valid: true };
51
+ }
46
52
  // Nếu là đường dẫn local
47
53
  if (path.isAbsolute(extension) || extension.startsWith('./') || extension.startsWith('../')) {
48
54
  const resolvedPath = path.resolve(extension);
@@ -67,6 +73,73 @@ class ExtensionHandler {
67
73
  }
68
74
  return { valid: false, error: 'Invalid extension format. Must be Chrome Store ID or valid path' };
69
75
  }
76
+ static getExtensionId(extension) {
77
+ // Nếu là ID trực tiếp (32 ký tự)
78
+ if (/^[a-z]{32}$/i.test(extension)) {
79
+ return extension;
80
+ }
81
+ // Nếu là URL Chrome Web Store, extract ID
82
+ const chromeStoreMatch = extension.match(/chrome\.google\.com\/webstore\/detail\/[^/]+\/([a-z]{32})/i);
83
+ if (chromeStoreMatch) {
84
+ return chromeStoreMatch[1];
85
+ }
86
+ return null;
87
+ }
88
+ static installExtensions(profilePath, extensions) {
89
+ if (!extensions || extensions.length === 0) {
90
+ return;
91
+ }
92
+ // Tách Chrome Store IDs và local paths
93
+ const chromeStoreIds = [];
94
+ const localPaths = [];
95
+ for (const ext of extensions) {
96
+ const extensionId = this.getExtensionId(ext);
97
+ if (extensionId) {
98
+ chromeStoreIds.push(extensionId);
99
+ }
100
+ else {
101
+ const validation = this.validateExtension(ext);
102
+ if (validation.valid && validation.path) {
103
+ localPaths.push(validation.path);
104
+ }
105
+ }
106
+ }
107
+ // Cài đặt Chrome Store extensions thông qua external_extensions.json
108
+ if (chromeStoreIds.length > 0) {
109
+ this.installChromeStoreExtensions(profilePath, chromeStoreIds);
110
+ }
111
+ // Local extensions sẽ được load qua --load-extension khi start browser
112
+ // (đã được xử lý trong getExtensionArgs)
113
+ }
114
+ static installChromeStoreExtensions(profilePath, extensionIds) {
115
+ // Tạo thư mục Extensions nếu chưa có
116
+ const extensionsDir = path.join(profilePath, 'Extensions');
117
+ if (!fs.existsSync(extensionsDir)) {
118
+ fs.mkdirSync(extensionsDir, { recursive: true });
119
+ }
120
+ // Đường dẫn đến file external_extensions.json
121
+ const externalExtensionsPath = path.join(extensionsDir, 'external_extensions.json');
122
+ // Đọc file hiện tại nếu có
123
+ let externalExtensions = {};
124
+ if (fs.existsSync(externalExtensionsPath)) {
125
+ try {
126
+ const content = fs.readFileSync(externalExtensionsPath, 'utf-8');
127
+ externalExtensions = JSON.parse(content);
128
+ }
129
+ catch (error) {
130
+ // Nếu file không hợp lệ, bắt đầu từ đầu
131
+ externalExtensions = {};
132
+ }
133
+ }
134
+ // Thêm các extension IDs vào external_extensions
135
+ for (const extensionId of extensionIds) {
136
+ externalExtensions[extensionId] = {
137
+ external_update_url: 'https://clients2.google.com/service/update2/crx',
138
+ };
139
+ }
140
+ // Ghi lại file
141
+ fs.writeFileSync(externalExtensionsPath, JSON.stringify(externalExtensions, null, 2), 'utf-8');
142
+ }
70
143
  static getExtensionArgs(extensions) {
71
144
  const args = [];
72
145
  const validPaths = [];
@@ -75,7 +148,7 @@ class ExtensionHandler {
75
148
  if (validation.valid && validation.path) {
76
149
  validPaths.push(validation.path);
77
150
  }
78
- // Chrome Store IDs sẽ được load sau khi browser khởi động
151
+ // Chrome Store IDs đã được cài đặt qua external_extensions.json
79
152
  }
80
153
  if (validPaths.length > 0) {
81
154
  args.push(`--load-extension=${validPaths.join(',')}`);
@@ -101,19 +101,23 @@ class ProfileManager {
101
101
  // Cũng set trong Preferences file
102
102
  const prefsPath = path.join(defaultDir, 'Preferences');
103
103
  let prefs = {};
104
+ // Đọc Preferences nếu đã tồn tại
104
105
  if (fs.existsSync(prefsPath)) {
105
106
  try {
106
107
  const prefsContent = fs.readFileSync(prefsPath, 'utf-8');
107
108
  prefs = JSON.parse(prefsContent);
108
109
  }
109
110
  catch (error) {
110
- // Ignore
111
+ // Nếu file không hợp lệ, bắt đầu từ đầu
112
+ prefs = {};
111
113
  }
112
114
  }
115
+ // Đảm bảo cấu trúc profile tồn tại
113
116
  if (!prefs.profile) {
114
117
  prefs.profile = {};
115
118
  }
116
119
  prefs.profile.name = profileName;
120
+ // Luôn ghi Preferences file (tạo mới hoặc cập nhật)
117
121
  fs.writeFileSync(prefsPath, JSON.stringify(prefs, null, 2), 'utf-8');
118
122
  }
119
123
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-nvk-browser",
3
- "version": "1.0.39",
3
+ "version": "1.0.40",
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",