n8n-nodes-nvk-browser 1.0.0
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.
- package/LICENSE +22 -0
- package/README.md +76 -0
- package/dist/nodes/PageInteraction/MoveAndClick/MoveAndClick.description.d.ts +2 -0
- package/dist/nodes/PageInteraction/MoveAndClick/MoveAndClick.description.js +132 -0
- package/dist/nodes/PageInteraction/MoveAndClick/MoveAndClick.node.d.ts +5 -0
- package/dist/nodes/PageInteraction/MoveAndClick/MoveAndClick.node.js +176 -0
- package/dist/nodes/PageInteraction/MoveAndClick/click.svg +5 -0
- package/dist/nodes/PageInteraction/MoveAndClick/code.svg +5 -0
- package/dist/nodes/PageInteraction/MoveAndClick/profile.svg +5 -0
- package/dist/nodes/PageInteraction/RunJavaScript/RunJavaScript.description.d.ts +2 -0
- package/dist/nodes/PageInteraction/RunJavaScript/RunJavaScript.description.js +62 -0
- package/dist/nodes/PageInteraction/RunJavaScript/RunJavaScript.node.d.ts +5 -0
- package/dist/nodes/PageInteraction/RunJavaScript/RunJavaScript.node.js +126 -0
- package/dist/nodes/PageInteraction/RunJavaScript/click.svg +5 -0
- package/dist/nodes/PageInteraction/RunJavaScript/code.svg +5 -0
- package/dist/nodes/PageInteraction/RunJavaScript/profile.svg +5 -0
- package/dist/nodes/ProfileManagement/CreateProfile/CreateProfile.description.d.ts +2 -0
- package/dist/nodes/ProfileManagement/CreateProfile/CreateProfile.description.js +61 -0
- package/dist/nodes/ProfileManagement/CreateProfile/CreateProfile.node.d.ts +5 -0
- package/dist/nodes/ProfileManagement/CreateProfile/CreateProfile.node.js +124 -0
- package/dist/nodes/ProfileManagement/CreateProfile/click.svg +5 -0
- package/dist/nodes/ProfileManagement/CreateProfile/code.svg +5 -0
- package/dist/nodes/ProfileManagement/CreateProfile/profile.svg +5 -0
- package/dist/nodes/ProfileManagement/DeleteProfile/DeleteProfile.description.d.ts +2 -0
- package/dist/nodes/ProfileManagement/DeleteProfile/DeleteProfile.description.js +19 -0
- package/dist/nodes/ProfileManagement/DeleteProfile/DeleteProfile.node.d.ts +5 -0
- package/dist/nodes/ProfileManagement/DeleteProfile/DeleteProfile.node.js +123 -0
- package/dist/nodes/ProfileManagement/DeleteProfile/click.svg +5 -0
- package/dist/nodes/ProfileManagement/DeleteProfile/code.svg +5 -0
- package/dist/nodes/ProfileManagement/DeleteProfile/profile.svg +5 -0
- package/dist/nodes/ProfileManagement/StartProfile/StartProfile.description.d.ts +2 -0
- package/dist/nodes/ProfileManagement/StartProfile/StartProfile.description.js +122 -0
- package/dist/nodes/ProfileManagement/StartProfile/StartProfile.node.d.ts +5 -0
- package/dist/nodes/ProfileManagement/StartProfile/StartProfile.node.js +140 -0
- package/dist/nodes/ProfileManagement/StartProfile/click.svg +5 -0
- package/dist/nodes/ProfileManagement/StartProfile/code.svg +5 -0
- package/dist/nodes/ProfileManagement/StartProfile/profile.svg +5 -0
- package/dist/nodes/ProfileManagement/StopProfile/StopProfile.description.d.ts +2 -0
- package/dist/nodes/ProfileManagement/StopProfile/StopProfile.description.js +19 -0
- package/dist/nodes/ProfileManagement/StopProfile/StopProfile.node.d.ts +5 -0
- package/dist/nodes/ProfileManagement/StopProfile/StopProfile.node.js +115 -0
- package/dist/nodes/ProfileManagement/StopProfile/click.svg +5 -0
- package/dist/nodes/ProfileManagement/StopProfile/code.svg +5 -0
- package/dist/nodes/ProfileManagement/StopProfile/profile.svg +5 -0
- package/dist/utils/BrowserManager.d.ts +18 -0
- package/dist/utils/BrowserManager.js +174 -0
- package/dist/utils/ExtensionHandler.d.ts +9 -0
- package/dist/utils/ExtensionHandler.js +86 -0
- package/dist/utils/ProfileManager.d.ts +14 -0
- package/dist/utils/ProfileManager.js +128 -0
- package/dist/utils/ProxyHandler.d.ts +5 -0
- package/dist/utils/ProxyHandler.js +50 -0
- package/dist/utils/types.d.ts +47 -0
- package/dist/utils/types.js +2 -0
- package/package.json +66 -0
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
2
|
+
<polyline points="16 18 22 12 16 6"></polyline>
|
|
3
|
+
<polyline points="8 6 2 12 8 18"></polyline>
|
|
4
|
+
</svg>
|
|
5
|
+
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
2
|
+
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
|
3
|
+
<circle cx="12" cy="7" r="4"></circle>
|
|
4
|
+
</svg>
|
|
5
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.stopProfileFields = void 0;
|
|
4
|
+
exports.stopProfileFields = [
|
|
5
|
+
{
|
|
6
|
+
displayName: 'Profile ID',
|
|
7
|
+
name: 'profileId',
|
|
8
|
+
type: 'string',
|
|
9
|
+
required: true,
|
|
10
|
+
default: '',
|
|
11
|
+
description: 'ID of the profile to stop',
|
|
12
|
+
displayOptions: {
|
|
13
|
+
show: {
|
|
14
|
+
resource: ['profile'],
|
|
15
|
+
operation: ['stop'],
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
];
|
|
@@ -0,0 +1,115 @@
|
|
|
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.StopProfile = void 0;
|
|
27
|
+
const BrowserManager_1 = require("../../../utils/BrowserManager");
|
|
28
|
+
const StopProfile_description_1 = require("./StopProfile.description");
|
|
29
|
+
const path = __importStar(require("path"));
|
|
30
|
+
class StopProfile {
|
|
31
|
+
constructor() {
|
|
32
|
+
this.description = {
|
|
33
|
+
displayName: 'Stop Profile',
|
|
34
|
+
name: 'stopProfile',
|
|
35
|
+
icon: 'file:profile.svg',
|
|
36
|
+
group: ['transform'],
|
|
37
|
+
version: 1,
|
|
38
|
+
description: 'Stop a running browser profile',
|
|
39
|
+
defaults: {
|
|
40
|
+
name: 'Stop Profile',
|
|
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: 'Profile',
|
|
53
|
+
value: 'profile',
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
default: 'profile',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
displayName: 'Operation',
|
|
60
|
+
name: 'operation',
|
|
61
|
+
type: 'options',
|
|
62
|
+
noDataExpression: true,
|
|
63
|
+
options: [
|
|
64
|
+
{
|
|
65
|
+
name: 'Stop',
|
|
66
|
+
value: 'stop',
|
|
67
|
+
description: 'Stop a profile',
|
|
68
|
+
action: 'Stop a profile',
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
default: 'stop',
|
|
72
|
+
},
|
|
73
|
+
...StopProfile_description_1.stopProfileFields,
|
|
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
|
+
BrowserManager_1.BrowserManager.resetInstance(); // Reset để đảm bảo dùng đúng paths
|
|
90
|
+
const browserManager = BrowserManager_1.BrowserManager.getInstance(resolvedBrowserPath, resolvedProfilesDir);
|
|
91
|
+
for (let i = 0; i < items.length; i++) {
|
|
92
|
+
try {
|
|
93
|
+
const profileId = this.getNodeParameter('profileId', i);
|
|
94
|
+
const stopped = await browserManager.stopProfile(profileId);
|
|
95
|
+
returnData.push({
|
|
96
|
+
json: {
|
|
97
|
+
success: stopped,
|
|
98
|
+
profileId,
|
|
99
|
+
message: stopped ? 'Profile stopped successfully' : 'Profile not running',
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
returnData.push({
|
|
105
|
+
json: {
|
|
106
|
+
success: false,
|
|
107
|
+
error: error instanceof Error ? error.message : String(error),
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return [returnData];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
exports.StopProfile = StopProfile;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
2
|
+
<path d="M9 9l5 12 1.774-5.226L21 14 9 9z"></path>
|
|
3
|
+
<path d="M21 3l-3.5 3.5"></path>
|
|
4
|
+
</svg>
|
|
5
|
+
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
2
|
+
<polyline points="16 18 22 12 16 6"></polyline>
|
|
3
|
+
<polyline points="8 6 2 12 8 18"></polyline>
|
|
4
|
+
</svg>
|
|
5
|
+
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
2
|
+
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
|
3
|
+
<circle cx="12" cy="7" r="4"></circle>
|
|
4
|
+
</svg>
|
|
5
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Page } from 'puppeteer-core';
|
|
2
|
+
import { BrowserInstance, WindowConfig } from './types';
|
|
3
|
+
export declare class BrowserManager {
|
|
4
|
+
private static instance;
|
|
5
|
+
private instances;
|
|
6
|
+
private browserPath;
|
|
7
|
+
private profilesManager;
|
|
8
|
+
private constructor();
|
|
9
|
+
static getInstance(browserPath?: string, profilesDir?: string): BrowserManager;
|
|
10
|
+
static resetInstance(): void;
|
|
11
|
+
startProfile(profileId: string, windowConfig?: WindowConfig): Promise<BrowserInstance>;
|
|
12
|
+
stopProfile(profileId: string): Promise<boolean>;
|
|
13
|
+
getInstance(profileId: string): BrowserInstance | undefined;
|
|
14
|
+
getAllInstances(): BrowserInstance[];
|
|
15
|
+
private getDebugPort;
|
|
16
|
+
private setWindowPosition;
|
|
17
|
+
getPage(profileId: string, tabIndex?: number): Promise<Page | null>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.BrowserManager = void 0;
|
|
7
|
+
const puppeteer_core_1 = __importDefault(require("puppeteer-core"));
|
|
8
|
+
const ProxyHandler_1 = require("./ProxyHandler");
|
|
9
|
+
const ExtensionHandler_1 = require("./ExtensionHandler");
|
|
10
|
+
const ProfileManager_1 = require("./ProfileManager");
|
|
11
|
+
class BrowserManager {
|
|
12
|
+
constructor(browserPath, profilesDir) {
|
|
13
|
+
this.instances = new Map();
|
|
14
|
+
this.browserPath = browserPath;
|
|
15
|
+
this.profilesManager = new ProfileManager_1.ProfileManager(profilesDir);
|
|
16
|
+
}
|
|
17
|
+
static getInstance(browserPath, profilesDir) {
|
|
18
|
+
if (!BrowserManager.instance) {
|
|
19
|
+
if (!browserPath || !profilesDir) {
|
|
20
|
+
throw new Error('BrowserManager must be initialized with browserPath and profilesDir');
|
|
21
|
+
}
|
|
22
|
+
BrowserManager.instance = new BrowserManager(browserPath, profilesDir);
|
|
23
|
+
}
|
|
24
|
+
return BrowserManager.instance;
|
|
25
|
+
}
|
|
26
|
+
static resetInstance() {
|
|
27
|
+
BrowserManager.instance = null;
|
|
28
|
+
}
|
|
29
|
+
async startProfile(profileId, windowConfig) {
|
|
30
|
+
// Kiểm tra xem profile đã đang chạy chưa
|
|
31
|
+
if (this.instances.has(profileId)) {
|
|
32
|
+
throw new Error(`Profile ${profileId} is already running`);
|
|
33
|
+
}
|
|
34
|
+
// Lấy thông tin profile
|
|
35
|
+
const profile = this.profilesManager.getProfile(profileId);
|
|
36
|
+
if (!profile) {
|
|
37
|
+
throw new Error(`Profile ${profileId} not found`);
|
|
38
|
+
}
|
|
39
|
+
const profilePath = this.profilesManager.getProfilePath(profileId);
|
|
40
|
+
// Chuẩn bị Chrome arguments
|
|
41
|
+
const args = [
|
|
42
|
+
`--user-data-dir=${profilePath}`,
|
|
43
|
+
'--remote-debugging-port=0',
|
|
44
|
+
'--disable-blink-features=AutomationControlled',
|
|
45
|
+
'--disable-dev-shm-usage',
|
|
46
|
+
'--no-sandbox',
|
|
47
|
+
'--disable-setuid-sandbox',
|
|
48
|
+
];
|
|
49
|
+
// Proxy configuration
|
|
50
|
+
if (profile.proxy) {
|
|
51
|
+
const proxyConfig = ProxyHandler_1.ProxyHandler.parseProxyString(profile.proxy);
|
|
52
|
+
if (proxyConfig) {
|
|
53
|
+
args.push(...ProxyHandler_1.ProxyHandler.getChromeProxyArgs(proxyConfig));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Extension configuration
|
|
57
|
+
if (profile.extensions && profile.extensions.length > 0) {
|
|
58
|
+
args.push(...ExtensionHandler_1.ExtensionHandler.getExtensionArgs(profile.extensions));
|
|
59
|
+
}
|
|
60
|
+
// Window configuration
|
|
61
|
+
if (windowConfig) {
|
|
62
|
+
if (windowConfig.scale) {
|
|
63
|
+
args.push(`--force-device-scale-factor=${windowConfig.scale}`);
|
|
64
|
+
}
|
|
65
|
+
if (windowConfig.headless) {
|
|
66
|
+
args.push('--headless=new');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Khởi động Chrome
|
|
70
|
+
const browser = await puppeteer_core_1.default.launch({
|
|
71
|
+
executablePath: this.browserPath,
|
|
72
|
+
args,
|
|
73
|
+
headless: windowConfig?.headless || false,
|
|
74
|
+
defaultViewport: windowConfig?.size
|
|
75
|
+
? {
|
|
76
|
+
width: windowConfig.size.width,
|
|
77
|
+
height: windowConfig.size.height,
|
|
78
|
+
}
|
|
79
|
+
: null,
|
|
80
|
+
});
|
|
81
|
+
// Lấy debug port từ browser
|
|
82
|
+
const targets = browser.targets();
|
|
83
|
+
const debugPort = await this.getDebugPort(browser);
|
|
84
|
+
// Tạo instance
|
|
85
|
+
const instance = {
|
|
86
|
+
profileId,
|
|
87
|
+
process: browser.process(),
|
|
88
|
+
browser,
|
|
89
|
+
pages: new Map(),
|
|
90
|
+
debugPort,
|
|
91
|
+
};
|
|
92
|
+
// Lưu instance
|
|
93
|
+
this.instances.set(profileId, instance);
|
|
94
|
+
// Set window position nếu có
|
|
95
|
+
if (windowConfig?.position && !windowConfig.headless) {
|
|
96
|
+
await this.setWindowPosition(browser, windowConfig.position);
|
|
97
|
+
}
|
|
98
|
+
return instance;
|
|
99
|
+
}
|
|
100
|
+
async stopProfile(profileId) {
|
|
101
|
+
const instance = this.instances.get(profileId);
|
|
102
|
+
if (!instance) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
await instance.browser.close();
|
|
107
|
+
this.instances.delete(profileId);
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
this.instances.delete(profileId);
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
getInstance(profileId) {
|
|
116
|
+
return this.instances.get(profileId);
|
|
117
|
+
}
|
|
118
|
+
getAllInstances() {
|
|
119
|
+
return Array.from(this.instances.values());
|
|
120
|
+
}
|
|
121
|
+
async getDebugPort(browser) {
|
|
122
|
+
try {
|
|
123
|
+
const endpoint = browser.wsEndpoint();
|
|
124
|
+
const match = endpoint.match(/localhost:(\d+)/);
|
|
125
|
+
if (match) {
|
|
126
|
+
return parseInt(match[1], 10);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
// Ignore
|
|
131
|
+
}
|
|
132
|
+
return 0;
|
|
133
|
+
}
|
|
134
|
+
async setWindowPosition(browser, position) {
|
|
135
|
+
try {
|
|
136
|
+
// Window position được set thông qua Chrome DevTools Protocol
|
|
137
|
+
const pages = await browser.pages();
|
|
138
|
+
if (pages.length > 0) {
|
|
139
|
+
const target = pages[0].target();
|
|
140
|
+
const client = await target.createCDPSession();
|
|
141
|
+
// Note: Window position setting via CDP may require additional setup
|
|
142
|
+
// For now, we'll rely on Chrome's default behavior
|
|
143
|
+
// The position parameter is stored but not actively used
|
|
144
|
+
await client.detach();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
// Ignore if not supported - window position is optional
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async getPage(profileId, tabIndex = 0) {
|
|
152
|
+
const instance = this.instances.get(profileId);
|
|
153
|
+
if (!instance) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
// Kiểm tra cache
|
|
157
|
+
if (instance.pages.has(tabIndex)) {
|
|
158
|
+
return instance.pages.get(tabIndex);
|
|
159
|
+
}
|
|
160
|
+
// Lấy pages từ browser
|
|
161
|
+
const pages = await instance.browser.pages();
|
|
162
|
+
if (pages.length > tabIndex) {
|
|
163
|
+
const page = pages[tabIndex];
|
|
164
|
+
instance.pages.set(tabIndex, page);
|
|
165
|
+
return page;
|
|
166
|
+
}
|
|
167
|
+
// Tạo page mới nếu không có
|
|
168
|
+
const newPage = await instance.browser.newPage();
|
|
169
|
+
instance.pages.set(tabIndex, newPage);
|
|
170
|
+
return newPage;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
BrowserManager.instance = null;
|
|
174
|
+
exports.BrowserManager = BrowserManager;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare class ExtensionHandler {
|
|
2
|
+
static parseExtensions(extensionInput: string): string[];
|
|
3
|
+
static validateExtension(extension: string): {
|
|
4
|
+
valid: boolean;
|
|
5
|
+
path?: string;
|
|
6
|
+
error?: string;
|
|
7
|
+
};
|
|
8
|
+
static getExtensionArgs(extensions: string[]): string[];
|
|
9
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
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.ExtensionHandler = void 0;
|
|
27
|
+
const fs = __importStar(require("fs"));
|
|
28
|
+
const path = __importStar(require("path"));
|
|
29
|
+
class ExtensionHandler {
|
|
30
|
+
static parseExtensions(extensionInput) {
|
|
31
|
+
if (!extensionInput || !extensionInput.trim()) {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
// Mỗi extension một dòng
|
|
35
|
+
const lines = extensionInput
|
|
36
|
+
.split('\n')
|
|
37
|
+
.map(line => line.trim())
|
|
38
|
+
.filter(line => line.length > 0);
|
|
39
|
+
return lines;
|
|
40
|
+
}
|
|
41
|
+
static validateExtension(extension) {
|
|
42
|
+
// Nếu là ID từ Chrome Store (thường là 32 ký tự alphanumeric)
|
|
43
|
+
if (/^[a-z]{32}$/i.test(extension)) {
|
|
44
|
+
return { valid: true };
|
|
45
|
+
}
|
|
46
|
+
// Nếu là đường dẫn local
|
|
47
|
+
if (path.isAbsolute(extension) || extension.startsWith('./') || extension.startsWith('../')) {
|
|
48
|
+
const resolvedPath = path.resolve(extension);
|
|
49
|
+
if (fs.existsSync(resolvedPath)) {
|
|
50
|
+
// Kiểm tra xem có phải là extension directory không
|
|
51
|
+
const manifestPath = path.join(resolvedPath, 'manifest.json');
|
|
52
|
+
if (fs.existsSync(manifestPath)) {
|
|
53
|
+
return { valid: true, path: resolvedPath };
|
|
54
|
+
}
|
|
55
|
+
return { valid: false, error: 'Extension directory does not contain manifest.json' };
|
|
56
|
+
}
|
|
57
|
+
return { valid: false, error: 'Extension path does not exist' };
|
|
58
|
+
}
|
|
59
|
+
// Nếu là relative path từ workspace
|
|
60
|
+
const workspacePath = path.resolve(__dirname, '..', extension);
|
|
61
|
+
if (fs.existsSync(workspacePath)) {
|
|
62
|
+
const manifestPath = path.join(workspacePath, 'manifest.json');
|
|
63
|
+
if (fs.existsSync(manifestPath)) {
|
|
64
|
+
return { valid: true, path: workspacePath };
|
|
65
|
+
}
|
|
66
|
+
return { valid: false, error: 'Extension directory does not contain manifest.json' };
|
|
67
|
+
}
|
|
68
|
+
return { valid: false, error: 'Invalid extension format. Must be Chrome Store ID or valid path' };
|
|
69
|
+
}
|
|
70
|
+
static getExtensionArgs(extensions) {
|
|
71
|
+
const args = [];
|
|
72
|
+
const validPaths = [];
|
|
73
|
+
for (const ext of extensions) {
|
|
74
|
+
const validation = this.validateExtension(ext);
|
|
75
|
+
if (validation.valid && validation.path) {
|
|
76
|
+
validPaths.push(validation.path);
|
|
77
|
+
}
|
|
78
|
+
// Chrome Store IDs sẽ được load sau khi browser khởi động
|
|
79
|
+
}
|
|
80
|
+
if (validPaths.length > 0) {
|
|
81
|
+
args.push(`--load-extension=${validPaths.join(',')}`);
|
|
82
|
+
}
|
|
83
|
+
return args;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
exports.ExtensionHandler = ExtensionHandler;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ProfileData } from './types';
|
|
2
|
+
export declare class ProfileManager {
|
|
3
|
+
private profilesDir;
|
|
4
|
+
constructor(profilesDir: string);
|
|
5
|
+
private ensureProfilesDir;
|
|
6
|
+
private getProfileMetadataPath;
|
|
7
|
+
private getProfileDir;
|
|
8
|
+
createProfile(name: string, proxy?: string, note?: string, extensions?: string[]): ProfileData;
|
|
9
|
+
getProfile(profileId: string): ProfileData | null;
|
|
10
|
+
getAllProfiles(): ProfileData[];
|
|
11
|
+
updateProfile(profileId: string, updates: Partial<ProfileData>): ProfileData | null;
|
|
12
|
+
deleteProfile(profileId: string): boolean;
|
|
13
|
+
getProfilePath(profileId: string): string;
|
|
14
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
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.ProfileManager = void 0;
|
|
27
|
+
const fs = __importStar(require("fs"));
|
|
28
|
+
const path = __importStar(require("path"));
|
|
29
|
+
const uuid_1 = require("uuid");
|
|
30
|
+
class ProfileManager {
|
|
31
|
+
constructor(profilesDir) {
|
|
32
|
+
this.profilesDir = profilesDir;
|
|
33
|
+
this.ensureProfilesDir();
|
|
34
|
+
}
|
|
35
|
+
ensureProfilesDir() {
|
|
36
|
+
if (!fs.existsSync(this.profilesDir)) {
|
|
37
|
+
fs.mkdirSync(this.profilesDir, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
getProfileMetadataPath(profileId) {
|
|
41
|
+
return path.join(this.profilesDir, profileId, 'profile.metadata.json');
|
|
42
|
+
}
|
|
43
|
+
getProfileDir(profileId) {
|
|
44
|
+
return path.join(this.profilesDir, profileId);
|
|
45
|
+
}
|
|
46
|
+
createProfile(name, proxy, note, extensions) {
|
|
47
|
+
const profileId = (0, uuid_1.v4)();
|
|
48
|
+
const profileDir = this.getProfileDir(profileId);
|
|
49
|
+
// Tạo thư mục profile
|
|
50
|
+
fs.mkdirSync(profileDir, { recursive: true });
|
|
51
|
+
const profileData = {
|
|
52
|
+
id: profileId,
|
|
53
|
+
name,
|
|
54
|
+
proxy,
|
|
55
|
+
note,
|
|
56
|
+
extensions: extensions || [],
|
|
57
|
+
createdAt: new Date().toISOString(),
|
|
58
|
+
updatedAt: new Date().toISOString(),
|
|
59
|
+
};
|
|
60
|
+
// Lưu metadata
|
|
61
|
+
const metadataPath = this.getProfileMetadataPath(profileId);
|
|
62
|
+
fs.writeFileSync(metadataPath, JSON.stringify(profileData, null, 2), 'utf-8');
|
|
63
|
+
return profileData;
|
|
64
|
+
}
|
|
65
|
+
getProfile(profileId) {
|
|
66
|
+
const metadataPath = this.getProfileMetadataPath(profileId);
|
|
67
|
+
if (!fs.existsSync(metadataPath)) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const content = fs.readFileSync(metadataPath, 'utf-8');
|
|
72
|
+
return JSON.parse(content);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
getAllProfiles() {
|
|
79
|
+
const profiles = [];
|
|
80
|
+
if (!fs.existsSync(this.profilesDir)) {
|
|
81
|
+
return profiles;
|
|
82
|
+
}
|
|
83
|
+
const dirs = fs.readdirSync(this.profilesDir);
|
|
84
|
+
for (const dir of dirs) {
|
|
85
|
+
const profilePath = path.join(this.profilesDir, dir);
|
|
86
|
+
if (fs.statSync(profilePath).isDirectory()) {
|
|
87
|
+
const profile = this.getProfile(dir);
|
|
88
|
+
if (profile) {
|
|
89
|
+
profiles.push(profile);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return profiles;
|
|
94
|
+
}
|
|
95
|
+
updateProfile(profileId, updates) {
|
|
96
|
+
const profile = this.getProfile(profileId);
|
|
97
|
+
if (!profile) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
const updatedProfile = {
|
|
101
|
+
...profile,
|
|
102
|
+
...updates,
|
|
103
|
+
id: profileId,
|
|
104
|
+
updatedAt: new Date().toISOString(),
|
|
105
|
+
};
|
|
106
|
+
const metadataPath = this.getProfileMetadataPath(profileId);
|
|
107
|
+
fs.writeFileSync(metadataPath, JSON.stringify(updatedProfile, null, 2), 'utf-8');
|
|
108
|
+
return updatedProfile;
|
|
109
|
+
}
|
|
110
|
+
deleteProfile(profileId) {
|
|
111
|
+
const profileDir = this.getProfileDir(profileId);
|
|
112
|
+
if (!fs.existsSync(profileDir)) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
// Xóa toàn bộ thư mục profile
|
|
117
|
+
fs.rmSync(profileDir, { recursive: true, force: true });
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
getProfilePath(profileId) {
|
|
125
|
+
return this.getProfileDir(profileId);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
exports.ProfileManager = ProfileManager;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ProxyHandler = void 0;
|
|
4
|
+
class ProxyHandler {
|
|
5
|
+
static parseProxyString(proxyString) {
|
|
6
|
+
if (!proxyString || !proxyString.trim()) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
// Format: http:127.0.0.1:8080:user:pass hoặc socks5:127.0.0.1:1080:user:pass
|
|
10
|
+
const parts = proxyString.split(':');
|
|
11
|
+
if (parts.length < 3) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
const type = parts[0].toLowerCase();
|
|
15
|
+
if (type !== 'http' && type !== 'socks5') {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const host = parts[1];
|
|
19
|
+
const port = parseInt(parts[2], 10);
|
|
20
|
+
if (isNaN(port)) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const config = {
|
|
24
|
+
type,
|
|
25
|
+
host,
|
|
26
|
+
port,
|
|
27
|
+
};
|
|
28
|
+
if (parts.length >= 4) {
|
|
29
|
+
config.username = parts[3];
|
|
30
|
+
}
|
|
31
|
+
if (parts.length >= 5) {
|
|
32
|
+
config.password = parts[4];
|
|
33
|
+
}
|
|
34
|
+
return config;
|
|
35
|
+
}
|
|
36
|
+
static getChromeProxyArgs(proxyConfig) {
|
|
37
|
+
if (!proxyConfig) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
const args = [];
|
|
41
|
+
if (proxyConfig.type === 'http') {
|
|
42
|
+
args.push(`--proxy-server=http://${proxyConfig.host}:${proxyConfig.port}`);
|
|
43
|
+
}
|
|
44
|
+
else if (proxyConfig.type === 'socks5') {
|
|
45
|
+
args.push(`--proxy-server=socks5://${proxyConfig.host}:${proxyConfig.port}`);
|
|
46
|
+
}
|
|
47
|
+
return args;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.ProxyHandler = ProxyHandler;
|