n8n-nodes-nvk-browser 1.0.78 → 1.0.79
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 +30 -11
- package/dist/utils/BrowserManager.js +13 -32
- package/dist/utils/ExtensionHandler.d.ts +42 -0
- package/dist/utils/ExtensionHandler.js +192 -0
- package/dist/utils/ProfileManager.d.ts +9 -0
- package/dist/utils/ProfileManager.js +83 -0
- package/package.json +1 -1
|
@@ -95,29 +95,48 @@ 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] Validating ${extensions.length} extension(s)...`);
|
|
105
|
+
console.log(`[CreateProfile] Validating and installing ${extensions.length} extension(s)...`);
|
|
102
106
|
for (const ext of extensions) {
|
|
103
107
|
try {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
+
// Validate extension và lấy info
|
|
109
|
+
const validation = await ExtensionHandler_1.ExtensionHandler.validateAndGetExtensionInfo(ext, resolvedProfilesDir);
|
|
110
|
+
if (validation.valid && validation.path && validation.extensionId) {
|
|
111
|
+
// Install extension vào profile Extensions directory
|
|
112
|
+
const extensionInfo = await ExtensionHandler_1.ExtensionHandler.installExtensionToProfile(validation.path, profilePath, ext);
|
|
113
|
+
extensionInfos.push({
|
|
114
|
+
extensionId: extensionInfo.extensionId,
|
|
115
|
+
version: extensionInfo.version,
|
|
116
|
+
path: extensionInfo.path,
|
|
117
|
+
});
|
|
118
|
+
console.log(`[CreateProfile] ✓ Extension installed: ${ext} -> ${extensionInfo.extensionId} v${extensionInfo.version}`);
|
|
108
119
|
}
|
|
109
120
|
else {
|
|
110
121
|
console.error(`[CreateProfile] ✗ Extension validation failed: ${ext} - ${validation.error || 'Unknown error'}`);
|
|
111
122
|
}
|
|
112
123
|
}
|
|
113
124
|
catch (error) {
|
|
114
|
-
console.error(`[CreateProfile] ✗ Error
|
|
125
|
+
console.error(`[CreateProfile] ✗ Error installing extension "${ext}":`, error);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
console.log(`[CreateProfile] Successfully installed ${extensionInfos.length}/${extensions.length} extension(s)`);
|
|
129
|
+
// Lưu extension configuration vào Preferences
|
|
130
|
+
if (extensionInfos.length > 0) {
|
|
131
|
+
try {
|
|
132
|
+
profileManager.saveExtensionsToPreferences(profilePath, extensionInfos);
|
|
133
|
+
console.log(`[CreateProfile] ✓ Extension configurations saved to Preferences`);
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
console.error(`[CreateProfile] ✗ Error saving extension configurations:`, error);
|
|
115
137
|
}
|
|
116
138
|
}
|
|
117
|
-
console.log(`[CreateProfile] Successfully validated ${extensionPaths.length}/${extensions.length} extension(s)`);
|
|
118
139
|
}
|
|
119
|
-
// Tạo profile với cả extensionPaths đã resolve
|
|
120
|
-
const profile = profileManager.createProfile(profileName, proxy, note, extensions, extensionPaths);
|
|
121
140
|
// Lưu proxy authentication vào profile nếu có
|
|
122
141
|
// QUAN TRỌNG: Phải gọi SAU khi createProfile và setProfileName để Preferences file đã được tạo
|
|
123
142
|
// Nhưng phải đảm bảo không bị ghi đè
|
|
@@ -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,25 @@ class BrowserManager {
|
|
|
91
90
|
'--disable-dev-shm-usage',
|
|
92
91
|
'--no-sandbox',
|
|
93
92
|
'--disable-setuid-sandbox',
|
|
93
|
+
'--enable-extensions', // Cho phép extensions
|
|
94
94
|
];
|
|
95
95
|
// Proxy configuration - KHÔNG bao gồm credentials trong URL
|
|
96
96
|
if (proxyConfig) {
|
|
97
97
|
args.push(...ProxyHandler_1.ProxyHandler.getChromeProxyArgs(proxyConfig));
|
|
98
98
|
}
|
|
99
99
|
// 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
|
|
100
|
+
// Extensions đã được install vào profile/Default/Extensions/ nên không cần --load-extension
|
|
101
|
+
// Chrome sẽ tự động load từ Preferences và Extensions directory
|
|
102
|
+
const extensionsDir = path.join(profilePath, 'Default', 'Extensions');
|
|
103
|
+
if (fs.existsSync(extensionsDir)) {
|
|
104
|
+
const extensionDirs = fs.readdirSync(extensionsDir).filter((dir) => {
|
|
105
|
+
const dirPath = path.join(extensionsDir, dir);
|
|
106
|
+
return fs.statSync(dirPath).isDirectory();
|
|
107
|
+
});
|
|
108
|
+
if (extensionDirs.length > 0) {
|
|
109
|
+
console.log(`[BrowserManager] Found ${extensionDirs.length} extension(s) installed in profile Extensions directory`);
|
|
110
|
+
// Extensions sẽ được load tự động từ Preferences và Extensions directory
|
|
111
|
+
// Không cần --load-extension vì đã có trong profile
|
|
131
112
|
}
|
|
132
113
|
}
|
|
133
114
|
// 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,198 @@ 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
|
+
fs.rmSync(versionDir, { recursive: true, force: true });
|
|
622
|
+
}
|
|
623
|
+
fs.mkdirSync(versionDir, { recursive: true });
|
|
624
|
+
// Copy tất cả files từ extensionPath vào versionDir
|
|
625
|
+
this.copyDirectory(extensionPath, versionDir);
|
|
626
|
+
// Tạo _metadata directory và verified_contents.json
|
|
627
|
+
const metadataDir = path.join(versionDir, '_metadata');
|
|
628
|
+
fs.mkdirSync(metadataDir, { recursive: true });
|
|
629
|
+
// Tạo verified_contents.json (simplified version)
|
|
630
|
+
const verifiedContents = this.createVerifiedContents(extensionId, version, versionDir);
|
|
631
|
+
const verifiedContentsPath = path.join(metadataDir, 'verified_contents.json');
|
|
632
|
+
fs.writeFileSync(verifiedContentsPath, JSON.stringify(verifiedContents, null, 2), 'utf-8');
|
|
633
|
+
console.log(`[ExtensionHandler] ✓ Extension installed: ${extensionId} v${version} -> ${versionDir}`);
|
|
634
|
+
return {
|
|
635
|
+
extensionId,
|
|
636
|
+
version,
|
|
637
|
+
path: versionDir,
|
|
638
|
+
manifest,
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Copy directory recursively
|
|
643
|
+
*/
|
|
644
|
+
static copyDirectory(src, dest) {
|
|
645
|
+
if (!fs.existsSync(src)) {
|
|
646
|
+
throw new Error(`Source directory does not exist: ${src}`);
|
|
647
|
+
}
|
|
648
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
649
|
+
for (const entry of entries) {
|
|
650
|
+
const srcPath = path.join(src, entry.name);
|
|
651
|
+
const destPath = path.join(dest, entry.name);
|
|
652
|
+
if (entry.isDirectory()) {
|
|
653
|
+
// Skip _metadata if copying from another profile
|
|
654
|
+
if (entry.name === '_metadata' && fs.existsSync(destPath)) {
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
fs.mkdirSync(destPath, { recursive: true });
|
|
658
|
+
this.copyDirectory(srcPath, destPath);
|
|
659
|
+
}
|
|
660
|
+
else {
|
|
661
|
+
fs.copyFileSync(srcPath, destPath);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Tạo verified_contents.json structure
|
|
667
|
+
*/
|
|
668
|
+
static createVerifiedContents(extensionId, version, extensionPath) {
|
|
669
|
+
// Simplified verified_contents - Chrome sẽ tự verify khi cần
|
|
670
|
+
// Đây là format cơ bản, Chrome có thể tự generate lại khi cần
|
|
671
|
+
return [
|
|
672
|
+
{
|
|
673
|
+
description: 'treehash per file',
|
|
674
|
+
signed_content: {
|
|
675
|
+
payload: '',
|
|
676
|
+
signatures: [],
|
|
677
|
+
},
|
|
678
|
+
},
|
|
679
|
+
];
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Validate extension và lấy thông tin chi tiết
|
|
683
|
+
*/
|
|
684
|
+
static async validateAndGetExtensionInfo(extension, workspacePath) {
|
|
685
|
+
const validation = await this.validateExtension(extension, workspacePath);
|
|
686
|
+
if (validation.valid && validation.path) {
|
|
687
|
+
try {
|
|
688
|
+
const manifest = this.readManifest(validation.path);
|
|
689
|
+
const extensionId = this.getExtensionId(manifest, extension);
|
|
690
|
+
const version = this.getExtensionVersion(manifest);
|
|
691
|
+
return {
|
|
692
|
+
...validation,
|
|
693
|
+
extensionId,
|
|
694
|
+
version,
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
catch (error) {
|
|
698
|
+
console.warn(`[ExtensionHandler] Could not read extension info:`, error);
|
|
699
|
+
return validation;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
return validation;
|
|
703
|
+
}
|
|
512
704
|
/**
|
|
513
705
|
* Get Chrome arguments để load extensions
|
|
514
706
|
*/
|
|
@@ -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
|
}
|
|
@@ -643,5 +643,88 @@ class ProfileManager {
|
|
|
643
643
|
// Ignore errors - initialization is optional
|
|
644
644
|
}
|
|
645
645
|
}
|
|
646
|
+
/**
|
|
647
|
+
* Lưu extension configuration vào Preferences file
|
|
648
|
+
*/
|
|
649
|
+
saveExtensionsToPreferences(profilePath, extensions) {
|
|
650
|
+
try {
|
|
651
|
+
const prefsPath = path.join(profilePath, 'Default', 'Preferences');
|
|
652
|
+
let prefs = {};
|
|
653
|
+
if (fs.existsSync(prefsPath)) {
|
|
654
|
+
try {
|
|
655
|
+
const prefsContent = fs.readFileSync(prefsPath, 'utf-8');
|
|
656
|
+
prefs = JSON.parse(prefsContent);
|
|
657
|
+
}
|
|
658
|
+
catch (error) {
|
|
659
|
+
console.error(`[ProfileManager] Error reading Preferences:`, error);
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
// Khởi tạo extensions structure
|
|
664
|
+
if (!prefs.extensions) {
|
|
665
|
+
prefs.extensions = {};
|
|
666
|
+
}
|
|
667
|
+
if (!prefs.extensions.settings) {
|
|
668
|
+
prefs.extensions.settings = {};
|
|
669
|
+
}
|
|
670
|
+
if (!prefs.extensions.preferences) {
|
|
671
|
+
prefs.extensions.preferences = {};
|
|
672
|
+
}
|
|
673
|
+
// Lưu từng extension
|
|
674
|
+
const extensionIds = [];
|
|
675
|
+
for (const ext of extensions) {
|
|
676
|
+
const extensionId = ext.extensionId;
|
|
677
|
+
extensionIds.push(extensionId);
|
|
678
|
+
// Đọc manifest nếu chưa có
|
|
679
|
+
let manifest = ext.manifest;
|
|
680
|
+
if (!manifest) {
|
|
681
|
+
try {
|
|
682
|
+
const manifestPath = path.join(ext.path, 'manifest.json');
|
|
683
|
+
if (fs.existsSync(manifestPath)) {
|
|
684
|
+
const manifestContent = fs.readFileSync(manifestPath, 'utf-8');
|
|
685
|
+
manifest = JSON.parse(manifestContent);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
catch (error) {
|
|
689
|
+
console.warn(`[ProfileManager] Could not read manifest for ${extensionId}:`, error);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
// Tính relative path từ Default directory
|
|
693
|
+
const relativePath = path.relative(path.join(profilePath, 'Default'), ext.path).replace(/\\/g, '/');
|
|
694
|
+
// Cấu hình extension settings
|
|
695
|
+
prefs.extensions.settings[extensionId] = {
|
|
696
|
+
location: 1,
|
|
697
|
+
creation_flags: 1,
|
|
698
|
+
path: relativePath,
|
|
699
|
+
state: 1,
|
|
700
|
+
incognito: 0,
|
|
701
|
+
manifest: manifest || {},
|
|
702
|
+
granted_permissions: manifest?.permissions || [],
|
|
703
|
+
withheld_permissions: [],
|
|
704
|
+
disable_reasons: 0,
|
|
705
|
+
installation_timestamp: Date.now(),
|
|
706
|
+
update_url: '',
|
|
707
|
+
};
|
|
708
|
+
console.log(`[ProfileManager] ✓ Configured extension ${extensionId} in Preferences`);
|
|
709
|
+
}
|
|
710
|
+
// Lưu danh sách extension IDs
|
|
711
|
+
prefs.extensions.preferences = {
|
|
712
|
+
...prefs.extensions.preferences,
|
|
713
|
+
extensions: {
|
|
714
|
+
...(prefs.extensions.preferences.extensions || {}),
|
|
715
|
+
ui: {
|
|
716
|
+
developer_mode: false,
|
|
717
|
+
},
|
|
718
|
+
},
|
|
719
|
+
};
|
|
720
|
+
// Lưu lại Preferences
|
|
721
|
+
fs.writeFileSync(prefsPath, JSON.stringify(prefs, null, 2), 'utf-8');
|
|
722
|
+
console.log(`[ProfileManager] ✓ Saved ${extensionIds.length} extension(s) to Preferences`);
|
|
723
|
+
}
|
|
724
|
+
catch (error) {
|
|
725
|
+
console.error(`[ProfileManager] Error saving extensions to Preferences:`, error);
|
|
726
|
+
throw error;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
646
729
|
}
|
|
647
730
|
exports.ProfileManager = ProfileManager;
|
package/package.json
CHANGED