n8n-nodes-nvk-browser 1.0.11 → 1.0.12

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.
@@ -0,0 +1,2 @@
1
+ import { INodeProperties } from 'n8n-workflow';
2
+ export declare const browserHttpRequestFields: INodeProperties[];
@@ -0,0 +1,274 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.browserHttpRequestFields = void 0;
4
+ exports.browserHttpRequestFields = [
5
+ {
6
+ displayName: 'Profile ID',
7
+ name: 'profileId',
8
+ type: 'string',
9
+ required: true,
10
+ default: '',
11
+ description: 'ID of the running profile to use for cookies and headers',
12
+ displayOptions: {
13
+ show: {
14
+ resource: ['page'],
15
+ operation: ['browserHttpRequest'],
16
+ },
17
+ },
18
+ },
19
+ {
20
+ displayName: 'Method',
21
+ name: 'method',
22
+ type: 'options',
23
+ options: [
24
+ {
25
+ name: 'GET',
26
+ value: 'GET',
27
+ },
28
+ {
29
+ name: 'POST',
30
+ value: 'POST',
31
+ },
32
+ ],
33
+ default: 'GET',
34
+ description: 'HTTP method to use',
35
+ displayOptions: {
36
+ show: {
37
+ resource: ['page'],
38
+ operation: ['browserHttpRequest'],
39
+ },
40
+ },
41
+ },
42
+ {
43
+ displayName: 'URL',
44
+ name: 'url',
45
+ type: 'string',
46
+ required: true,
47
+ default: '',
48
+ description: 'The URL to make the request to',
49
+ displayOptions: {
50
+ show: {
51
+ resource: ['page'],
52
+ operation: ['browserHttpRequest'],
53
+ },
54
+ },
55
+ },
56
+ {
57
+ displayName: 'Send Body',
58
+ name: 'sendBody',
59
+ type: 'boolean',
60
+ default: false,
61
+ description: 'Whether to send a body in the request',
62
+ displayOptions: {
63
+ show: {
64
+ resource: ['page'],
65
+ operation: ['browserHttpRequest'],
66
+ method: ['POST'],
67
+ },
68
+ },
69
+ },
70
+ {
71
+ displayName: 'Body Content Type',
72
+ name: 'bodyContentType',
73
+ type: 'options',
74
+ options: [
75
+ {
76
+ name: 'JSON',
77
+ value: 'json',
78
+ },
79
+ {
80
+ name: 'Raw',
81
+ value: 'raw',
82
+ },
83
+ {
84
+ name: 'Form Data',
85
+ value: 'formData',
86
+ },
87
+ ],
88
+ default: 'json',
89
+ description: 'Content type of the body',
90
+ displayOptions: {
91
+ show: {
92
+ resource: ['page'],
93
+ operation: ['browserHttpRequest'],
94
+ method: ['POST'],
95
+ sendBody: [true],
96
+ },
97
+ },
98
+ },
99
+ {
100
+ displayName: 'JSON Body',
101
+ name: 'jsonBody',
102
+ type: 'string',
103
+ typeOptions: {
104
+ rows: 4,
105
+ },
106
+ default: '',
107
+ description: 'JSON body to send',
108
+ displayOptions: {
109
+ show: {
110
+ resource: ['page'],
111
+ operation: ['browserHttpRequest'],
112
+ method: ['POST'],
113
+ sendBody: [true],
114
+ bodyContentType: ['json'],
115
+ },
116
+ },
117
+ },
118
+ {
119
+ displayName: 'Raw Body',
120
+ name: 'rawBody',
121
+ type: 'string',
122
+ typeOptions: {
123
+ rows: 4,
124
+ },
125
+ default: '',
126
+ description: 'Raw body to send',
127
+ displayOptions: {
128
+ show: {
129
+ resource: ['page'],
130
+ operation: ['browserHttpRequest'],
131
+ method: ['POST'],
132
+ sendBody: [true],
133
+ bodyContentType: ['raw'],
134
+ },
135
+ },
136
+ },
137
+ {
138
+ displayName: 'Form Data',
139
+ name: 'formData',
140
+ type: 'fixedCollection',
141
+ typeOptions: {
142
+ multipleValues: true,
143
+ },
144
+ default: {},
145
+ description: 'Form data to send',
146
+ displayOptions: {
147
+ show: {
148
+ resource: ['page'],
149
+ operation: ['browserHttpRequest'],
150
+ method: ['POST'],
151
+ sendBody: [true],
152
+ bodyContentType: ['formData'],
153
+ },
154
+ },
155
+ options: [
156
+ {
157
+ displayName: 'Parameter',
158
+ name: 'parameter',
159
+ values: [
160
+ {
161
+ displayName: 'Name',
162
+ name: 'name',
163
+ type: 'string',
164
+ default: '',
165
+ },
166
+ {
167
+ displayName: 'Value',
168
+ name: 'value',
169
+ type: 'string',
170
+ default: '',
171
+ },
172
+ ],
173
+ },
174
+ ],
175
+ },
176
+ {
177
+ displayName: 'Query Parameters',
178
+ name: 'queryParameters',
179
+ type: 'fixedCollection',
180
+ typeOptions: {
181
+ multipleValues: true,
182
+ },
183
+ default: {},
184
+ description: 'Query parameters to send',
185
+ displayOptions: {
186
+ show: {
187
+ resource: ['page'],
188
+ operation: ['browserHttpRequest'],
189
+ },
190
+ },
191
+ options: [
192
+ {
193
+ displayName: 'Parameter',
194
+ name: 'parameter',
195
+ values: [
196
+ {
197
+ displayName: 'Name',
198
+ name: 'name',
199
+ type: 'string',
200
+ default: '',
201
+ },
202
+ {
203
+ displayName: 'Value',
204
+ name: 'value',
205
+ type: 'string',
206
+ default: '',
207
+ },
208
+ ],
209
+ },
210
+ ],
211
+ },
212
+ {
213
+ displayName: 'Additional Headers',
214
+ name: 'additionalHeaders',
215
+ type: 'fixedCollection',
216
+ typeOptions: {
217
+ multipleValues: true,
218
+ },
219
+ default: {},
220
+ description: 'Additional headers to send (will be merged with browser headers)',
221
+ displayOptions: {
222
+ show: {
223
+ resource: ['page'],
224
+ operation: ['browserHttpRequest'],
225
+ },
226
+ },
227
+ options: [
228
+ {
229
+ displayName: 'Header',
230
+ name: 'header',
231
+ values: [
232
+ {
233
+ displayName: 'Name',
234
+ name: 'name',
235
+ type: 'string',
236
+ default: '',
237
+ },
238
+ {
239
+ displayName: 'Value',
240
+ name: 'value',
241
+ type: 'string',
242
+ default: '',
243
+ },
244
+ ],
245
+ },
246
+ ],
247
+ },
248
+ {
249
+ displayName: 'Tab Index',
250
+ name: 'tabIndex',
251
+ type: 'number',
252
+ default: 0,
253
+ description: 'Index of the tab to get cookies and headers from (0 = first tab)',
254
+ displayOptions: {
255
+ show: {
256
+ resource: ['page'],
257
+ operation: ['browserHttpRequest'],
258
+ },
259
+ },
260
+ },
261
+ {
262
+ displayName: 'Auto Start Profile',
263
+ name: 'autoStart',
264
+ type: 'boolean',
265
+ default: false,
266
+ description: 'Automatically start the profile if it is not running',
267
+ displayOptions: {
268
+ show: {
269
+ resource: ['page'],
270
+ operation: ['browserHttpRequest'],
271
+ },
272
+ },
273
+ },
274
+ ];
@@ -0,0 +1,5 @@
1
+ import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class BrowserHttpRequest implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }
@@ -0,0 +1,244 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.BrowserHttpRequest = void 0;
27
+ const BrowserManager_1 = require("../../../utils/BrowserManager");
28
+ const BrowserHttpRequest_description_1 = require("./BrowserHttpRequest.description");
29
+ const path = __importStar(require("path"));
30
+ class BrowserHttpRequest {
31
+ constructor() {
32
+ this.description = {
33
+ displayName: 'Browser HTTP Request',
34
+ name: 'browserHttpRequest',
35
+ icon: 'file:http.svg',
36
+ group: ['transform'],
37
+ version: 1,
38
+ description: 'Make HTTP requests using browser profile cookies and headers to bypass Cloudflare',
39
+ defaults: {
40
+ name: 'Browser HTTP Request',
41
+ },
42
+ inputs: ['main'],
43
+ outputs: ['main'],
44
+ properties: [
45
+ {
46
+ displayName: 'Resource',
47
+ name: 'resource',
48
+ type: 'options',
49
+ noDataExpression: true,
50
+ options: [
51
+ {
52
+ name: 'Page',
53
+ value: 'page',
54
+ },
55
+ ],
56
+ default: 'page',
57
+ },
58
+ {
59
+ displayName: 'Operation',
60
+ name: 'operation',
61
+ type: 'options',
62
+ noDataExpression: true,
63
+ options: [
64
+ {
65
+ name: 'Browser HTTP Request',
66
+ value: 'browserHttpRequest',
67
+ description: 'Make HTTP request with browser cookies and headers',
68
+ action: 'Make HTTP request',
69
+ },
70
+ ],
71
+ default: 'browserHttpRequest',
72
+ },
73
+ ...BrowserHttpRequest_description_1.browserHttpRequestFields,
74
+ ],
75
+ };
76
+ }
77
+ async execute() {
78
+ const items = this.getInputData();
79
+ const returnData = [];
80
+ const workspacePath = process.cwd();
81
+ const profilesDir = process.env.NVK_PROFILES_DIR || path.join(workspacePath, 'profiles');
82
+ const browserPath = process.env.NVK_BROWSER_PATH || path.join(workspacePath, 'browser-142', 'chrome.exe');
83
+ const resolvedBrowserPath = path.isAbsolute(browserPath)
84
+ ? browserPath
85
+ : path.resolve(workspacePath, browserPath);
86
+ const resolvedProfilesDir = path.isAbsolute(profilesDir)
87
+ ? profilesDir
88
+ : path.resolve(workspacePath, profilesDir);
89
+ const browserManager = BrowserManager_1.BrowserManager.getInstance(resolvedBrowserPath, resolvedProfilesDir);
90
+ for (let i = 0; i < items.length; i++) {
91
+ try {
92
+ const profileId = this.getNodeParameter('profileId', i);
93
+ const method = this.getNodeParameter('method', i) || 'GET';
94
+ const url = this.getNodeParameter('url', i);
95
+ const tabIndex = this.getNodeParameter('tabIndex', i) || 0;
96
+ const autoStart = this.getNodeParameter('autoStart', i) || false;
97
+ let instance = browserManager.getInstance(profileId);
98
+ // Auto start profile nếu chưa chạy và option được bật
99
+ if (!instance && autoStart) {
100
+ instance = await browserManager.startProfileIfNotRunning(profileId);
101
+ }
102
+ if (!instance) {
103
+ throw new Error(`Profile ${profileId} is not running. Enable "Auto Start Profile" to start it automatically.`);
104
+ }
105
+ const page = await browserManager.getPage(profileId, tabIndex);
106
+ if (!page) {
107
+ throw new Error(`Could not get page for profile ${profileId}`);
108
+ }
109
+ // Get cookies from browser
110
+ const cookies = await page.cookies();
111
+ const cookieString = cookies.map(cookie => `${cookie.name}=${cookie.value}`).join('; ');
112
+ // Get user agent from browser
113
+ const userAgent = await page.evaluate(() => navigator.userAgent);
114
+ // Get additional headers from browser context
115
+ const browserHeaders = {
116
+ 'User-Agent': userAgent,
117
+ 'Accept': '*/*',
118
+ 'Accept-Language': 'en-US,en;q=0.9',
119
+ 'Accept-Encoding': 'gzip, deflate, br',
120
+ 'Connection': 'keep-alive',
121
+ 'Sec-Fetch-Dest': 'empty',
122
+ 'Sec-Fetch-Mode': 'cors',
123
+ 'Sec-Fetch-Site': 'same-origin',
124
+ 'Cookie': cookieString,
125
+ };
126
+ // Get additional headers from node parameters
127
+ const additionalHeadersParam = this.getNodeParameter('additionalHeaders', i);
128
+ if (additionalHeadersParam?.header) {
129
+ additionalHeadersParam.header.forEach((header) => {
130
+ if (header.name && header.value) {
131
+ browserHeaders[header.name] = header.value;
132
+ }
133
+ });
134
+ }
135
+ // Build URL with query parameters
136
+ const queryParametersParam = this.getNodeParameter('queryParameters', i);
137
+ let finalUrl = url;
138
+ if (queryParametersParam?.parameter && queryParametersParam.parameter.length > 0) {
139
+ const urlObj = new URL(url);
140
+ queryParametersParam.parameter.forEach((param) => {
141
+ if (param.name && param.value) {
142
+ urlObj.searchParams.append(param.name, param.value);
143
+ }
144
+ });
145
+ finalUrl = urlObj.toString();
146
+ }
147
+ // Prepare request body for POST
148
+ let body;
149
+ let contentType;
150
+ if (method === 'POST') {
151
+ const sendBody = this.getNodeParameter('sendBody', i);
152
+ if (sendBody) {
153
+ const bodyContentType = this.getNodeParameter('bodyContentType', i);
154
+ if (bodyContentType === 'json') {
155
+ const jsonBody = this.getNodeParameter('jsonBody', i);
156
+ body = jsonBody;
157
+ contentType = 'application/json';
158
+ }
159
+ else if (bodyContentType === 'raw') {
160
+ const rawBody = this.getNodeParameter('rawBody', i);
161
+ body = rawBody;
162
+ contentType = 'text/plain';
163
+ }
164
+ else if (bodyContentType === 'formData') {
165
+ const formDataParam = this.getNodeParameter('formData', i);
166
+ if (formDataParam?.parameter) {
167
+ const formData = new URLSearchParams();
168
+ formDataParam.parameter.forEach((param) => {
169
+ if (param.name && param.value) {
170
+ formData.append(param.name, param.value);
171
+ }
172
+ });
173
+ body = formData.toString();
174
+ contentType = 'application/x-www-form-urlencoded';
175
+ }
176
+ }
177
+ }
178
+ }
179
+ // Set Content-Type header if body is provided
180
+ if (contentType) {
181
+ browserHeaders['Content-Type'] = contentType;
182
+ }
183
+ // Make HTTP request using page.evaluate to use browser's fetch with all cookies and headers
184
+ const response = await page.evaluate(async (requestData) => {
185
+ const fetchOptions = {
186
+ method: requestData.method,
187
+ headers: requestData.headers,
188
+ };
189
+ if (requestData.body) {
190
+ fetchOptions.body = requestData.body;
191
+ }
192
+ const response = await fetch(requestData.url, fetchOptions);
193
+ // Get response headers
194
+ const responseHeaders = {};
195
+ response.headers.forEach((value, key) => {
196
+ responseHeaders[key] = value;
197
+ });
198
+ // Get response body
199
+ let responseBody;
200
+ const contentType = response.headers.get('content-type') || '';
201
+ if (contentType.includes('application/json')) {
202
+ responseBody = await response.json();
203
+ }
204
+ else {
205
+ responseBody = await response.text();
206
+ }
207
+ return {
208
+ status: response.status,
209
+ statusText: response.statusText,
210
+ headers: responseHeaders,
211
+ body: responseBody,
212
+ url: response.url,
213
+ };
214
+ }, {
215
+ url: finalUrl,
216
+ method,
217
+ headers: browserHeaders,
218
+ body,
219
+ });
220
+ returnData.push({
221
+ json: {
222
+ success: true,
223
+ statusCode: response.status,
224
+ statusMessage: response.statusText,
225
+ headers: response.headers,
226
+ body: response.body,
227
+ url: response.url,
228
+ message: 'Request completed successfully',
229
+ },
230
+ });
231
+ }
232
+ catch (error) {
233
+ returnData.push({
234
+ json: {
235
+ success: false,
236
+ error: error instanceof Error ? error.message : String(error),
237
+ },
238
+ });
239
+ }
240
+ }
241
+ return [returnData];
242
+ }
243
+ }
244
+ exports.BrowserHttpRequest = BrowserHttpRequest;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-nvk-browser",
3
- "version": "1.0.11",
3
+ "version": "1.0.12",
4
4
  "description": "n8n nodes for managing Chrome browser profiles and page interactions",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",
@@ -40,7 +40,8 @@
40
40
  "dist/nodes/ProfileManagement/StopProfile/StopProfile.node.js",
41
41
  "dist/nodes/PageInteraction/MoveAndClick/MoveAndClick.node.js",
42
42
  "dist/nodes/PageInteraction/RunJavaScript/RunJavaScript.node.js",
43
- "dist/nodes/PageInteraction/GetNetworkResponse/GetNetworkResponse.node.js"
43
+ "dist/nodes/PageInteraction/GetNetworkResponse/GetNetworkResponse.node.js",
44
+ "dist/nodes/PageInteraction/BrowserHttpRequest/BrowserHttpRequest.node.js"
44
45
  ]
45
46
  },
46
47
  "devDependencies": {