gologin 1.0.52 → 1.0.56
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/browser-user-data-manager.js +86 -23
- package/common.js +23 -0
- package/extensions-manager.js +119 -99
- package/gologin.js +28 -8
- package/package.json +1 -1
- package/user-extensions-manager.js +408 -0
|
@@ -2,7 +2,7 @@ const path = require('path');
|
|
|
2
2
|
const os = require('os');
|
|
3
3
|
const request = require('requestretry');
|
|
4
4
|
const { rmdirSync, createWriteStream } = require('fs');
|
|
5
|
-
const { access, readFile, writeFile, mkdir, readdir, copyFile } = require('fs').promises;
|
|
5
|
+
const { access, readFile, writeFile, mkdir, readdir, copyFile, rename } = require('fs').promises;
|
|
6
6
|
const crypto = require('crypto');
|
|
7
7
|
|
|
8
8
|
const fontsCollection = require('./fonts');
|
|
@@ -14,6 +14,9 @@ const HOMEDIR = os.homedir();
|
|
|
14
14
|
const BROWSER_PATH = path.join(HOMEDIR, '.gologin', 'browser');
|
|
15
15
|
const OS_PLATFORM = process.platform;
|
|
16
16
|
const DEFAULT_ORBITA_EXTENSIONS_NAMES = ['Google Hangouts', 'Chromium PDF Viewer', 'CryptoTokenExtension', 'Web Store'];
|
|
17
|
+
const GOLOGIN_BASE_FOLDER_NAME = '.gologin';
|
|
18
|
+
const GOLOGIN_TEST_FOLDER_NAME = '.gologin_test';
|
|
19
|
+
const osPlatform = process.platform;
|
|
17
20
|
|
|
18
21
|
class BrowserUserDataManager {
|
|
19
22
|
static downloadCookies({ profileId, ACCESS_TOKEN, API_BASE_URL }) {
|
|
@@ -119,7 +122,7 @@ class BrowserUserDataManager {
|
|
|
119
122
|
await writeFile(path.join(defaultFolderPath, 'fonts_config'), result);
|
|
120
123
|
}
|
|
121
124
|
|
|
122
|
-
static setExtPathsAndRemoveDeleted(settings = {}, profileExtensionsCheckRes = []) {
|
|
125
|
+
static setExtPathsAndRemoveDeleted(settings = {}, profileExtensionsCheckRes = [], profileId = '') {
|
|
123
126
|
const formattedLocalExtArray = profileExtensionsCheckRes.map((el) => {
|
|
124
127
|
const [extFolderName = ''] = el.split(path.sep).reverse();
|
|
125
128
|
const [originalId] = extFolderName.split('@');
|
|
@@ -130,54 +133,54 @@ class BrowserUserDataManager {
|
|
|
130
133
|
return {
|
|
131
134
|
path: el,
|
|
132
135
|
originalId,
|
|
133
|
-
}
|
|
136
|
+
};
|
|
134
137
|
}).filter(Boolean);
|
|
135
138
|
|
|
136
139
|
const extensionsSettings = settings.extensions?.settings || {};
|
|
137
140
|
const extensionsEntries = Object.entries(extensionsSettings);
|
|
138
141
|
|
|
139
|
-
extensionsEntries.
|
|
142
|
+
const promises = extensionsEntries.map(async (extensionObj) => {
|
|
140
143
|
let [extensionId, currentExtSettings = {}] = extensionObj;
|
|
141
144
|
const extName = currentExtSettings.manifest?.name || '';
|
|
142
145
|
let extPath = currentExtSettings.path || '';
|
|
143
146
|
let originalId = '';
|
|
144
147
|
|
|
145
148
|
const isExtensionToBeDeleted = ['resources', 'passwords-ext', 'cookies-ext'].some(substring => extPath.includes(substring))
|
|
146
|
-
|
|
149
|
+
&& [GOLOGIN_BASE_FOLDER_NAME, GOLOGIN_TEST_FOLDER_NAME].some(substring => extPath.includes(substring))
|
|
150
|
+
|| DEFAULT_ORBITA_EXTENSIONS_NAMES.includes(extName)
|
|
151
|
+
&& [GOLOGIN_BASE_FOLDER_NAME, GOLOGIN_TEST_FOLDER_NAME].some(substring => extPath.includes(substring));
|
|
152
|
+
|
|
147
153
|
if (isExtensionToBeDeleted) {
|
|
148
154
|
delete extensionsSettings[extensionId];
|
|
155
|
+
|
|
149
156
|
return;
|
|
150
157
|
}
|
|
151
158
|
|
|
152
|
-
if (
|
|
159
|
+
if (osPlatform === 'win32') {
|
|
153
160
|
extPath = extPath.replace(/\//g, '\\');
|
|
154
161
|
} else {
|
|
155
162
|
extPath = extPath.replace(/\\/g, '/');
|
|
156
163
|
}
|
|
164
|
+
|
|
157
165
|
extensionsSettings[extensionId].path = extPath;
|
|
158
166
|
|
|
159
|
-
const
|
|
167
|
+
const splittedPath = extPath.split(path.sep);
|
|
168
|
+
const isExtensionManageable = ['chrome-extensions', 'user-extensions'].some(substring => extPath.includes(substring))
|
|
169
|
+
&& [GOLOGIN_BASE_FOLDER_NAME, GOLOGIN_TEST_FOLDER_NAME].some(substring => extPath.includes(substring));
|
|
170
|
+
|
|
160
171
|
if (isExtensionManageable) {
|
|
161
172
|
const [extFolderName] = extPath.split(path.sep).reverse();
|
|
162
173
|
[originalId] = extFolderName.split('@');
|
|
174
|
+
} else if (splittedPath.length === 2) {
|
|
175
|
+
[originalId] = splittedPath;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (isExtensionManageable || splittedPath.length === 2) {
|
|
163
179
|
const isExtensionInProfileSettings = formattedLocalExtArray.find(el => el.path.includes(originalId));
|
|
164
180
|
if (!isExtensionInProfileSettings) {
|
|
165
181
|
delete extensionsSettings[extensionId];
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
182
|
|
|
169
|
-
|
|
170
|
-
const hexEncodedPath = crypto.createHash('sha256').update(extPath).digest('hex');
|
|
171
|
-
const newId = hexEncodedPath.split('').slice(0, 32).map(symbol => extIdEncoding[symbol]).join('');
|
|
172
|
-
delete extensionsSettings[extensionId];
|
|
173
|
-
|
|
174
|
-
extensionsSettings[newId] = currentExtSettings;
|
|
175
|
-
extensionId = newId;
|
|
176
|
-
}
|
|
177
|
-
} else {
|
|
178
|
-
const splittedPath = extPath.split(path.sep);
|
|
179
|
-
if (splittedPath.length === 2) {
|
|
180
|
-
[originalId] = splittedPath
|
|
183
|
+
return;
|
|
181
184
|
}
|
|
182
185
|
}
|
|
183
186
|
|
|
@@ -186,10 +189,28 @@ class BrowserUserDataManager {
|
|
|
186
189
|
return;
|
|
187
190
|
}
|
|
188
191
|
|
|
189
|
-
|
|
192
|
+
const initialExtName = extensionId;
|
|
193
|
+
|
|
194
|
+
extensionId = await this.recalculateId({
|
|
195
|
+
localExtObj, extensionId, extensionsSettings, currentExtSettings,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
if (initialExtName !== extensionId) {
|
|
199
|
+
const profilePath = path.join(os.tmpdir(), `gologin_profile_${profileId}`);
|
|
200
|
+
const extSyncFolder = path.join(profilePath, 'Default', 'Sync Extension Settings', initialExtName);
|
|
201
|
+
const newSyncFolder = path.join(profilePath, 'Default', 'Sync Extension Settings', extensionId);
|
|
202
|
+
|
|
203
|
+
await rename(extSyncFolder, newSyncFolder).catch(() => null);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (localExtObj.path.endsWith('.zip')) {
|
|
207
|
+
localExtObj.path = localExtObj.path.replace('.zip', '');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
extensionsSettings[extensionId].path = localExtObj.path || '';
|
|
190
211
|
});
|
|
191
212
|
|
|
192
|
-
return extensionsSettings;
|
|
213
|
+
return Promise.all(promises).then(() => extensionsSettings);
|
|
193
214
|
}
|
|
194
215
|
|
|
195
216
|
static async setOriginalExtPaths(settings = {}, originalExtensionsFolder = '') {
|
|
@@ -244,6 +265,48 @@ class BrowserUserDataManager {
|
|
|
244
265
|
|
|
245
266
|
return extensionsSettings;
|
|
246
267
|
}
|
|
268
|
+
|
|
269
|
+
static async recalculateId({ localExtObj, extensionId, extensionsSettings, currentExtSettings }) {
|
|
270
|
+
if (currentExtSettings.manifest?.key) {
|
|
271
|
+
return extensionId;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const manifestFilePath = path.join(localExtObj.path, 'manifest.json');
|
|
275
|
+
const manifestString = await readFile(manifestFilePath, { encoding: 'utf8' }).catch(() => ({}));
|
|
276
|
+
|
|
277
|
+
if (!manifestString) {
|
|
278
|
+
return extensionId;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
let manifestObject;
|
|
282
|
+
try {
|
|
283
|
+
manifestObject = JSON.parse(manifestString);
|
|
284
|
+
} catch {
|
|
285
|
+
return extensionId;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (manifestObject.key) {
|
|
289
|
+
return extensionId;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
let encoding = 'utf8';
|
|
293
|
+
if (osPlatform === 'win32') {
|
|
294
|
+
encoding = 'utf16le';
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const extPathToEncode = Buffer.from(localExtObj.path, encoding);
|
|
298
|
+
|
|
299
|
+
const hexEncodedPath = crypto.createHash('sha256').update(extPathToEncode).digest('hex');
|
|
300
|
+
const newId = hexEncodedPath.split('').slice(0, 32).map(symbol => extIdEncoding[symbol]).join('');
|
|
301
|
+
if (extensionId !== newId) {
|
|
302
|
+
delete extensionsSettings[extensionId];
|
|
303
|
+
|
|
304
|
+
extensionsSettings[newId] = currentExtSettings;
|
|
305
|
+
extensionId = newId;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return extensionId;
|
|
309
|
+
}
|
|
247
310
|
}
|
|
248
311
|
|
|
249
312
|
const extIdEncoding = {
|
package/common.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
const ExtensionsExtractor = require('./extensions-extractor');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
|
|
6
|
+
const HOMEDIR = os.homedir();
|
|
7
|
+
const CHROME_EXT_DIR_NAME = 'chrome-extensions';
|
|
8
|
+
const EXTENSIONS_PATH = path.join(HOMEDIR, '.gologin', 'extensions');
|
|
9
|
+
const CHROME_EXTENSIONS_PATH = path.join(EXTENSIONS_PATH, CHROME_EXT_DIR_NAME);
|
|
10
|
+
const USER_EXTENSIONS_PATH = path.join(HOMEDIR, '.gologin', 'extensions', 'user-extensions');
|
|
11
|
+
|
|
12
|
+
const composeExtractionPromises = (filteredArchives, destPath = CHROME_EXTENSIONS_PATH) => (
|
|
13
|
+
filteredArchives.map((extArchivePath) => {
|
|
14
|
+
const [archiveName = ''] = extArchivePath.split(path.sep).reverse();
|
|
15
|
+
const [destFolder] = archiveName.split('.');
|
|
16
|
+
return ExtensionsExtractor.extractExtension(extArchivePath, path.join(destPath, destFolder))
|
|
17
|
+
.then(() => ExtensionsExtractor.deleteExtensionArchive(extArchivePath))
|
|
18
|
+
})
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
module.exports.composeExtractionPromises = composeExtractionPromises;
|
|
22
|
+
module.exports.USER_EXTENSIONS_PATH = USER_EXTENSIONS_PATH;
|
|
23
|
+
module.exports.CHROME_EXTENSIONS_PATH = CHROME_EXTENSIONS_PATH;
|
package/extensions-manager.js
CHANGED
|
@@ -1,28 +1,23 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const request = require('requestretry').defaults({ timeout: 60000 });
|
|
3
3
|
const fs = require('fs');
|
|
4
|
-
const { mkdir, readdir, rmdir } = require('fs').promises;
|
|
5
|
-
const os = require('os');
|
|
4
|
+
const { mkdir, readdir, rmdir, unlink } = require('fs').promises;
|
|
6
5
|
|
|
7
|
-
const
|
|
6
|
+
const UserExtensionsManager = require('./user-extensions-manager');
|
|
7
|
+
const { composeExtractionPromises, CHROME_EXTENSIONS_PATH, USER_EXTENSIONS_PATH } = require('./common');
|
|
8
8
|
|
|
9
|
-
const HOMEDIR = os.homedir();
|
|
10
|
-
const CHROME_EXT_DIR_NAME = 'chrome-extensions';
|
|
11
|
-
const EXTENSIONS_PATH = path.join(HOMEDIR, '.gologin', 'extensions');
|
|
12
|
-
const CHROME_EXTENSIONS_PATH = path.join(EXTENSIONS_PATH, CHROME_EXT_DIR_NAME);
|
|
13
9
|
const EXTENSION_URL = 'https://clients2.google.com/service/update2/crx?response=redirect&acceptformat=crx2,crx3&x=id%3D{ext_id}%26uc&prodversion=97.0.4692.71';
|
|
14
10
|
|
|
15
|
-
class ExtensionsManager {
|
|
16
|
-
#USER_AGENT = '';
|
|
17
|
-
#API_BASE_URL = '';
|
|
18
|
-
#ACCESS_TOKEN = '';
|
|
11
|
+
class ExtensionsManager extends UserExtensionsManager {
|
|
19
12
|
#existedChromeExtensions = [];
|
|
20
13
|
#inited = false;
|
|
21
14
|
#useLocalExtStorage = false;
|
|
15
|
+
#useCookiesExt = false;
|
|
22
16
|
#deleteProfileExtFolders = false;
|
|
23
|
-
#
|
|
17
|
+
#extensionsUpdating = true;
|
|
24
18
|
|
|
25
19
|
constructor() {
|
|
20
|
+
super();
|
|
26
21
|
if (!ExtensionsManager.instance) {
|
|
27
22
|
ExtensionsManager.instance = this;
|
|
28
23
|
}
|
|
@@ -30,49 +25,42 @@ class ExtensionsManager {
|
|
|
30
25
|
return ExtensionsManager.instance;
|
|
31
26
|
}
|
|
32
27
|
|
|
33
|
-
|
|
34
|
-
get useLocalExtStorage() { return this.#useLocalExtStorage }
|
|
35
|
-
get deleteProfileExtFolders() { return this.#deleteProfileExtFolders }
|
|
36
|
-
get deleteWidevineCdmFolder() { return this.#deleteWidevineCdmFolder }
|
|
37
|
-
|
|
38
|
-
set userAgent(userAgent) {
|
|
39
|
-
if (!userAgent) {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
this.#USER_AGENT = userAgent;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
set accessToken(accessToken) {
|
|
47
|
-
if (!accessToken) {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
this.#ACCESS_TOKEN = accessToken;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
set apiUrl(apiUrl) {
|
|
55
|
-
if (!apiUrl) {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
this.#API_BASE_URL = apiUrl;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
init() {
|
|
28
|
+
async init() {
|
|
63
29
|
if (this.#inited) {
|
|
64
30
|
return Promise.resolve();
|
|
65
31
|
}
|
|
66
32
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
33
|
+
const promises = [
|
|
34
|
+
mkdir(CHROME_EXTENSIONS_PATH, { recursive: true })
|
|
35
|
+
.then(() => readdir(CHROME_EXTENSIONS_PATH))
|
|
36
|
+
.then(filesList => {
|
|
37
|
+
this.#existedChromeExtensions = filesList.filter(extPath => !extPath.includes('.zip'));
|
|
38
|
+
return filesList.map(fileName => fileName.includes('.zip') ?
|
|
39
|
+
unlink(path.join(CHROME_EXTENSIONS_PATH, fileName)) :
|
|
40
|
+
Promise.resolve());
|
|
41
|
+
})
|
|
42
|
+
.then(promisesToDelete => Promise.all(promisesToDelete))
|
|
43
|
+
.catch((e) => console.log('ExtensionsManager init error:', e)),
|
|
44
|
+
mkdir(USER_EXTENSIONS_PATH, { recursive: true })
|
|
45
|
+
.then(() => readdir(USER_EXTENSIONS_PATH))
|
|
46
|
+
.then(filesList => {
|
|
47
|
+
this.existedUserExtensions = filesList.filter(extPath => !extPath.includes('.zip'));
|
|
48
|
+
return filesList.map(fileName => fileName.includes('.zip') ?
|
|
49
|
+
unlink(path.join(USER_EXTENSIONS_PATH, fileName)) :
|
|
50
|
+
Promise.resolve());
|
|
51
|
+
})
|
|
52
|
+
.then((promisesToDelete) => Promise.all(promisesToDelete))
|
|
53
|
+
.catch((e) => console.log('error creating user extensions folder:', e)),
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
return Promise.all(promises).then(() => this.#inited = true);
|
|
74
57
|
}
|
|
75
58
|
|
|
59
|
+
get isInited() { return this.#inited }
|
|
60
|
+
get useLocalExtStorage() { return this.#useLocalExtStorage }
|
|
61
|
+
get deleteProfileExtFolders() { return this.#deleteProfileExtFolders }
|
|
62
|
+
get useCookiesExt() { return this.#useCookiesExt }
|
|
63
|
+
|
|
76
64
|
get existedChromeExtensionsList() {
|
|
77
65
|
return this.#existedChromeExtensions;
|
|
78
66
|
}
|
|
@@ -83,20 +71,25 @@ class ExtensionsManager {
|
|
|
83
71
|
}
|
|
84
72
|
|
|
85
73
|
const extensionsToDownload = this.#getExtensionsToDownload(profileExtensions);
|
|
86
|
-
if (!extensionsToDownload) {
|
|
87
|
-
return [];
|
|
88
|
-
}
|
|
89
74
|
|
|
90
75
|
const downloadedArchives = await this.downloadChromeExtensions(extensionsToDownload);
|
|
91
76
|
const filteredArchives = downloadedArchives.filter(Boolean);
|
|
92
|
-
const promises = composeExtractionPromises(filteredArchives);
|
|
93
77
|
|
|
94
|
-
|
|
78
|
+
if (filteredArchives.length) {
|
|
79
|
+
const [downloadedFolders] = filteredArchives.map(archivePath => archivePath.split(path.sep).reverse());
|
|
80
|
+
this.#existedChromeExtensions = [...this.#existedChromeExtensions, ...downloadedFolders];
|
|
81
|
+
|
|
82
|
+
const promises = composeExtractionPromises(filteredArchives);
|
|
83
|
+
|
|
84
|
+
await Promise.all(promises);
|
|
85
|
+
}
|
|
86
|
+
|
|
95
87
|
return this.getExtensionsStrToIncludeAsOrbitaParam(profileExtensions);
|
|
96
88
|
}
|
|
97
89
|
|
|
98
90
|
#getExtensionsToDownload(profileExtensions) {
|
|
99
|
-
const
|
|
91
|
+
const existedExtensionsFolders = [...this.#existedChromeExtensions, ...this.existedUserExtensions]
|
|
92
|
+
const existedOriginalIds = existedExtensionsFolders.map((val) => {
|
|
100
93
|
const [originalId] = val.split('@');
|
|
101
94
|
return originalId;
|
|
102
95
|
});
|
|
@@ -159,10 +152,11 @@ class ExtensionsManager {
|
|
|
159
152
|
}
|
|
160
153
|
|
|
161
154
|
async getExtensionsPolicies() {
|
|
162
|
-
const globalExtConfig = await request.get(`${this
|
|
155
|
+
const globalExtConfig = await request.get(`${this.apiBaseUrl}/gologin-settings/chrome_ext_policies`, {
|
|
163
156
|
headers: {
|
|
164
|
-
Authorization: `Bearer ${this
|
|
165
|
-
'user-agent': this
|
|
157
|
+
Authorization: `Bearer ${this.accessToken}`,
|
|
158
|
+
'user-agent': this.userAgent,
|
|
159
|
+
'x-two-factor-token': this.twoFaKey || '',
|
|
166
160
|
},
|
|
167
161
|
json: true,
|
|
168
162
|
maxAttempts: 2,
|
|
@@ -175,40 +169,12 @@ class ExtensionsManager {
|
|
|
175
169
|
const {
|
|
176
170
|
useLocalExtStorage = false,
|
|
177
171
|
deleteProfileExtFolders = false,
|
|
178
|
-
|
|
172
|
+
useCookiesExt = true,
|
|
179
173
|
} = chromeExtPolicies;
|
|
180
174
|
|
|
181
175
|
this.#useLocalExtStorage = useLocalExtStorage;
|
|
182
176
|
this.#deleteProfileExtFolders = deleteProfileExtFolders;
|
|
183
|
-
this.#
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
async getExtensionsStrToIncludeAsOrbitaParam(profileExtensions = []) {
|
|
187
|
-
if (!(Array.isArray(profileExtensions) && profileExtensions.length)) {
|
|
188
|
-
return [];
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const chromeExtList = await readdir(CHROME_EXTENSIONS_PATH);
|
|
192
|
-
if (!chromeExtList) {
|
|
193
|
-
return [];
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const formattedIdsList = chromeExtList.map((el) => {
|
|
197
|
-
const [originalId] = el.split('@');
|
|
198
|
-
return {
|
|
199
|
-
originalId,
|
|
200
|
-
folderName: el,
|
|
201
|
-
};
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
return profileExtensions.map((el) => {
|
|
205
|
-
const extExisted = formattedIdsList.find(chromeExtPathElem => chromeExtPathElem.originalId === el);
|
|
206
|
-
if (!extExisted) {
|
|
207
|
-
return '';
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return path.join(CHROME_EXTENSIONS_PATH, extExisted.folderName);
|
|
211
|
-
}).filter(Boolean);
|
|
177
|
+
this.#useCookiesExt = useCookiesExt;
|
|
212
178
|
}
|
|
213
179
|
|
|
214
180
|
async updateExtensions() {
|
|
@@ -220,7 +186,7 @@ class ExtensionsManager {
|
|
|
220
186
|
const oldFolders = [];
|
|
221
187
|
|
|
222
188
|
const versionCheckPromises = fileList.map(async (extension) => {
|
|
223
|
-
if (!extension.includes('@')) {
|
|
189
|
+
if (!extension.includes('@') || extension.includes('.zip')) {
|
|
224
190
|
return '';
|
|
225
191
|
}
|
|
226
192
|
|
|
@@ -247,7 +213,70 @@ class ExtensionsManager {
|
|
|
247
213
|
rmdir(folder, { recursive: true, maxRetries: 3 }).catch(() => {})
|
|
248
214
|
));
|
|
249
215
|
|
|
250
|
-
|
|
216
|
+
await Promise.all(removeFoldersPromises).then(() => this.#extensionsUpdating = false);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
async checkLocalExtensions() {
|
|
221
|
+
if (this.#extensionsUpdating || !this.accessToken) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const fileList = await readdir(CHROME_EXTENSIONS_PATH).catch(() => []);
|
|
226
|
+
if (!fileList.length) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const extensionsIds = fileList.filter(folderName => folderName.includes('@') && !folderName.includes('.zip'))
|
|
231
|
+
.map(folderName => {
|
|
232
|
+
const [name] = folderName.split('@');
|
|
233
|
+
return name;
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
if (!extensionsIds.length) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
this.insertExtensionsToDb(extensionsIds);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async insertExtensionsToDb(extensionsIds, pathToExtensions = CHROME_EXTENSIONS_PATH) {
|
|
244
|
+
if (!extensionsIds?.length) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const checkResponse = await request(`${this.apiBaseUrl}/extensions/check`, {
|
|
249
|
+
method: 'POST',
|
|
250
|
+
headers: {
|
|
251
|
+
Authorization: `Bearer ${this.accessToken}`,
|
|
252
|
+
'user-agent': this.userAgent,
|
|
253
|
+
'x-two-factor-token': this.twoFaKey || '',
|
|
254
|
+
},
|
|
255
|
+
body: {
|
|
256
|
+
extensionsIds,
|
|
257
|
+
},
|
|
258
|
+
json: true,
|
|
259
|
+
});
|
|
260
|
+
const { extensionsToAdd = [] } = checkResponse.body;
|
|
261
|
+
|
|
262
|
+
if (!extensionsToAdd.length) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const extensionsToUpdate = await this.getExtensionsNameAndImage(extensionsToAdd, pathToExtensions);
|
|
267
|
+
|
|
268
|
+
request(`${this.apiBaseUrl}/extensions/create`, {
|
|
269
|
+
method: 'POST',
|
|
270
|
+
headers: {
|
|
271
|
+
Authorization: `Bearer ${this.accessToken}`,
|
|
272
|
+
'user-agent': this.userAgent,
|
|
273
|
+
'x-two-factor-token': this.twoFaKey || '',
|
|
274
|
+
},
|
|
275
|
+
body: {
|
|
276
|
+
extensionsInfo: extensionsToUpdate,
|
|
277
|
+
},
|
|
278
|
+
json: true,
|
|
279
|
+
});
|
|
251
280
|
}
|
|
252
281
|
|
|
253
282
|
getExtensionsToInstall(extensionsFromPref, extensionsFromDB) {
|
|
@@ -330,15 +359,6 @@ const getExtVersion = (metadata) => {
|
|
|
330
359
|
return splitExtName.join('_');
|
|
331
360
|
};
|
|
332
361
|
|
|
333
|
-
const composeExtractionPromises = (filteredArchives) => (
|
|
334
|
-
filteredArchives.map((extArchivePath) => {
|
|
335
|
-
const [archiveName = ''] = extArchivePath.split(path.sep).reverse();
|
|
336
|
-
const [destFolder] = archiveName.split('.');
|
|
337
|
-
return ExtensionsExtractor.extractExtension(extArchivePath, path.join(CHROME_EXTENSIONS_PATH, destFolder))
|
|
338
|
-
.then(() => ExtensionsExtractor.deleteExtensionArchive(extArchivePath))
|
|
339
|
-
})
|
|
340
|
-
);
|
|
341
|
-
|
|
342
362
|
module.exports = ExtensionsManager;
|
|
343
363
|
|
|
344
364
|
|
package/gologin.js
CHANGED
|
@@ -23,6 +23,7 @@ const ExtensionsManager = require('./extensions-manager');
|
|
|
23
23
|
|
|
24
24
|
const SEPARATOR = path.sep;
|
|
25
25
|
const API_URL = 'https://api.gologin.com';
|
|
26
|
+
// const API_URL = 'http://localhost:3002';
|
|
26
27
|
const OS_PLATFORM = process.platform;
|
|
27
28
|
|
|
28
29
|
// process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0;
|
|
@@ -402,9 +403,11 @@ class GoLogin {
|
|
|
402
403
|
let preferences = JSON.parse(preferences_raw.toString());
|
|
403
404
|
let proxy = _.get(profile, 'proxy');
|
|
404
405
|
let name = _.get(profile, 'name');
|
|
405
|
-
const chromeExtensions = _.get(profile, 'chromeExtensions');
|
|
406
|
+
const chromeExtensions = _.get(profile, 'chromeExtensions') || [];
|
|
407
|
+
const userChromeExtensions = _.get(profile, 'userChromeExtensions') || [];
|
|
408
|
+
const allExtensions = [...chromeExtensions, ...userChromeExtensions];
|
|
406
409
|
|
|
407
|
-
if (
|
|
410
|
+
if (allExtensions.length) {
|
|
408
411
|
const ExtensionsManagerInst = new ExtensionsManager();
|
|
409
412
|
ExtensionsManagerInst.apiUrl = API_URL;
|
|
410
413
|
await ExtensionsManagerInst.init()
|
|
@@ -416,15 +419,31 @@ class GoLogin {
|
|
|
416
419
|
let profileExtensionsCheckRes = [];
|
|
417
420
|
|
|
418
421
|
if (ExtensionsManagerInst.useLocalExtStorage) {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
422
|
+
const promises = [
|
|
423
|
+
ExtensionsManagerInst.checkChromeExtensions(allExtensions)
|
|
424
|
+
.then(res => ({ profileExtensionsCheckRes: res }))
|
|
425
|
+
.catch((e) => {
|
|
426
|
+
console.log('checkChromeExtensions error: ', e);
|
|
427
|
+
return { profileExtensionsCheckRes: [] };
|
|
428
|
+
}),
|
|
429
|
+
ExtensionsManagerInst.checkLocalUserChromeExtensions(userChromeExtensions)
|
|
430
|
+
.then(res => ({ profileUserExtensionsCheckRes: res }))
|
|
431
|
+
.catch((error) => {
|
|
432
|
+
console.log('checkUserChromeExtensions error: ', error);
|
|
433
|
+
return null;
|
|
434
|
+
}),
|
|
435
|
+
];
|
|
436
|
+
const extensionsResult = await Promise.all(promises);
|
|
437
|
+
|
|
438
|
+
const profileExtensionPathRes = extensionsResult.find(el => 'profileExtensionsCheckRes' in el) || {};
|
|
439
|
+
const profileUserExtensionPathRes = extensionsResult.find(el => 'profileUserExtensionsCheckRes' in el);
|
|
440
|
+
profileExtensionsCheckRes =
|
|
441
|
+
(profileExtensionPathRes?.profileExtensionsCheckRes || []).concat(profileUserExtensionPathRes?.profileUserExtensionsCheckRes || []);
|
|
423
442
|
}
|
|
424
443
|
|
|
425
444
|
let extSettings;
|
|
426
445
|
if (ExtensionsManagerInst.useLocalExtStorage) {
|
|
427
|
-
extSettings = BrowserUserDataManager.setExtPathsAndRemoveDeleted(preferences, profileExtensionsCheckRes);
|
|
446
|
+
extSettings = await BrowserUserDataManager.setExtPathsAndRemoveDeleted(preferences, profileExtensionsCheckRes, this.profile_id);
|
|
428
447
|
} else {
|
|
429
448
|
const originalExtensionsFolder = path.join(profilePath, 'Default', 'Extensions');
|
|
430
449
|
extSettings = await BrowserUserDataManager.setOriginalExtPaths(preferences, originalExtensionsFolder);
|
|
@@ -484,6 +503,7 @@ class GoLogin {
|
|
|
484
503
|
};
|
|
485
504
|
profile.geoLocation = this.getGeolocationParams(profileGeolocation, tzGeoLocation);
|
|
486
505
|
profile.name = name;
|
|
506
|
+
profile.name_base64 = Buffer.from(name).toString('base64');
|
|
487
507
|
profile.profile_id = this.profile_id;
|
|
488
508
|
|
|
489
509
|
profile.webRtc = {
|
|
@@ -821,7 +841,7 @@ class GoLogin {
|
|
|
821
841
|
if (Array.isArray(this.extra_params) && this.extra_params.length) {
|
|
822
842
|
params = params.concat(this.extra_params);
|
|
823
843
|
}
|
|
824
|
-
|
|
844
|
+
console.log(params)
|
|
825
845
|
const child = execFile(ORBITA_BROWSER, params, {env});
|
|
826
846
|
// const child = spawn(ORBITA_BROWSER, params, { env, shell: true });
|
|
827
847
|
child.stdout.on('data', (data) => debug(data.toString()));
|
package/package.json
CHANGED
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const { readdir, rmdir, readFile, stat, mkdir, copyFile } = require('fs').promises;
|
|
4
|
+
const request = require('requestretry').defaults({ timeout: 60000 });
|
|
5
|
+
const zipdir = require('zip-dir');
|
|
6
|
+
|
|
7
|
+
const ExtensionsExtractor = require('./extensions-extractor');
|
|
8
|
+
const { composeExtractionPromises, CHROME_EXTENSIONS_PATH, USER_EXTENSIONS_PATH } = require('./common');
|
|
9
|
+
|
|
10
|
+
const MAX_FILE_SIZE = 80 * 1024 * 1024;
|
|
11
|
+
const MAX_FILE_SIZE_MB = MAX_FILE_SIZE / 1024 / 1024;
|
|
12
|
+
|
|
13
|
+
class UserExtensionsManager {
|
|
14
|
+
#existedUserExtensions = [];
|
|
15
|
+
#API_BASE_URL = '';
|
|
16
|
+
#ACCESS_TOKEN = '';
|
|
17
|
+
#USER_AGENT = '';
|
|
18
|
+
#TWO_FA_KEY = '';
|
|
19
|
+
|
|
20
|
+
set userAgent(userAgent) {
|
|
21
|
+
if (!userAgent) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
this.#USER_AGENT = userAgent;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
set accessToken(accessToken) {
|
|
29
|
+
if (!accessToken) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
this.#ACCESS_TOKEN = accessToken;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
set twoFaKey(twoFaKey) {
|
|
37
|
+
if (!twoFaKey) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.#TWO_FA_KEY = twoFaKey;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
set apiUrl(apiUrl) {
|
|
45
|
+
if (!apiUrl) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
this.#API_BASE_URL = apiUrl;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get apiBaseUrl() {
|
|
53
|
+
return this.#API_BASE_URL;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
get existedUserExtensions() {
|
|
57
|
+
return this.#existedUserExtensions;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
get accessToken() {
|
|
61
|
+
return this.#ACCESS_TOKEN;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
get twoFaKey() {
|
|
65
|
+
return this.#TWO_FA_KEY;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
get userAgent() {
|
|
69
|
+
return this.#USER_AGENT;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
set existedUserExtensions(fileList) {
|
|
73
|
+
if (!fileList) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
this.#existedUserExtensions = fileList;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async addCustomExtension(pathToFiles) {
|
|
81
|
+
try {
|
|
82
|
+
const filesSize = await checkFileSizeSync(pathToFiles);
|
|
83
|
+
const isZip = pathToFiles.endsWith('.zip');
|
|
84
|
+
|
|
85
|
+
if (filesSize > MAX_FILE_SIZE) {
|
|
86
|
+
throw new Error(`The maximum file size is ${MAX_FILE_SIZE_MB}MB`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const customId = this.generateExtensionId();
|
|
90
|
+
|
|
91
|
+
if (isZip) {
|
|
92
|
+
const pathToExtract = path.join(USER_EXTENSIONS_PATH, customId);
|
|
93
|
+
await ExtensionsExtractor.extractExtension(pathToFiles, pathToExtract);
|
|
94
|
+
pathToFiles = pathToExtract;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let fileList = (await readdir(pathToFiles).catch(() => ['cantReadError']))
|
|
98
|
+
.filter(folderContent => folderContent !== '.DS_Store');
|
|
99
|
+
|
|
100
|
+
if (fileList.length === 1 && !fileList.includes('cantReadError')) {
|
|
101
|
+
const isFolder = (await stat(pathToFiles)).isDirectory();
|
|
102
|
+
if (isFolder) {
|
|
103
|
+
const [folderName] = fileList;
|
|
104
|
+
pathToFiles = path.join(pathToFiles, folderName);
|
|
105
|
+
fileList = await readdir(pathToFiles).catch(() => ['cantReadError']);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (fileList.includes('cantReadError')) {
|
|
110
|
+
throw new Error('Can\'t access folder');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!fileList.includes('manifest.json')) {
|
|
114
|
+
if (isZip) {
|
|
115
|
+
rmdir(pathToFiles);
|
|
116
|
+
}
|
|
117
|
+
throw new Error('There is no manifest.json in the extension folder');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!isZip) {
|
|
121
|
+
const destPath = path.join(USER_EXTENSIONS_PATH, customId)
|
|
122
|
+
await copyFolder(pathToFiles, destPath).catch(() => {
|
|
123
|
+
throw new Error('Something went wrong coping your folder');
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const [nameIconId] = await this.getExtensionsNameAndImage([customId], USER_EXTENSIONS_PATH);
|
|
128
|
+
|
|
129
|
+
if (!nameIconId) {
|
|
130
|
+
throw new Error('Something went wrong. Please try again later');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const dbResult = await request(`${this.#API_BASE_URL}/extensions/create_user_extension`, {
|
|
134
|
+
method: 'POST',
|
|
135
|
+
headers: {
|
|
136
|
+
Authorization: `Bearer ${this.#ACCESS_TOKEN}`,
|
|
137
|
+
'user-agent': this.#USER_AGENT,
|
|
138
|
+
'x-two-factor-token': this.#TWO_FA_KEY || '',
|
|
139
|
+
},
|
|
140
|
+
body: {
|
|
141
|
+
extensionInfo: nameIconId,
|
|
142
|
+
},
|
|
143
|
+
json: true,
|
|
144
|
+
});
|
|
145
|
+
// if success - there is no body
|
|
146
|
+
if (dbResult.body) {
|
|
147
|
+
throw new Error('Something went wrong inserting your data to database');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const fileBuffer = await zipdir(pathToFiles).catch(() => null);
|
|
151
|
+
if (!fileBuffer) {
|
|
152
|
+
throw new Error('Something went wrong. Please try again later');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const signedUrl = await request.get(`${this.#API_BASE_URL}/extensions/upload_url?extId=${customId}`, {
|
|
156
|
+
headers: {
|
|
157
|
+
Authorization: `Bearer ${this.#ACCESS_TOKEN}`,
|
|
158
|
+
'user-agent': this.#USER_AGENT,
|
|
159
|
+
'x-two-factor-token': this.#TWO_FA_KEY || '',
|
|
160
|
+
},
|
|
161
|
+
maxAttempts: 3,
|
|
162
|
+
retryDelay: 2000,
|
|
163
|
+
timeout: 10 * 1000,
|
|
164
|
+
fullResponse: false,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
const uploadResponse = await request.put(signedUrl, {
|
|
168
|
+
headers: {
|
|
169
|
+
'Content-Type': 'application/zip',
|
|
170
|
+
'Content-Length': Buffer.byteLength(fileBuffer),
|
|
171
|
+
},
|
|
172
|
+
body: fileBuffer,
|
|
173
|
+
maxBodyLength: Infinity,
|
|
174
|
+
maxContentLength: Infinity,
|
|
175
|
+
maxAttempts: 3,
|
|
176
|
+
retryDelay: 2000,
|
|
177
|
+
timeout: 30 * 1000,
|
|
178
|
+
fullResponse: true,
|
|
179
|
+
});
|
|
180
|
+
// if success - there is no body, in case of error - there will be an error in the body
|
|
181
|
+
if (uploadResponse.body) {
|
|
182
|
+
throw new Error('Your extension is added locally but we couldn\'t upload it to the cloud');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
status: 'success',
|
|
187
|
+
message: nameIconId,
|
|
188
|
+
}
|
|
189
|
+
} catch (e) {
|
|
190
|
+
return {
|
|
191
|
+
status: 'error',
|
|
192
|
+
message: e.message,
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
checkLocalUserChromeExtensions = async (userChromeExtensions) => {
|
|
198
|
+
if (!userChromeExtensions.length) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const extensionsToDownloadPaths = await request.post(`${this.#API_BASE_URL}/extensions/user_chrome_extensions_paths`, {
|
|
203
|
+
json: true,
|
|
204
|
+
fullResponse: false,
|
|
205
|
+
headers: {
|
|
206
|
+
Authorization: `Bearer ${this.#ACCESS_TOKEN}`,
|
|
207
|
+
'user-agent': this.#USER_AGENT,
|
|
208
|
+
'x-two-factor-token': this.#TWO_FA_KEY || '',
|
|
209
|
+
},
|
|
210
|
+
body: {
|
|
211
|
+
existedUserChromeExtensions: this.#existedUserExtensions,
|
|
212
|
+
}
|
|
213
|
+
}) || [];
|
|
214
|
+
|
|
215
|
+
const extensionsToDownloadPathsFiltered =
|
|
216
|
+
extensionsToDownloadPaths.filter(extPath => userChromeExtensions.some(extId => extPath.includes(extId)));
|
|
217
|
+
|
|
218
|
+
if (!extensionsToDownloadPathsFiltered.length) {
|
|
219
|
+
return this.getExtensionsStrToIncludeAsOrbitaParam(userChromeExtensions, USER_EXTENSIONS_PATH);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const promises = extensionsToDownloadPathsFiltered.map(async awsPath => {
|
|
223
|
+
const [basePath] = awsPath.split('?');
|
|
224
|
+
const [extId] = basePath.split('/').reverse();
|
|
225
|
+
const zipPath = `${path.join(USER_EXTENSIONS_PATH, extId)}.zip`;
|
|
226
|
+
const archiveZip = fs.createWriteStream(zipPath);
|
|
227
|
+
|
|
228
|
+
await request(awsPath, {
|
|
229
|
+
retryDelay: 2 * 1000,
|
|
230
|
+
maxAttempts: 3,
|
|
231
|
+
}).pipe(archiveZip);
|
|
232
|
+
|
|
233
|
+
await new Promise(r => archiveZip.on('close', () => r()));
|
|
234
|
+
return zipPath;
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const zipPaths = await Promise.all(promises).catch(() => []);
|
|
238
|
+
|
|
239
|
+
if (!zipPaths) {
|
|
240
|
+
return this.getExtensionsStrToIncludeAsOrbitaParam(userChromeExtensions, USER_EXTENSIONS_PATH);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const extractionPromises = composeExtractionPromises(zipPaths, USER_EXTENSIONS_PATH);
|
|
244
|
+
const isExtensionsExtracted = await Promise.all(extractionPromises).catch(() => 'error');
|
|
245
|
+
|
|
246
|
+
if (isExtensionsExtracted !== 'error') {
|
|
247
|
+
const [downloadedFolders] = zipPaths.map(archivePath => archivePath.split(path.sep).reverse());
|
|
248
|
+
this.#existedUserExtensions = [...this.#existedUserExtensions, ...downloadedFolders];
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return this.getExtensionsStrToIncludeAsOrbitaParam(userChromeExtensions, USER_EXTENSIONS_PATH);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async getExtensionsStrToIncludeAsOrbitaParam(profileExtensions = [], folderPath = CHROME_EXTENSIONS_PATH) {
|
|
255
|
+
if (!(Array.isArray(profileExtensions) && profileExtensions.length)) {
|
|
256
|
+
return [];
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const folders = await readdir(folderPath).then(folderNames => folderNames.map(folderName => path.join(folderPath, folderName)));
|
|
260
|
+
|
|
261
|
+
if (!folders.length) {
|
|
262
|
+
return [];
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const formattedIdsList = folders.map((el) => {
|
|
266
|
+
const [folderName] = el.split(path.sep).reverse();
|
|
267
|
+
const [originalId] = folderName.split('@');
|
|
268
|
+
return {
|
|
269
|
+
originalId,
|
|
270
|
+
path: el,
|
|
271
|
+
};
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
return profileExtensions.map((el) => {
|
|
275
|
+
const extExisted = formattedIdsList.find(chromeExtPathElem => chromeExtPathElem.originalId === el);
|
|
276
|
+
|
|
277
|
+
if (!extExisted) {
|
|
278
|
+
return '';
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return extExisted.path;
|
|
282
|
+
}).filter(Boolean);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async getExtensionsNameAndImage(extensionsIds, pathToExtensions) {
|
|
286
|
+
const isCheckLocalFiles = [CHROME_EXTENSIONS_PATH, USER_EXTENSIONS_PATH].includes(pathToExtensions);
|
|
287
|
+
const extensionFolderNames = await readdir(pathToExtensions).catch(() => {});
|
|
288
|
+
const filteredExtensionFolderNames = extensionFolderNames.filter(extensionFolder => extensionsIds.some(extensionId => !extensionFolder.includes('.zip') && extensionFolder.includes(extensionId)));
|
|
289
|
+
|
|
290
|
+
if (!filteredExtensionFolderNames.length) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const namesPromise = extensionsIds.map(async (extensionsId) => {
|
|
295
|
+
const folderName = filteredExtensionFolderNames.find(folderName => folderName.includes(extensionsId));
|
|
296
|
+
|
|
297
|
+
if (!folderName) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
let pathToExtensionsFolder = [pathToExtensions, folderName];
|
|
302
|
+
if (!isCheckLocalFiles) {
|
|
303
|
+
const [extensionVersion] = await readdir(path.join(pathToExtensions, folderName));
|
|
304
|
+
pathToExtensionsFolder = [pathToExtensions, folderName, extensionVersion];
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const manifestPath = path.join(...pathToExtensionsFolder, 'manifest.json');
|
|
308
|
+
const manifestString = await readFile(manifestPath, 'utf8').catch(() => '');
|
|
309
|
+
if (!manifestString) {
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const manifestObject = JSON.parse(manifestString);
|
|
314
|
+
let name;
|
|
315
|
+
if (manifestObject.name.includes('__MSG')) {
|
|
316
|
+
const manifestName = manifestObject.name || '';
|
|
317
|
+
const fieldNameInLocale = manifestName.replace(/__/g, '').split('MSG_')[1];
|
|
318
|
+
const localePath = path.join(...pathToExtensionsFolder, '_locales', manifestObject.default_locale, 'messages.json');
|
|
319
|
+
const localeString = await readFile(localePath, 'utf8').catch(() => {});
|
|
320
|
+
|
|
321
|
+
try {
|
|
322
|
+
const parsedLocale = JSON.parse(localeString.trim());
|
|
323
|
+
name = parsedLocale[fieldNameInLocale].message;
|
|
324
|
+
} catch (e) {}
|
|
325
|
+
} else {
|
|
326
|
+
name = manifestObject.name;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (!name) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const iconObject = manifestObject.icons;
|
|
334
|
+
let iconPath = manifestObject.browser_action?.default_icon;
|
|
335
|
+
if (iconObject) {
|
|
336
|
+
iconPath = iconObject['128'];
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
let iconBSON = '';
|
|
340
|
+
if (iconPath) {
|
|
341
|
+
const iconPathFull = path.join(...pathToExtensionsFolder, iconPath);
|
|
342
|
+
iconBSON = await readFile(iconPathFull, 'base64').catch(() => {});
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return {
|
|
346
|
+
name,
|
|
347
|
+
extId: extensionsId,
|
|
348
|
+
iconBinary: iconBSON,
|
|
349
|
+
};
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
const extensionsArray = await Promise.all(namesPromise);
|
|
353
|
+
return extensionsArray.filter(Boolean);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
generateExtensionId() {
|
|
357
|
+
let result = '';
|
|
358
|
+
let extensionIdLength = 32;
|
|
359
|
+
const characters = 'abcdefghijklmnopqrstuvwxyz';
|
|
360
|
+
const charactersLength = characters.length;
|
|
361
|
+
while (extensionIdLength--) {
|
|
362
|
+
result += characters.charAt(Math.floor(Math.random() *
|
|
363
|
+
charactersLength));
|
|
364
|
+
}
|
|
365
|
+
return result;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const checkFileSizeSync = async (pathToFile) => {
|
|
370
|
+
try {
|
|
371
|
+
const [fileName] = pathToFile.split(path.sep).reverse();
|
|
372
|
+
if (fileName === '.DS_Store') {
|
|
373
|
+
return 0;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const fileStats = await stat(pathToFile);
|
|
377
|
+
if (!fileStats.isDirectory()) {
|
|
378
|
+
return fileStats.size;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const files = await readdir(pathToFile);
|
|
382
|
+
const promises = files.map(async file => checkFileSizeSync(path.join(pathToFile, file)));
|
|
383
|
+
|
|
384
|
+
return (await Promise.all(promises)).reduce((result, value) => result + value, 0);
|
|
385
|
+
} catch {
|
|
386
|
+
return -1;
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
const copyFolder = async (fromPath, destPath) => {
|
|
391
|
+
const stats = await stat(fromPath);
|
|
392
|
+
|
|
393
|
+
if (!stats.isDirectory()) {
|
|
394
|
+
return copyFile(fromPath, destPath);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
await mkdir(destPath, { recursive: true }).catch(() => null);
|
|
398
|
+
const files = await readdir(fromPath);
|
|
399
|
+
const promises = files.map(async file => {
|
|
400
|
+
await mkdir(destPath, { recursive: true }).catch(() => null);
|
|
401
|
+
|
|
402
|
+
return copyFolder(path.join(fromPath, file), path.join(destPath, file));
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
return Promise.all(promises);
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
module.exports = UserExtensionsManager;
|