n8n-nodes-nvk-browser 1.0.78 → 1.0.80
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/dist/nodes/ProfileManagement/CreateProfile/CreateProfile.node.js +77 -12
- package/dist/utils/BrowserManager.js +15 -32
- package/dist/utils/ExtensionHandler.d.ts +42 -0
- package/dist/utils/ExtensionHandler.js +196 -0
- package/dist/utils/ProfileManager.d.ts +9 -0
- package/dist/utils/ProfileManager.js +101 -0
- package/package.json +1 -1
|
@@ -95,29 +95,76 @@ class CreateProfile {
|
|
|
95
95
|
const extensionsInput = this.getNodeParameter('extensions', i) || '';
|
|
96
96
|
// Parse extensions
|
|
97
97
|
const extensions = ExtensionHandler_1.ExtensionHandler.parseExtensions(extensionsInput);
|
|
98
|
-
//
|
|
99
|
-
const
|
|
98
|
+
// Tạo profile trước (để có profilePath)
|
|
99
|
+
const profile = profileManager.createProfile(profileName, proxy, note, extensions, [] // extensionPaths sẽ được set sau
|
|
100
|
+
);
|
|
101
|
+
const profilePath = profileManager.getProfilePath(profile.id);
|
|
102
|
+
// Validate và install extensions vào profile
|
|
103
|
+
const extensionInfos = [];
|
|
100
104
|
if (extensions.length > 0) {
|
|
101
|
-
console.log(`[CreateProfile]
|
|
105
|
+
console.log(`[CreateProfile] ==========================================`);
|
|
106
|
+
console.log(`[CreateProfile] Validating and installing ${extensions.length} extension(s)...`);
|
|
107
|
+
console.log(`[CreateProfile] Profile path: ${profilePath}`);
|
|
108
|
+
console.log(`[CreateProfile] ==========================================`);
|
|
102
109
|
for (const ext of extensions) {
|
|
103
110
|
try {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
111
|
+
console.log(`[CreateProfile] Processing extension: ${ext}`);
|
|
112
|
+
// Validate extension và lấy info
|
|
113
|
+
const validation = await ExtensionHandler_1.ExtensionHandler.validateAndGetExtensionInfo(ext, resolvedProfilesDir);
|
|
114
|
+
console.log(`[CreateProfile] Validation result:`, {
|
|
115
|
+
valid: validation.valid,
|
|
116
|
+
path: validation.path,
|
|
117
|
+
extensionId: validation.extensionId,
|
|
118
|
+
version: validation.version,
|
|
119
|
+
error: validation.error,
|
|
120
|
+
});
|
|
121
|
+
if (validation.valid && validation.path && validation.extensionId) {
|
|
122
|
+
// Install extension vào profile Extensions directory
|
|
123
|
+
console.log(`[CreateProfile] Installing extension to profile...`);
|
|
124
|
+
const extensionInfo = await ExtensionHandler_1.ExtensionHandler.installExtensionToProfile(validation.path, profilePath, ext);
|
|
125
|
+
extensionInfos.push({
|
|
126
|
+
extensionId: extensionInfo.extensionId,
|
|
127
|
+
version: extensionInfo.version,
|
|
128
|
+
path: extensionInfo.path,
|
|
129
|
+
manifest: extensionInfo.manifest,
|
|
130
|
+
});
|
|
131
|
+
console.log(`[CreateProfile] ✓ Extension installed successfully: ${ext} -> ${extensionInfo.extensionId} v${extensionInfo.version}`);
|
|
132
|
+
console.log(`[CreateProfile] Installed at: ${extensionInfo.path}`);
|
|
108
133
|
}
|
|
109
134
|
else {
|
|
110
|
-
console.error(`[CreateProfile] ✗ Extension validation failed: ${ext}
|
|
135
|
+
console.error(`[CreateProfile] ✗ Extension validation failed: ${ext}`);
|
|
136
|
+
console.error(`[CreateProfile] Error: ${validation.error || 'Unknown error'}`);
|
|
137
|
+
console.error(`[CreateProfile] Valid: ${validation.valid}, Path: ${validation.path}, ExtensionId: ${validation.extensionId}`);
|
|
111
138
|
}
|
|
112
139
|
}
|
|
113
140
|
catch (error) {
|
|
114
|
-
console.error(`[CreateProfile] ✗
|
|
141
|
+
console.error(`[CreateProfile] ✗ ERROR installing extension "${ext}":`, error);
|
|
142
|
+
console.error(`[CreateProfile] Stack: ${error instanceof Error ? error.stack : 'N/A'}`);
|
|
115
143
|
}
|
|
116
144
|
}
|
|
117
|
-
console.log(`[CreateProfile]
|
|
145
|
+
console.log(`[CreateProfile] ==========================================`);
|
|
146
|
+
console.log(`[CreateProfile] Installation summary: ${extensionInfos.length}/${extensions.length} extension(s) installed`);
|
|
147
|
+
console.log(`[CreateProfile] ==========================================`);
|
|
148
|
+
// Lưu extension configuration vào Preferences
|
|
149
|
+
if (extensionInfos.length > 0) {
|
|
150
|
+
try {
|
|
151
|
+
console.log(`[CreateProfile] Saving extension configurations to Preferences...`);
|
|
152
|
+
profileManager.saveExtensionsToPreferences(profilePath, extensionInfos);
|
|
153
|
+
console.log(`[CreateProfile] ✓ Extension configurations saved to Preferences`);
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
console.error(`[CreateProfile] ✗ ERROR saving extension configurations:`, error);
|
|
157
|
+
console.error(`[CreateProfile] Stack: ${error instanceof Error ? error.stack : 'N/A'}`);
|
|
158
|
+
// Không throw - chỉ log error để user biết
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
console.warn(`[CreateProfile] ⚠ No extensions were installed successfully. Check logs above for errors.`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
console.log(`[CreateProfile] No extensions to install`);
|
|
118
167
|
}
|
|
119
|
-
// Tạo profile với cả extensionPaths đã resolve
|
|
120
|
-
const profile = profileManager.createProfile(profileName, proxy, note, extensions, extensionPaths);
|
|
121
168
|
// Lưu proxy authentication vào profile nếu có
|
|
122
169
|
// QUAN TRỌNG: Phải gọi SAU khi createProfile và setProfileName để Preferences file đã được tạo
|
|
123
170
|
// Nhưng phải đảm bảo không bị ghi đè
|
|
@@ -192,6 +239,22 @@ class CreateProfile {
|
|
|
192
239
|
else {
|
|
193
240
|
console.log(`[CreateProfile] No proxy provided`);
|
|
194
241
|
}
|
|
242
|
+
// Kiểm tra xem extensions đã được install chưa
|
|
243
|
+
const extensionsDir = path.join(profilePath, 'Default', 'Extensions');
|
|
244
|
+
const hasExtensionsDir = fs.existsSync(extensionsDir);
|
|
245
|
+
let installedExtensionsCount = 0;
|
|
246
|
+
if (hasExtensionsDir) {
|
|
247
|
+
try {
|
|
248
|
+
const extensionDirs = fs.readdirSync(extensionsDir).filter((dir) => {
|
|
249
|
+
const dirPath = path.join(extensionsDir, dir);
|
|
250
|
+
return fs.statSync(dirPath).isDirectory();
|
|
251
|
+
});
|
|
252
|
+
installedExtensionsCount = extensionDirs.length;
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
// Ignore
|
|
256
|
+
}
|
|
257
|
+
}
|
|
195
258
|
returnData.push({
|
|
196
259
|
json: {
|
|
197
260
|
success: true,
|
|
@@ -203,6 +266,8 @@ class CreateProfile {
|
|
|
203
266
|
extensions: profile.extensions,
|
|
204
267
|
extensionPaths: profile.extensionPaths,
|
|
205
268
|
createdAt: profile.createdAt,
|
|
269
|
+
installedExtensionsCount: installedExtensionsCount,
|
|
270
|
+
extensionsDirectoryExists: hasExtensionsDir,
|
|
206
271
|
},
|
|
207
272
|
},
|
|
208
273
|
});
|
|
@@ -31,7 +31,6 @@ const path = __importStar(require("path"));
|
|
|
31
31
|
const fs = __importStar(require("fs"));
|
|
32
32
|
const puppeteer_core_1 = __importDefault(require("puppeteer-core"));
|
|
33
33
|
const ProxyHandler_1 = require("./ProxyHandler");
|
|
34
|
-
const ExtensionHandler_1 = require("./ExtensionHandler");
|
|
35
34
|
const ProfileManager_1 = require("./ProfileManager");
|
|
36
35
|
class BrowserManager {
|
|
37
36
|
constructor(browserPath, profilesDir) {
|
|
@@ -91,43 +90,27 @@ class BrowserManager {
|
|
|
91
90
|
'--disable-dev-shm-usage',
|
|
92
91
|
'--no-sandbox',
|
|
93
92
|
'--disable-setuid-sandbox',
|
|
93
|
+
'--enable-extensions',
|
|
94
|
+
'--extensions-on-chrome-urls',
|
|
95
|
+
'--disable-features=ExtensionsMenuInAppMenu', // Đảm bảo extensions menu hoạt động
|
|
94
96
|
];
|
|
95
97
|
// Proxy configuration - KHÔNG bao gồm credentials trong URL
|
|
96
98
|
if (proxyConfig) {
|
|
97
99
|
args.push(...ProxyHandler_1.ProxyHandler.getChromeProxyArgs(proxyConfig));
|
|
98
100
|
}
|
|
99
101
|
// Extension configuration
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
if (validPaths.length > 0) {
|
|
114
|
-
args.push(`--load-extension=${validPaths.join(',')}`);
|
|
115
|
-
console.log(`[BrowserManager] Loading ${validPaths.length} extension(s) from cached paths`);
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
console.warn(`[BrowserManager] No valid extension paths found, skipping extensions`);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
else if (profile.extensions && profile.extensions.length > 0) {
|
|
122
|
-
// Fallback: Nếu không có extensionPaths, validate lại (cho backward compatibility)
|
|
123
|
-
console.log(`[BrowserManager] No extensionPaths found, validating extensions on-the-fly (legacy mode)`);
|
|
124
|
-
try {
|
|
125
|
-
const extensionArgs = await ExtensionHandler_1.ExtensionHandler.getExtensionArgs(profile.extensions, this.profilesDir);
|
|
126
|
-
args.push(...extensionArgs);
|
|
127
|
-
}
|
|
128
|
-
catch (error) {
|
|
129
|
-
console.error(`[BrowserManager] Error loading extensions:`, error);
|
|
130
|
-
// Continue without extensions rather than failing completely
|
|
102
|
+
// Extensions đã được install vào profile/Default/Extensions/ nên không cần --load-extension
|
|
103
|
+
// Chrome sẽ tự động load từ Preferences và Extensions directory
|
|
104
|
+
const extensionsDir = path.join(profilePath, 'Default', 'Extensions');
|
|
105
|
+
if (fs.existsSync(extensionsDir)) {
|
|
106
|
+
const extensionDirs = fs.readdirSync(extensionsDir).filter((dir) => {
|
|
107
|
+
const dirPath = path.join(extensionsDir, dir);
|
|
108
|
+
return fs.statSync(dirPath).isDirectory();
|
|
109
|
+
});
|
|
110
|
+
if (extensionDirs.length > 0) {
|
|
111
|
+
console.log(`[BrowserManager] Found ${extensionDirs.length} extension(s) installed in profile Extensions directory`);
|
|
112
|
+
// Extensions sẽ được load tự động từ Preferences và Extensions directory
|
|
113
|
+
// Không cần --load-extension vì đã có trong profile
|
|
131
114
|
}
|
|
132
115
|
}
|
|
133
116
|
// Window configuration
|
|
@@ -3,6 +3,14 @@ interface ExtensionValidationResult {
|
|
|
3
3
|
path?: string;
|
|
4
4
|
error?: string;
|
|
5
5
|
type?: 'store-id' | 'url' | 'local-path';
|
|
6
|
+
extensionId?: string;
|
|
7
|
+
version?: string;
|
|
8
|
+
}
|
|
9
|
+
interface ExtensionInfo {
|
|
10
|
+
extensionId: string;
|
|
11
|
+
version: string;
|
|
12
|
+
path: string;
|
|
13
|
+
manifest: any;
|
|
6
14
|
}
|
|
7
15
|
export declare class ExtensionHandler {
|
|
8
16
|
private static readonly CACHE_DIR_NAME;
|
|
@@ -62,6 +70,40 @@ export declare class ExtensionHandler {
|
|
|
62
70
|
* Download và extract extension từ URL
|
|
63
71
|
*/
|
|
64
72
|
private static downloadAndExtractExtensionFromUrl;
|
|
73
|
+
/**
|
|
74
|
+
* Đọc và parse manifest.json từ extension path
|
|
75
|
+
*/
|
|
76
|
+
private static readManifest;
|
|
77
|
+
/**
|
|
78
|
+
* Lấy extension ID từ manifest.json
|
|
79
|
+
* Nếu là Chrome Store ID thì dùng ID đó, nếu không thì generate từ manifest key
|
|
80
|
+
*/
|
|
81
|
+
private static getExtensionId;
|
|
82
|
+
/**
|
|
83
|
+
* Lấy version từ manifest.json
|
|
84
|
+
*/
|
|
85
|
+
private static getExtensionVersion;
|
|
86
|
+
/**
|
|
87
|
+
* Copy extension vào profile Extensions directory
|
|
88
|
+
* Structure: {profile}/Default/Extensions/{extension-id}/{version}/
|
|
89
|
+
*/
|
|
90
|
+
static installExtensionToProfile(extensionPath: string, profilePath: string, originalExtensionInput?: string): Promise<ExtensionInfo>;
|
|
91
|
+
/**
|
|
92
|
+
* Copy directory recursively
|
|
93
|
+
*/
|
|
94
|
+
private static copyDirectory;
|
|
95
|
+
/**
|
|
96
|
+
* Tạo verified_contents.json structure
|
|
97
|
+
*/
|
|
98
|
+
private static createVerifiedContents;
|
|
99
|
+
/**
|
|
100
|
+
* Validate extension và lấy thông tin chi tiết
|
|
101
|
+
*/
|
|
102
|
+
static validateAndGetExtensionInfo(extension: string, workspacePath?: string): Promise<ExtensionValidationResult & {
|
|
103
|
+
extensionId?: string;
|
|
104
|
+
version?: string;
|
|
105
|
+
manifest?: any;
|
|
106
|
+
}>;
|
|
65
107
|
/**
|
|
66
108
|
* Get Chrome arguments để load extensions
|
|
67
109
|
*/
|
|
@@ -509,6 +509,202 @@ class ExtensionHandler {
|
|
|
509
509
|
throw error;
|
|
510
510
|
}
|
|
511
511
|
}
|
|
512
|
+
/**
|
|
513
|
+
* Đọc và parse manifest.json từ extension path
|
|
514
|
+
*/
|
|
515
|
+
static readManifest(extensionPath) {
|
|
516
|
+
const manifestPath = path.join(extensionPath, 'manifest.json');
|
|
517
|
+
if (!fs.existsSync(manifestPath)) {
|
|
518
|
+
throw new Error('manifest.json not found');
|
|
519
|
+
}
|
|
520
|
+
const content = fs.readFileSync(manifestPath, 'utf-8');
|
|
521
|
+
return JSON.parse(content);
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Lấy extension ID từ manifest.json
|
|
525
|
+
* Nếu là Chrome Store ID thì dùng ID đó, nếu không thì generate từ manifest key
|
|
526
|
+
*/
|
|
527
|
+
static getExtensionId(manifest, originalExtensionInput) {
|
|
528
|
+
// Nếu input là Chrome Store ID (32 chars), dùng nó
|
|
529
|
+
if (originalExtensionInput && /^[a-z0-9]{32}$/i.test(originalExtensionInput)) {
|
|
530
|
+
return originalExtensionInput.toLowerCase();
|
|
531
|
+
}
|
|
532
|
+
// Nếu manifest có key (public key), generate ID từ key theo cách Chrome làm
|
|
533
|
+
if (manifest.key) {
|
|
534
|
+
try {
|
|
535
|
+
// Chrome extension ID được generate từ public key PEM
|
|
536
|
+
// Format: Base32 encoding của first 128 bits (16 bytes) của SHA256(public key)
|
|
537
|
+
let keyData = manifest.key;
|
|
538
|
+
let keyString = '';
|
|
539
|
+
// Nếu key là string (PEM format hoặc base64)
|
|
540
|
+
if (typeof keyData === 'string') {
|
|
541
|
+
keyString = keyData;
|
|
542
|
+
}
|
|
543
|
+
else if (Array.isArray(keyData) || Buffer.isBuffer(keyData)) {
|
|
544
|
+
keyString = Buffer.from(keyData).toString('base64');
|
|
545
|
+
}
|
|
546
|
+
if (keyString) {
|
|
547
|
+
// Parse PEM nếu có
|
|
548
|
+
if (keyString.includes('-----BEGIN')) {
|
|
549
|
+
// Extract base64 content từ PEM
|
|
550
|
+
const pemMatch = keyString.match(/-----BEGIN[^-]+-----([\s\S]*)-----END[^-]+-----/);
|
|
551
|
+
if (pemMatch) {
|
|
552
|
+
keyString = pemMatch[1].replace(/\s/g, '');
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
// SHA256 hash của public key
|
|
556
|
+
const hash = crypto.createHash('sha256').update(keyString).digest();
|
|
557
|
+
// Base32 encode first 16 bytes (128 bits)
|
|
558
|
+
const base32Chars = 'abcdefghijklmnopqrstuvwxyz234567';
|
|
559
|
+
let extensionId = '';
|
|
560
|
+
let bits = 0;
|
|
561
|
+
let value = 0;
|
|
562
|
+
for (let i = 0; i < 16; i++) {
|
|
563
|
+
value = (value << 8) | hash[i];
|
|
564
|
+
bits += 8;
|
|
565
|
+
while (bits >= 5) {
|
|
566
|
+
extensionId += base32Chars[(value >>> (bits - 5)) & 31];
|
|
567
|
+
bits -= 5;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
if (bits > 0) {
|
|
571
|
+
extensionId += base32Chars[(value << (5 - bits)) & 31];
|
|
572
|
+
}
|
|
573
|
+
// Chrome extension ID luôn là 32 ký tự
|
|
574
|
+
return extensionId.substring(0, 32).toLowerCase();
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
catch (error) {
|
|
578
|
+
console.warn(`[ExtensionHandler] Could not generate ID from key:`, error);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
// Fallback: Generate ID từ manifest (name + path hash) - luôn tạo 32 ký tự
|
|
582
|
+
const name = manifest.name || 'extension';
|
|
583
|
+
const version = manifest.version || '1.0.0';
|
|
584
|
+
// Dùng SHA256 và lấy 32 ký tự đầu tiên (hex)
|
|
585
|
+
const hash = crypto
|
|
586
|
+
.createHash('sha256')
|
|
587
|
+
.update(`${name}${version}${Date.now()}`)
|
|
588
|
+
.digest('hex')
|
|
589
|
+
.substring(0, 32);
|
|
590
|
+
return hash;
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Lấy version từ manifest.json
|
|
594
|
+
*/
|
|
595
|
+
static getExtensionVersion(manifest) {
|
|
596
|
+
return manifest.version || '1.0.0';
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Copy extension vào profile Extensions directory
|
|
600
|
+
* Structure: {profile}/Default/Extensions/{extension-id}/{version}/
|
|
601
|
+
*/
|
|
602
|
+
static async installExtensionToProfile(extensionPath, profilePath, originalExtensionInput) {
|
|
603
|
+
console.log(`[ExtensionHandler] Installing extension to profile: ${extensionPath} -> ${profilePath}`);
|
|
604
|
+
// Đọc manifest
|
|
605
|
+
const manifest = this.readManifest(extensionPath);
|
|
606
|
+
const extensionId = this.getExtensionId(manifest, originalExtensionInput);
|
|
607
|
+
const version = this.getExtensionVersion(manifest);
|
|
608
|
+
// Tạo Extensions directory structure
|
|
609
|
+
const extensionsDir = path.join(profilePath, 'Default', 'Extensions');
|
|
610
|
+
if (!fs.existsSync(extensionsDir)) {
|
|
611
|
+
fs.mkdirSync(extensionsDir, { recursive: true });
|
|
612
|
+
}
|
|
613
|
+
const extensionIdDir = path.join(extensionsDir, extensionId);
|
|
614
|
+
if (!fs.existsSync(extensionIdDir)) {
|
|
615
|
+
fs.mkdirSync(extensionIdDir, { recursive: true });
|
|
616
|
+
}
|
|
617
|
+
// Version directory (format: {version}_0)
|
|
618
|
+
const versionDir = path.join(extensionIdDir, `${version}_0`);
|
|
619
|
+
if (fs.existsSync(versionDir)) {
|
|
620
|
+
// Nếu đã tồn tại, xóa và copy lại
|
|
621
|
+
console.log(`[ExtensionHandler] Removing existing version directory: ${versionDir}`);
|
|
622
|
+
fs.rmSync(versionDir, { recursive: true, force: true });
|
|
623
|
+
}
|
|
624
|
+
fs.mkdirSync(versionDir, { recursive: true });
|
|
625
|
+
console.log(`[ExtensionHandler] Created version directory: ${versionDir}`);
|
|
626
|
+
// Copy tất cả files từ extensionPath vào versionDir
|
|
627
|
+
console.log(`[ExtensionHandler] Copying files from ${extensionPath} to ${versionDir}...`);
|
|
628
|
+
this.copyDirectory(extensionPath, versionDir);
|
|
629
|
+
console.log(`[ExtensionHandler] Files copied successfully`);
|
|
630
|
+
// Tạo _metadata directory và verified_contents.json
|
|
631
|
+
const metadataDir = path.join(versionDir, '_metadata');
|
|
632
|
+
fs.mkdirSync(metadataDir, { recursive: true });
|
|
633
|
+
// Tạo verified_contents.json (simplified version)
|
|
634
|
+
const verifiedContents = this.createVerifiedContents(extensionId, version, versionDir);
|
|
635
|
+
const verifiedContentsPath = path.join(metadataDir, 'verified_contents.json');
|
|
636
|
+
fs.writeFileSync(verifiedContentsPath, JSON.stringify(verifiedContents, null, 2), 'utf-8');
|
|
637
|
+
console.log(`[ExtensionHandler] ✓ Extension installed: ${extensionId} v${version} -> ${versionDir}`);
|
|
638
|
+
return {
|
|
639
|
+
extensionId,
|
|
640
|
+
version,
|
|
641
|
+
path: versionDir,
|
|
642
|
+
manifest,
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Copy directory recursively
|
|
647
|
+
*/
|
|
648
|
+
static copyDirectory(src, dest) {
|
|
649
|
+
if (!fs.existsSync(src)) {
|
|
650
|
+
throw new Error(`Source directory does not exist: ${src}`);
|
|
651
|
+
}
|
|
652
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
653
|
+
for (const entry of entries) {
|
|
654
|
+
const srcPath = path.join(src, entry.name);
|
|
655
|
+
const destPath = path.join(dest, entry.name);
|
|
656
|
+
if (entry.isDirectory()) {
|
|
657
|
+
// Skip _metadata if copying from another profile
|
|
658
|
+
if (entry.name === '_metadata' && fs.existsSync(destPath)) {
|
|
659
|
+
continue;
|
|
660
|
+
}
|
|
661
|
+
fs.mkdirSync(destPath, { recursive: true });
|
|
662
|
+
this.copyDirectory(srcPath, destPath);
|
|
663
|
+
}
|
|
664
|
+
else {
|
|
665
|
+
fs.copyFileSync(srcPath, destPath);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Tạo verified_contents.json structure
|
|
671
|
+
*/
|
|
672
|
+
static createVerifiedContents(extensionId, version, extensionPath) {
|
|
673
|
+
// Simplified verified_contents - Chrome sẽ tự verify khi cần
|
|
674
|
+
// Đây là format cơ bản, Chrome có thể tự generate lại khi cần
|
|
675
|
+
return [
|
|
676
|
+
{
|
|
677
|
+
description: 'treehash per file',
|
|
678
|
+
signed_content: {
|
|
679
|
+
payload: '',
|
|
680
|
+
signatures: [],
|
|
681
|
+
},
|
|
682
|
+
},
|
|
683
|
+
];
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Validate extension và lấy thông tin chi tiết
|
|
687
|
+
*/
|
|
688
|
+
static async validateAndGetExtensionInfo(extension, workspacePath) {
|
|
689
|
+
const validation = await this.validateExtension(extension, workspacePath);
|
|
690
|
+
if (validation.valid && validation.path) {
|
|
691
|
+
try {
|
|
692
|
+
const manifest = this.readManifest(validation.path);
|
|
693
|
+
const extensionId = this.getExtensionId(manifest, extension);
|
|
694
|
+
const version = this.getExtensionVersion(manifest);
|
|
695
|
+
return {
|
|
696
|
+
...validation,
|
|
697
|
+
extensionId,
|
|
698
|
+
version,
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
catch (error) {
|
|
702
|
+
console.warn(`[ExtensionHandler] Could not read extension info:`, error);
|
|
703
|
+
return validation;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
return validation;
|
|
707
|
+
}
|
|
512
708
|
/**
|
|
513
709
|
* Get Chrome arguments để load extensions
|
|
514
710
|
*/
|
|
@@ -29,4 +29,13 @@ export declare class ProfileManager {
|
|
|
29
29
|
* Đảm bảo file tồn tại và có cấu trúc gologin đầy đủ
|
|
30
30
|
*/
|
|
31
31
|
private initializePreferencesFile;
|
|
32
|
+
/**
|
|
33
|
+
* Lưu extension configuration vào Preferences file
|
|
34
|
+
*/
|
|
35
|
+
saveExtensionsToPreferences(profilePath: string, extensions: Array<{
|
|
36
|
+
extensionId: string;
|
|
37
|
+
version: string;
|
|
38
|
+
path: string;
|
|
39
|
+
manifest?: any;
|
|
40
|
+
}>): void;
|
|
32
41
|
}
|
|
@@ -636,6 +636,21 @@ class ProfileManager {
|
|
|
636
636
|
prefs.gologin.webgl_noise_value = "5.669";
|
|
637
637
|
}
|
|
638
638
|
}
|
|
639
|
+
// Đảm bảo extensions structure tồn tại ngay từ đầu (để Developer mode được bật)
|
|
640
|
+
if (!prefs.extensions) {
|
|
641
|
+
prefs.extensions = {};
|
|
642
|
+
}
|
|
643
|
+
if (!prefs.extensions.preferences) {
|
|
644
|
+
prefs.extensions.preferences = {};
|
|
645
|
+
}
|
|
646
|
+
if (!prefs.extensions.preferences.extensions) {
|
|
647
|
+
prefs.extensions.preferences.extensions = {};
|
|
648
|
+
}
|
|
649
|
+
if (!prefs.extensions.preferences.extensions.ui) {
|
|
650
|
+
prefs.extensions.preferences.extensions.ui = {};
|
|
651
|
+
}
|
|
652
|
+
// Bật Developer mode ngay từ đầu
|
|
653
|
+
prefs.extensions.preferences.extensions.ui.developer_mode = true;
|
|
639
654
|
// Ghi file
|
|
640
655
|
fs.writeFileSync(prefsPath, JSON.stringify(prefs, null, 2), 'utf-8');
|
|
641
656
|
}
|
|
@@ -643,5 +658,91 @@ class ProfileManager {
|
|
|
643
658
|
// Ignore errors - initialization is optional
|
|
644
659
|
}
|
|
645
660
|
}
|
|
661
|
+
/**
|
|
662
|
+
* Lưu extension configuration vào Preferences file
|
|
663
|
+
*/
|
|
664
|
+
saveExtensionsToPreferences(profilePath, extensions) {
|
|
665
|
+
try {
|
|
666
|
+
const prefsPath = path.join(profilePath, 'Default', 'Preferences');
|
|
667
|
+
let prefs = {};
|
|
668
|
+
if (fs.existsSync(prefsPath)) {
|
|
669
|
+
try {
|
|
670
|
+
const prefsContent = fs.readFileSync(prefsPath, 'utf-8');
|
|
671
|
+
prefs = JSON.parse(prefsContent);
|
|
672
|
+
}
|
|
673
|
+
catch (error) {
|
|
674
|
+
console.error(`[ProfileManager] Error reading Preferences:`, error);
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
else {
|
|
679
|
+
console.warn(`[ProfileManager] Preferences file does not exist, creating new one`);
|
|
680
|
+
}
|
|
681
|
+
// Khởi tạo extensions structure
|
|
682
|
+
if (!prefs.extensions) {
|
|
683
|
+
prefs.extensions = {};
|
|
684
|
+
}
|
|
685
|
+
if (!prefs.extensions.settings) {
|
|
686
|
+
prefs.extensions.settings = {};
|
|
687
|
+
}
|
|
688
|
+
if (!prefs.extensions.preferences) {
|
|
689
|
+
prefs.extensions.preferences = {};
|
|
690
|
+
}
|
|
691
|
+
// Lưu từng extension
|
|
692
|
+
const extensionIds = [];
|
|
693
|
+
for (const ext of extensions) {
|
|
694
|
+
const extensionId = ext.extensionId;
|
|
695
|
+
extensionIds.push(extensionId);
|
|
696
|
+
// Đọc manifest nếu chưa có
|
|
697
|
+
let manifest = ext.manifest;
|
|
698
|
+
if (!manifest) {
|
|
699
|
+
try {
|
|
700
|
+
const manifestPath = path.join(ext.path, 'manifest.json');
|
|
701
|
+
if (fs.existsSync(manifestPath)) {
|
|
702
|
+
const manifestContent = fs.readFileSync(manifestPath, 'utf-8');
|
|
703
|
+
manifest = JSON.parse(manifestContent);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
catch (error) {
|
|
707
|
+
console.warn(`[ProfileManager] Could not read manifest for ${extensionId}:`, error);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
// Tính relative path từ Default directory
|
|
711
|
+
const relativePath = path.relative(path.join(profilePath, 'Default'), ext.path).replace(/\\/g, '/');
|
|
712
|
+
// Cấu hình extension settings
|
|
713
|
+
prefs.extensions.settings[extensionId] = {
|
|
714
|
+
location: 1,
|
|
715
|
+
creation_flags: 1,
|
|
716
|
+
path: relativePath,
|
|
717
|
+
state: 1,
|
|
718
|
+
incognito: 0,
|
|
719
|
+
manifest: manifest || {},
|
|
720
|
+
granted_permissions: manifest?.permissions || [],
|
|
721
|
+
withheld_permissions: [],
|
|
722
|
+
disable_reasons: 0,
|
|
723
|
+
installation_timestamp: Date.now(),
|
|
724
|
+
update_url: '',
|
|
725
|
+
};
|
|
726
|
+
console.log(`[ProfileManager] ✓ Configured extension ${extensionId} in Preferences`);
|
|
727
|
+
}
|
|
728
|
+
// Lưu danh sách extension IDs và bật Developer mode
|
|
729
|
+
prefs.extensions.preferences = {
|
|
730
|
+
...prefs.extensions.preferences,
|
|
731
|
+
extensions: {
|
|
732
|
+
...(prefs.extensions.preferences.extensions || {}),
|
|
733
|
+
ui: {
|
|
734
|
+
developer_mode: true, // Bật Developer mode để cho phép cài extension thủ công
|
|
735
|
+
},
|
|
736
|
+
},
|
|
737
|
+
};
|
|
738
|
+
// Lưu lại Preferences
|
|
739
|
+
fs.writeFileSync(prefsPath, JSON.stringify(prefs, null, 2), 'utf-8');
|
|
740
|
+
console.log(`[ProfileManager] ✓ Saved ${extensionIds.length} extension(s) to Preferences`);
|
|
741
|
+
}
|
|
742
|
+
catch (error) {
|
|
743
|
+
console.error(`[ProfileManager] Error saving extensions to Preferences:`, error);
|
|
744
|
+
throw error;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
646
747
|
}
|
|
647
748
|
exports.ProfileManager = ProfileManager;
|
package/package.json
CHANGED