gologin 1.0.50 → 1.0.53
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/README.md +4 -1
- package/browser-user-data-manager.js +90 -24
- package/common.js +23 -0
- package/cookies-manager.js +14 -5
- package/extensions-manager.js +131 -76
- package/gologin.js +26 -10
- package/package.json +1 -1
- package/profile_export_example.csv +2 -0
- package/user-extensions-manager.js +368 -0
package/README.md
CHANGED
|
@@ -103,7 +103,10 @@ For debugging use `DEBUG=* node example.js` command
|
|
|
103
103
|
To use GoLogin with Selenium see `selenium/example.js`
|
|
104
104
|
|
|
105
105
|
## Full GoLogin API
|
|
106
|
-
<a href="https://api.gologin.com/docs" target="_blank">
|
|
106
|
+
**Swagger:** <a href="https://api.gologin.com/docs" target="_blank">link here</a>
|
|
107
|
+
|
|
108
|
+
**Postman:** <a href="https://documenter.getpostman.com/view/21126834/Uz5GnvaL" target="_blank">link here</a>
|
|
109
|
+
|
|
107
110
|
|
|
108
111
|
## For local profiles
|
|
109
112
|
|
|
@@ -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 }) {
|
|
@@ -113,10 +116,13 @@ class BrowserUserDataManager {
|
|
|
113
116
|
|
|
114
117
|
const fileContent = await readFile(path.resolve(__dirname, 'fonts_config'), 'utf-8');
|
|
115
118
|
const result = fileContent.replace(/\$\$GOLOGIN_FONTS\$\$/g, path.join(profilePath, FONTS_DIR_NAME));
|
|
116
|
-
|
|
119
|
+
|
|
120
|
+
const defaultFolderPath = path.join(profilePath, 'Default');
|
|
121
|
+
await mkdir(defaultFolderPath, { recursive: true });
|
|
122
|
+
await writeFile(path.join(defaultFolderPath, 'fonts_config'), result);
|
|
117
123
|
}
|
|
118
124
|
|
|
119
|
-
static setExtPathsAndRemoveDeleted(settings = {}, profileExtensionsCheckRes = []) {
|
|
125
|
+
static setExtPathsAndRemoveDeleted(settings = {}, profileExtensionsCheckRes = [], profileId = '') {
|
|
120
126
|
const formattedLocalExtArray = profileExtensionsCheckRes.map((el) => {
|
|
121
127
|
const [extFolderName = ''] = el.split(path.sep).reverse();
|
|
122
128
|
const [originalId] = extFolderName.split('@');
|
|
@@ -127,54 +133,54 @@ class BrowserUserDataManager {
|
|
|
127
133
|
return {
|
|
128
134
|
path: el,
|
|
129
135
|
originalId,
|
|
130
|
-
}
|
|
136
|
+
};
|
|
131
137
|
}).filter(Boolean);
|
|
132
138
|
|
|
133
139
|
const extensionsSettings = settings.extensions?.settings || {};
|
|
134
140
|
const extensionsEntries = Object.entries(extensionsSettings);
|
|
135
141
|
|
|
136
|
-
extensionsEntries.
|
|
142
|
+
const promises = extensionsEntries.map(async (extensionObj) => {
|
|
137
143
|
let [extensionId, currentExtSettings = {}] = extensionObj;
|
|
138
144
|
const extName = currentExtSettings.manifest?.name || '';
|
|
139
145
|
let extPath = currentExtSettings.path || '';
|
|
140
146
|
let originalId = '';
|
|
141
147
|
|
|
142
148
|
const isExtensionToBeDeleted = ['resources', 'passwords-ext', 'cookies-ext'].some(substring => extPath.includes(substring))
|
|
143
|
-
|
|
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
|
+
|
|
144
153
|
if (isExtensionToBeDeleted) {
|
|
145
154
|
delete extensionsSettings[extensionId];
|
|
155
|
+
|
|
146
156
|
return;
|
|
147
157
|
}
|
|
148
158
|
|
|
149
|
-
if (
|
|
159
|
+
if (osPlatform === 'win32') {
|
|
150
160
|
extPath = extPath.replace(/\//g, '\\');
|
|
151
161
|
} else {
|
|
152
162
|
extPath = extPath.replace(/\\/g, '/');
|
|
153
163
|
}
|
|
164
|
+
|
|
154
165
|
extensionsSettings[extensionId].path = extPath;
|
|
155
166
|
|
|
156
|
-
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
|
+
|
|
157
171
|
if (isExtensionManageable) {
|
|
158
172
|
const [extFolderName] = extPath.split(path.sep).reverse();
|
|
159
173
|
[originalId] = extFolderName.split('@');
|
|
174
|
+
} else if (splittedPath.length === 2) {
|
|
175
|
+
[originalId] = splittedPath;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (isExtensionManageable || splittedPath.length === 2) {
|
|
160
179
|
const isExtensionInProfileSettings = formattedLocalExtArray.find(el => el.path.includes(originalId));
|
|
161
180
|
if (!isExtensionInProfileSettings) {
|
|
162
181
|
delete extensionsSettings[extensionId];
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (!currentExtSettings.manifest?.key) {
|
|
167
|
-
const hexEncodedPath = crypto.createHash('sha256').update(extPath).digest('hex');
|
|
168
|
-
const newId = hexEncodedPath.split('').slice(0, 32).map(symbol => extIdEncoding[symbol]).join('');
|
|
169
|
-
delete extensionsSettings[extensionId];
|
|
170
182
|
|
|
171
|
-
|
|
172
|
-
extensionId = newId;
|
|
173
|
-
}
|
|
174
|
-
} else {
|
|
175
|
-
const splittedPath = extPath.split(path.sep);
|
|
176
|
-
if (splittedPath.length === 2) {
|
|
177
|
-
[originalId] = splittedPath
|
|
183
|
+
return;
|
|
178
184
|
}
|
|
179
185
|
}
|
|
180
186
|
|
|
@@ -183,10 +189,28 @@ class BrowserUserDataManager {
|
|
|
183
189
|
return;
|
|
184
190
|
}
|
|
185
191
|
|
|
186
|
-
|
|
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 || '';
|
|
187
211
|
});
|
|
188
212
|
|
|
189
|
-
return extensionsSettings;
|
|
213
|
+
return Promise.all(promises).then(() => extensionsSettings);
|
|
190
214
|
}
|
|
191
215
|
|
|
192
216
|
static async setOriginalExtPaths(settings = {}, originalExtensionsFolder = '') {
|
|
@@ -241,6 +265,48 @@ class BrowserUserDataManager {
|
|
|
241
265
|
|
|
242
266
|
return extensionsSettings;
|
|
243
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
|
+
}
|
|
244
310
|
}
|
|
245
311
|
|
|
246
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/cookies-manager.js
CHANGED
|
@@ -29,8 +29,8 @@ class CookiesManager {
|
|
|
29
29
|
const chunckedCookiesArr = this.chunk(cookiesArr, MAX_SQLITE_VARIABLES);
|
|
30
30
|
|
|
31
31
|
return chunckedCookiesArr.map((cookies) => {
|
|
32
|
-
const queryPlaceholders = cookies.map(() => '(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)').join(', ');
|
|
33
|
-
const query = `insert or replace into cookies (creation_utc,
|
|
32
|
+
const queryPlaceholders = cookies.map(() => '(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)').join(', ');
|
|
33
|
+
const query = `insert or replace into cookies (creation_utc, host_key, top_frame_site_key, name, value, encrypted_value, path, expires_utc, is_secure, is_httponly, last_access_utc, has_expires, is_persistent, priority, samesite, source_scheme, source_port, is_same_party, last_update_utc) values ${queryPlaceholders}`;
|
|
34
34
|
const queryParams = cookies.flatMap((cookie) => {
|
|
35
35
|
const creationDate = cookie.creationDate ? cookie.creationDate : this.unixToLDAP(todayUnix);
|
|
36
36
|
let expirationDate = cookie.session ? 0 : this.unixToLDAP(cookie.expirationDate);
|
|
@@ -38,6 +38,10 @@ class CookiesManager {
|
|
|
38
38
|
const samesite = Object.keys(SAME_SITE).find((key) => SAME_SITE[key] === (cookie.sameSite || '-1'));
|
|
39
39
|
const isSecure =
|
|
40
40
|
cookie.name.startsWith('__Host-') || cookie.name.startsWith('__Secure-') ? 1 : Number(cookie.secure);
|
|
41
|
+
|
|
42
|
+
const sourceScheme = isSecure === 1 ? 2 : 1;
|
|
43
|
+
const sourcePort = isSecure === 1 ? 443 : 80;
|
|
44
|
+
// eslint-disable-next-line no-undefined
|
|
41
45
|
let isPersistent = [undefined, null].includes(cookie.session)
|
|
42
46
|
? Number(expirationDate !== 0)
|
|
43
47
|
: Number(!cookie.session);
|
|
@@ -49,19 +53,24 @@ class CookiesManager {
|
|
|
49
53
|
|
|
50
54
|
return [
|
|
51
55
|
creationDate,
|
|
52
|
-
'', // top_frame_site_key
|
|
53
56
|
cookie.domain,
|
|
57
|
+
'', // top_frame_site_key
|
|
54
58
|
cookie.name,
|
|
55
59
|
'', // value
|
|
60
|
+
encryptedValue,
|
|
56
61
|
cookie.path,
|
|
57
62
|
expirationDate,
|
|
58
63
|
isSecure,
|
|
59
64
|
Number(cookie.httpOnly),
|
|
60
65
|
0, // last_access_utc
|
|
66
|
+
expirationDate === 0 ? 0 : 1, // has_expires
|
|
61
67
|
isPersistent,
|
|
62
|
-
|
|
68
|
+
1, // default priority value (https://github.com/chromium/chromium/blob/main/net/cookies/cookie_constants.h)
|
|
63
69
|
samesite,
|
|
64
|
-
|
|
70
|
+
sourceScheme,
|
|
71
|
+
sourcePort,
|
|
72
|
+
0, // is_same_party
|
|
73
|
+
0, // last_update_utc
|
|
65
74
|
];
|
|
66
75
|
});
|
|
67
76
|
|
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,12 +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.#
|
|
177
|
+
this.#useCookiesExt = useCookiesExt;
|
|
184
178
|
}
|
|
185
179
|
|
|
186
180
|
async getExtensionsStrToIncludeAsOrbitaParam(profileExtensions = []) {
|
|
@@ -188,16 +182,23 @@ class ExtensionsManager {
|
|
|
188
182
|
return [];
|
|
189
183
|
}
|
|
190
184
|
|
|
191
|
-
const
|
|
192
|
-
|
|
185
|
+
const folders = await Promise.all([
|
|
186
|
+
readdir(CHROME_EXTENSIONS_PATH).then(folderNames => folderNames.map(folderName => path.join(CHROME_EXTENSIONS_PATH, folderName))),
|
|
187
|
+
readdir(USER_EXTENSIONS_PATH).then(folderNames => folderNames.map(folderName => path.join(USER_EXTENSIONS_PATH, folderName))),
|
|
188
|
+
]);
|
|
189
|
+
|
|
190
|
+
const chromeExtList = [].concat.apply([], folders).filter(Boolean);
|
|
191
|
+
|
|
192
|
+
if (!chromeExtList.length) {
|
|
193
193
|
return [];
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
const formattedIdsList = chromeExtList.map((el) => {
|
|
197
|
-
const [
|
|
197
|
+
const [folderName] = el.split(path.sep).reverse();
|
|
198
|
+
const [originalId] = folderName.split('@');
|
|
198
199
|
return {
|
|
199
200
|
originalId,
|
|
200
|
-
|
|
201
|
+
path: el,
|
|
201
202
|
};
|
|
202
203
|
});
|
|
203
204
|
|
|
@@ -207,7 +208,7 @@ class ExtensionsManager {
|
|
|
207
208
|
return '';
|
|
208
209
|
}
|
|
209
210
|
|
|
210
|
-
return
|
|
211
|
+
return extExisted.path;
|
|
211
212
|
}).filter(Boolean);
|
|
212
213
|
}
|
|
213
214
|
|
|
@@ -220,7 +221,7 @@ class ExtensionsManager {
|
|
|
220
221
|
const oldFolders = [];
|
|
221
222
|
|
|
222
223
|
const versionCheckPromises = fileList.map(async (extension) => {
|
|
223
|
-
if (!extension.includes('@')) {
|
|
224
|
+
if (!extension.includes('@') || extension.includes('.zip')) {
|
|
224
225
|
return '';
|
|
225
226
|
}
|
|
226
227
|
|
|
@@ -247,7 +248,70 @@ class ExtensionsManager {
|
|
|
247
248
|
rmdir(folder, { recursive: true, maxRetries: 3 }).catch(() => {})
|
|
248
249
|
));
|
|
249
250
|
|
|
250
|
-
|
|
251
|
+
await Promise.all(removeFoldersPromises).then(() => this.#extensionsUpdating = false);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
async checkLocalExtensions() {
|
|
256
|
+
if (this.#extensionsUpdating || !this.accessToken) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const fileList = await readdir(CHROME_EXTENSIONS_PATH).catch(() => []);
|
|
261
|
+
if (!fileList.length) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const extensionsIds = fileList.filter(folderName => folderName.includes('@') && !folderName.includes('.zip'))
|
|
266
|
+
.map(folderName => {
|
|
267
|
+
const [name] = folderName.split('@');
|
|
268
|
+
return name;
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
if (!extensionsIds.length) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
this.insertExtensionsToDb(extensionsIds);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async insertExtensionsToDb(extensionsIds, pathToExtensions = CHROME_EXTENSIONS_PATH) {
|
|
279
|
+
if (!extensionsIds?.length) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const checkResponse = await request(`${this.apiBaseUrl}/extensions/check`, {
|
|
284
|
+
method: 'POST',
|
|
285
|
+
headers: {
|
|
286
|
+
Authorization: `Bearer ${this.accessToken}`,
|
|
287
|
+
'user-agent': this.userAgent,
|
|
288
|
+
'x-two-factor-token': this.twoFaKey || '',
|
|
289
|
+
},
|
|
290
|
+
body: {
|
|
291
|
+
extensionsIds,
|
|
292
|
+
},
|
|
293
|
+
json: true,
|
|
294
|
+
});
|
|
295
|
+
const { extensionsToAdd = [] } = checkResponse.body;
|
|
296
|
+
|
|
297
|
+
if (!extensionsToAdd.length) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const extensionsToUpdate = await this.getExtensionsNameAndImage(extensionsToAdd, pathToExtensions);
|
|
302
|
+
|
|
303
|
+
request(`${this.apiBaseUrl}/extensions/create`, {
|
|
304
|
+
method: 'POST',
|
|
305
|
+
headers: {
|
|
306
|
+
Authorization: `Bearer ${this.accessToken}`,
|
|
307
|
+
'user-agent': this.userAgent,
|
|
308
|
+
'x-two-factor-token': this.twoFaKey || '',
|
|
309
|
+
},
|
|
310
|
+
body: {
|
|
311
|
+
extensionsInfo: extensionsToUpdate,
|
|
312
|
+
},
|
|
313
|
+
json: true,
|
|
314
|
+
});
|
|
251
315
|
}
|
|
252
316
|
|
|
253
317
|
getExtensionsToInstall(extensionsFromPref, extensionsFromDB) {
|
|
@@ -330,15 +394,6 @@ const getExtVersion = (metadata) => {
|
|
|
330
394
|
return splitExtName.join('_');
|
|
331
395
|
};
|
|
332
396
|
|
|
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
397
|
module.exports = ExtensionsManager;
|
|
343
398
|
|
|
344
399
|
|
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,27 @@ 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().catch((error) => {
|
|
430
|
+
console.log('checkUserChromeExtensions error: ', error);
|
|
431
|
+
return null;
|
|
432
|
+
}),
|
|
433
|
+
];
|
|
434
|
+
const extensionsResult = await Promise.all(promises);
|
|
435
|
+
|
|
436
|
+
const profileExtensionsPathRes = extensionsResult.find(el => 'profileExtensionsCheckRes' in el) || {};
|
|
437
|
+
profileExtensionsCheckRes = profileExtensionsPathRes.profileExtensionsCheckRes;
|
|
423
438
|
}
|
|
424
439
|
|
|
425
440
|
let extSettings;
|
|
426
441
|
if (ExtensionsManagerInst.useLocalExtStorage) {
|
|
427
|
-
extSettings = BrowserUserDataManager.setExtPathsAndRemoveDeleted(preferences, profileExtensionsCheckRes);
|
|
442
|
+
extSettings = await BrowserUserDataManager.setExtPathsAndRemoveDeleted(preferences, profileExtensionsCheckRes, this.profile_id);
|
|
428
443
|
} else {
|
|
429
444
|
const originalExtensionsFolder = path.join(profilePath, 'Default', 'Extensions');
|
|
430
445
|
extSettings = await BrowserUserDataManager.setOriginalExtPaths(preferences, originalExtensionsFolder);
|
|
@@ -484,6 +499,7 @@ class GoLogin {
|
|
|
484
499
|
};
|
|
485
500
|
profile.geoLocation = this.getGeolocationParams(profileGeolocation, tzGeoLocation);
|
|
486
501
|
profile.name = name;
|
|
502
|
+
profile.profile_id = this.profile_id;
|
|
487
503
|
|
|
488
504
|
profile.webRtc = {
|
|
489
505
|
mode: _.get(profile, 'webRTC.mode') === 'alerted' ? 'public' : _.get(profile, 'webRTC.mode'),
|
|
@@ -820,7 +836,7 @@ class GoLogin {
|
|
|
820
836
|
if (Array.isArray(this.extra_params) && this.extra_params.length) {
|
|
821
837
|
params = params.concat(this.extra_params);
|
|
822
838
|
}
|
|
823
|
-
|
|
839
|
+
console.log(params)
|
|
824
840
|
const child = execFile(ORBITA_BROWSER, params, {env});
|
|
825
841
|
// const child = spawn(ORBITA_BROWSER, params, { env, shell: true });
|
|
826
842
|
child.stdout.on('data', (data) => debug(data.toString()));
|
|
@@ -1045,11 +1061,11 @@ class GoLogin {
|
|
|
1045
1061
|
});
|
|
1046
1062
|
|
|
1047
1063
|
if (response.body.statusCode === 400) {
|
|
1048
|
-
throw new Error(`gologin failed account creation with status code, ${
|
|
1064
|
+
throw new Error(`gologin failed account creation with status code, ${response.statusCode} DATA ${JSON.stringify(response.body.message)}`);
|
|
1049
1065
|
}
|
|
1050
1066
|
|
|
1051
1067
|
if (response.body.statusCode === 500) {
|
|
1052
|
-
throw new Error(`gologin failed account creation with status code, ${
|
|
1068
|
+
throw new Error(`gologin failed account creation with status code, ${response.statusCode}`);
|
|
1053
1069
|
}
|
|
1054
1070
|
debug(JSON.stringify(response.body));
|
|
1055
1071
|
return response.body.id;
|
package/package.json
CHANGED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
_id,name,notes,proxy,os,isM1,timezone.enabled,timezone.fillBasedOnIp,timezone.timezone,geolocation.accuracy,geolocation.customize,geolocation.enabled,geolocation.fillBasedOnIp,geolocation.latitude,geolocation.longitude,geolocation.mode,cookies,webGLMetadata.renderer,webGLMetadata.vendor,proxy.autoProxyRegion,proxy.torProxyRegion,audioContext.mode,audioContext.noise,browserType,canvas.mode,canvas.noise,chromeExtensions,clientRects.mode,clientRects.noise,devicePixelRatio,dns,extensions.enabled,extensions.preloadCustom,fonts.enableDomRect,fonts.enableMasking,fonts.families,googleServicesEnabled,lockEnabled,mediaDevices.audioInputs,mediaDevices.audioOutputs,mediaDevices.enableMasking,mediaDevices.uid,mediaDevices.videoInputs,navigator.deviceMemory,navigator.hardwareConcurrency,navigator.language,navigator.maxTouchPoints,navigator.platform,navigator.resolution,navigator.userAgent,plugins.enableFlash,plugins.enableVulnerable,proxyEnabled,startUrl,storage.bookmarks,storage.extensions,storage.history,storage.local,storage.passwords,storage.session,updateUALastChosenBrowserV,userChromeExtensions,webGL.getClientRectsNoise,webGL.mode,webGL.noise,webGLMetadata.mode,webRTC.customize,webRTC.enabled,webRTC.fillBasedOnIp,webRTC.localIpMasking,webRTC.localIps,webRTC.mode,webRTC.publicIp,webglParams.antialiasing,webglParams.extensions,webglParams.glCanvas,webglParams.glParamValues,webglParams.shaiderPrecisionFormat,webglParams.supportedFunctions,webglParams.textureMaxAnisotropyExt
|
|
2
|
+
"62ceac1ebf5c65e7b9737e90",divine-moon,,gologin://us,lin,,true,true,,10,true,true,true,0,0,prompt,"[{""sameSite"":""unspecified"",""url"":""http://accounts.youtube.com/accounts"",""domain"":""accounts.youtube.com"",""name"":""CheckConnectionTempCookie418"",""value"":""583900"",""path"":""/accounts"",""secure"":false,""httpOnly"":false,""hostOnly"":true,""session"":false,""expirationDate"":1657711797},{""sameSite"":""no_restriction"",""url"":""https://google.com/"",""domain"":"".google.com"",""name"":""NID"",""value"":""511=pmrVWL6S6C04qFVhNbPwFk7VuJS3Q4GIbuYcwLeIBx8qxFPkraAGDr89L_9v1OxfCdvukGBBXLZzQ-Zx17qqxmKesRDAwk91GzT2uXWM6n_62DBXD0K5Dt2vBPtnVV15UA-AkE1ujldxEDI5FJjzN6RJ_VLYK3YaJax_Drdhp_I"",""path"":""/"",""secure"":true,""httpOnly"":true,""hostOnly"":false,""session"":false,""expirationDate"":1673522989.3169844},{""sameSite"":""unspecified"",""url"":""https://accounts.google.com/"",""domain"":""accounts.google.com"",""name"":""__Host-GAPS"",""value"":""1:XwbHhqGMbhJNRMbV1-WjwIE3WM3iqA:SGVT-tDrpcVPTnu3"",""path"":""/"",""secure"":true,""httpOnly"":true,""hostOnly"":true,""session"":false,""expirationDate"":1720783788.086744}]",,Google Inc.,us,us,noise,2.992842533846e-8,chrome,off,0.3554128,[],noise,5.40273,1.34375,,true,true,true,true,"[""AIGDT"",""AMGDT"",""Alef"",""Ani"",""AnjaliOldLipi"",""Caladea"",""Chilanka"",""David Libre"",""DejaVu Sans"",""DejaVu Sans Condensed"",""DejaVu Sans Light"",""DejaVu Serif"",""Droid Sans"",""Frank Ruehl"",""Frank Ruehl Libre"",""Frank Ruehl Libre Black"",""Frank Ruehl Libre Light"",""FreeMono"",""FreeSans"",""FreeSerif"",""Gargi"",""Gubbi"",""Jamrul"",""KacstBook"",""KacstLetter"",""KacstNaskh"",""Kalapi"",""Kalimati"",""Karumbi"",""Khmer OS"",""Khmer UI"",""Kinnari"",""Liberation Mono"",""Liberation Sans"",""Liberation Sans Narrow"",""Liberation Serif"",""Lohit Devanagari"",""Loma"",""Meera"",""Meera Inimai"",""Miriam"",""Miriam Fixed"",""Miriam Libre"",""Mitra Mono"",""Mukti Narrow"",""Nakula"",""Navuli"",""Nimbus Roman"",""Nimbus Sans"",""Nimbus Sans Narrow"",""Norasi"",""Noto Sans"",""Noto Sans Arabic UI"",""Noto Sans CJK JP"",""Noto Sans CJK KR"",""Noto Sans CJK SC"",""Noto Sans CJK TC"",""Noto Sans Mono CJK HK"",""Noto Sans Mono CJK KR"",""Noto Sans Mono CJK SC"",""Noto Sans Mono CJK TC"",""Noto Serif"",""Noto Serif CJK JP"",""Noto Serif CJK KR"",""Noto Serif CJK SC"",""Noto Serif Georgian"",""Oswald"",""Padauk"",""Pagul"",""Phetsarath OT"",""Pothana2000"",""Purisa"",""Roboto Light"",""Roboto Medium"",""Roboto Thin"",""Rubik"",""Sahadeva"",""Samyak Devanagari"",""Samyak Gujarati"",""Source Code Pro"",""Source Code Pro Black"",""Source Code Pro Extra Light"",""Source Code Pro Light"",""Source Code Pro Medium"",""Source Code Pro Semibold"",""Source Sans Pro"",""Source Sans Pro Black"",""Source Sans Pro Extra Light"",""Source Sans Pro Semibold"",""Source Serif Pro"",""Source Serif Pro Light"",""Source Serif Pro Semibold"",""Suruma"",""Tibetan Machine Uni"",""Tlwg Mono"",""Tlwg Typist"",""URW Gothic L"",""Uroob"",""Vemana2000""]",false,false,1,1,true,b04187cb002a40aeb4382c7c2d9cc3ccb395aee9613241af81df212fd0,0,8,8,"en-US,en;q=0.9",0,Linux x86_64,1600x900,"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.60 Safari/537.36",true,true,true,https://iphey.com,true,true,true,true,true,true,,[],5.40273,noise,35.999,mask,true,true,true,true,[],alerted,,false,"[""ANGLE_instanced_arrays"",""EXT_blend_minmax"",""EXT_color_buffer_half_float"",""EXT_float_blend"",""EXT_frag_depth"",""EXT_sRGB"",""EXT_shader_texture_lod"",""EXT_texture_compression_rgtc"",""EXT_texture_filter_anisotropic"",""KHR_parallel_shader_compile"",""OES_element_index_uint"",""OES_fbo_render_mipmap"",""OES_standard_derivatives"",""OES_texture_float"",""OES_texture_float_linear"",""OES_texture_half_float"",""OES_texture_half_float_linear"",""OES_vertex_array_object"",""WEBGL_color_buffer_float"",""WEBGL_compressed_texture_astc"",""WEBGL_compressed_texture_s3tc"",""WEBGL_compressed_texture_s3tc_srgb"",""WEBGL_debug_renderer_info"",""WEBGL_debug_shaders"",""WEBGL_depth_texture"",""WEBGL_draw_buffers"",""WEBGL_lose_context"",""WEBGL_multi_draw"",""WEBKIT_EXT_texture_filter_anisotropic"",""WEBKIT_WEBGL_compressed_texture_s3tc"",""WEBKIT_WEBGL_depth_texture"",""WEBKIT_WEBGL_lose_context""]",webgl,"[{""name"":""ALIASED_LINE_WIDTH_RANGE"",""value"":{""0"":1,""1"":7}},{""name"":""ALIASED_POINT_SIZE_RANGE"",""value"":{""0"":1,""1"":255}},{""name"":[""DEPTH_BITS"",""STENCIL_BITS""],""value"":""n/a""},{""name"":""MAX_3D_TEXTURE_SIZE"",""value"":""n/a""},{""name"":""MAX_ARRAY_TEXTURE_LAYERS"",""value"":""n/a""},{""name"":""MAX_COLOR_ATTACHMENTS"",""value"":""n/a""},{""name"":""MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS"",""value"":""n/a""},{""name"":""MAX_COMBINED_TEXTURE_IMAGE_UNITS"",""value"":32},{""name"":""MAX_COMBINED_UNIFORM_BLOCKS"",""value"":""n/a""},{""name"":""MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS"",""value"":""n/a""},{""name"":""MAX_CUBE_MAP_TEXTURE_SIZE"",""value"":8192},{""name"":""MAX_DRAW_BUFFERS"",""value"":""n/a""},{""name"":""MAX_FRAGMENT_INPUT_COMPONENTS"",""value"":""n/a""},{""name"":""MAX_FRAGMENT_UNIFORM_BLOCKS"",""value"":""n/a""},{""name"":""MAX_FRAGMENT_UNIFORM_COMPONENTS"",""value"":""n/a""},{""name"":""MAX_FRAGMENT_UNIFORM_VECTORS"",""value"":1024},{""name"":""MAX_PROGRAM_TEXEL_OFFSET"",""value"":""n/a""},{""name"":""MAX_RENDERBUFFER_SIZE"",""value"":8192},{""name"":""MAX_SAMPLES"",""value"":""n/a""},{""name"":""MAX_TEXTURE_IMAGE_UNITS"",""value"":16},{""name"":""MAX_TEXTURE_LOD_BIAS"",""value"":""n/a""},{""name"":""MAX_TEXTURE_SIZE"",""value"":8192},{""name"":""MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS"",""value"":""n/a""},{""name"":""MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS"",""value"":""n/a""},{""name"":""MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS"",""value"":""n/a""},{""name"":""MAX_UNIFORM_BLOCK_SIZE"",""value"":""n/a""},{""name"":""MAX_UNIFORM_BUFFER_BINDINGS"",""value"":""n/a""},{""name"":""MAX_VARYING_COMPONENTS"",""value"":""n/a""},{""name"":""MAX_VARYING_VECTORS"",""value"":16},{""name"":""MAX_VERTEX_ATTRIBS"",""value"":16},{""name"":""MAX_VERTEX_OUTPUT_COMPONENTS"",""value"":""n/a""},{""name"":""MAX_VERTEX_TEXTURE_IMAGE_UNITS"",""value"":16},{""name"":""MAX_VERTEX_UNIFORM_BLOCKS"",""value"":""n/a""},{""name"":""MAX_VERTEX_UNIFORM_COMPONENTS"",""value"":""n/a""},{""name"":""MAX_VERTEX_UNIFORM_VECTORS"",""value"":1024},{""name"":""MAX_VIEWPORT_DIMS"",""value"":{""0"":8192,""1"":8192}},{""name"":""MIN_PROGRAM_TEXEL_OFFSET"",""value"":""n/a""},{""name"":[""RED_BITS"",""GREEN_BITS"",""BLUE_BITS"",""ALPHA_BITS""],""value"":""n/a""},{""name"":""RENDERER"",""value"":""WebKit WebGL""},{""name"":""SHADING_LANGUAGE_VERSION"",""value"":""WebGL GLSL ES 1.0 (OpenGL ES GLSL ES 1.0 Chromium)""},{""name"":""UNIFORM_BUFFER_OFFSET_ALIGNMENT"",""value"":""n/a""},{""name"":""VENDOR"",""value"":""WebKit""},{""name"":""VERSION"",""value"":""WebGL 1.0 (OpenGL ES 2.0 Chromium)""}]",highp/highp,"[{""name"":""beginQuery"",""supported"":false},{""name"":""beginTransformFeedback"",""supported"":false},{""name"":""bindBufferBase"",""supported"":false},{""name"":""bindBufferRange"",""supported"":false},{""name"":""bindSampler"",""supported"":false},{""name"":""bindTransformFeedback"",""supported"":false},{""name"":""bindVertexArray"",""supported"":false},{""name"":""blitFramebuffer"",""supported"":false},{""name"":""clearBufferfi"",""supported"":false},{""name"":""clearBufferfv"",""supported"":false},{""name"":""clearBufferiv"",""supported"":false},{""name"":""clearBufferuiv"",""supported"":false},{""name"":""clientWaitSync"",""supported"":false},{""name"":""compressedTexImage3D"",""supported"":false},{""name"":""compressedTexSubImage3D"",""supported"":false},{""name"":""copyBufferSubData"",""supported"":false},{""name"":""copyTexSubImage3D"",""supported"":false},{""name"":""createQuery"",""supported"":false},{""name"":""createSampler"",""supported"":false},{""name"":""createTransformFeedback"",""supported"":false},{""name"":""createVertexArray"",""supported"":false},{""name"":""deleteQuery"",""supported"":false},{""name"":""deleteSampler"",""supported"":false},{""name"":""deleteSync"",""supported"":false},{""name"":""deleteTransformFeedback"",""supported"":false},{""name"":""deleteVertexArray"",""supported"":false},{""name"":""drawArraysInstanced"",""supported"":false},{""name"":""drawBuffers"",""supported"":false},{""name"":""drawElementsInstanced"",""supported"":false},{""name"":""drawRangeElements"",""supported"":false},{""name"":""endQuery"",""supported"":false},{""name"":""endTransformFeedback"",""supported"":false},{""name"":""fenceSync"",""supported"":false},{""name"":""framebufferTextureLayer"",""supported"":false},{""name"":""getActiveUniformBlockName"",""supported"":false},{""name"":""getActiveUniformBlockParameter"",""supported"":false},{""name"":""getActiveUniforms"",""supported"":false},{""name"":""getBufferSubData"",""supported"":false},{""name"":""getFragDataLocation"",""supported"":false},{""name"":""getIndexedParameter"",""supported"":false},{""name"":""getInternalformatParameter"",""supported"":false},{""name"":""getQuery"",""supported"":false},{""name"":""getQueryParameter"",""supported"":false},{""name"":""getSamplerParameter"",""supported"":false},{""name"":""getSyncParameter"",""supported"":false},{""name"":""getTransformFeedbackVarying"",""supported"":false},{""name"":""getUniformBlockIndex"",""supported"":false},{""name"":""getUniformIndices"",""supported"":false},{""name"":""invalidateFramebuffer"",""supported"":false},{""name"":""invalidateSubFramebuffer"",""supported"":false},{""name"":""isQuery"",""supported"":false},{""name"":""isSampler"",""supported"":false},{""name"":""isSync"",""supported"":false},{""name"":""isTransformFeedback"",""supported"":false},{""name"":""isVertexArray"",""supported"":false},{""name"":""pauseTransformFeedback"",""supported"":false},{""name"":""readBuffer"",""supported"":false},{""name"":""renderbufferStorageMultisample"",""supported"":false},{""name"":""resumeTransformFeedback"",""supported"":false},{""name"":""samplerParameterf"",""supported"":false},{""name"":""samplerParameteri"",""supported"":false},{""name"":""texImage3D"",""supported"":false},{""name"":""texStorage2D"",""supported"":false},{""name"":""texStorage3D"",""supported"":false},{""name"":""texSubImage3D"",""supported"":false},{""name"":""transformFeedbackVaryings"",""supported"":false},{""name"":""uniform1ui"",""supported"":false},{""name"":""uniform1uiv"",""supported"":false},{""name"":""uniform2ui"",""supported"":false},{""name"":""uniform2uiv"",""supported"":false},{""name"":""uniform3ui"",""supported"":false},{""name"":""uniform3uiv"",""supported"":false},{""name"":""uniform4ui"",""supported"":false},{""name"":""uniform4uiv"",""supported"":false},{""name"":""uniformBlockBinding"",""supported"":false},{""name"":""uniformMatrix2x3fv"",""supported"":false},{""name"":""uniformMatrix2x4fv"",""supported"":false},{""name"":""uniformMatrix3x2fv"",""supported"":false},{""name"":""uniformMatrix3x4fv"",""supported"":false},{""name"":""uniformMatrix4x2fv"",""supported"":false},{""name"":""uniformMatrix4x3fv"",""supported"":false},{""name"":""vertexAttribDivisor"",""supported"":false},{""name"":""vertexAttribI4i"",""supported"":false},{""name"":""vertexAttribI4iv"",""supported"":false},{""name"":""vertexAttribI4ui"",""supported"":false},{""name"":""vertexAttribI4uiv"",""supported"":false},{""name"":""vertexAttribIPointer"",""supported"":false},{""name"":""waitSync"",""supported"":false}]",16
|
|
@@ -0,0 +1,368 @@
|
|
|
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 () => {
|
|
198
|
+
const extensionsToDownloadPaths = await request.post(`${this.#API_BASE_URL}/extensions/user_chrome_extensions_paths`, {
|
|
199
|
+
json: true,
|
|
200
|
+
fullResponse: false,
|
|
201
|
+
headers: {
|
|
202
|
+
Authorization: `Bearer ${this.#ACCESS_TOKEN}`,
|
|
203
|
+
'user-agent': this.#USER_AGENT,
|
|
204
|
+
'x-two-factor-token': this.#TWO_FA_KEY || '',
|
|
205
|
+
},
|
|
206
|
+
body: {
|
|
207
|
+
existedUserChromeExtensions: this.#existedUserExtensions,
|
|
208
|
+
}
|
|
209
|
+
}) || [];
|
|
210
|
+
|
|
211
|
+
if (!extensionsToDownloadPaths.length) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const promises = extensionsToDownloadPaths.map(async awsPath => {
|
|
216
|
+
const [basePath] = awsPath.split('?');
|
|
217
|
+
const [extId] = basePath.split('/').reverse();
|
|
218
|
+
const zipPath = `${path.join(USER_EXTENSIONS_PATH, extId)}.zip`;
|
|
219
|
+
const archiveZip = fs.createWriteStream(zipPath);
|
|
220
|
+
|
|
221
|
+
await request(awsPath, {
|
|
222
|
+
retryDelay: 2 * 1000,
|
|
223
|
+
maxAttempts: 3,
|
|
224
|
+
}).pipe(archiveZip);
|
|
225
|
+
|
|
226
|
+
await new Promise(r => archiveZip.on('close', () => r()));
|
|
227
|
+
return zipPath;
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
const zipPaths = await Promise.all(promises).catch(() => []);
|
|
231
|
+
|
|
232
|
+
if (!zipPaths) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const extractionPromises = composeExtractionPromises(zipPaths, USER_EXTENSIONS_PATH);
|
|
237
|
+
const isExtensionsExtracted = await Promise.all(extractionPromises).catch(() => 'error');
|
|
238
|
+
|
|
239
|
+
if (isExtensionsExtracted !== 'error') {
|
|
240
|
+
const [downloadedFolders] = zipPaths.map(archivePath => archivePath.split(path.sep).reverse());
|
|
241
|
+
this.#existedUserExtensions = [...this.#existedUserExtensions, ...downloadedFolders];
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async getExtensionsNameAndImage(extensionsIds, pathToExtensions) {
|
|
246
|
+
const isCheckLocalFiles = [CHROME_EXTENSIONS_PATH, USER_EXTENSIONS_PATH].includes(pathToExtensions);
|
|
247
|
+
const extensionFolderNames = await readdir(pathToExtensions).catch(() => {});
|
|
248
|
+
const filteredExtensionFolderNames = extensionFolderNames.filter(extensionFolder => extensionsIds.some(extensionId => !extensionFolder.includes('.zip') && extensionFolder.includes(extensionId)));
|
|
249
|
+
|
|
250
|
+
if (!filteredExtensionFolderNames.length) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const namesPromise = extensionsIds.map(async (extensionsId) => {
|
|
255
|
+
const folderName = filteredExtensionFolderNames.find(folderName => folderName.includes(extensionsId));
|
|
256
|
+
|
|
257
|
+
if (!folderName) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
let pathToExtensionsFolder = [pathToExtensions, folderName];
|
|
262
|
+
if (!isCheckLocalFiles) {
|
|
263
|
+
const [extensionVersion] = await readdir(path.join(pathToExtensions, folderName));
|
|
264
|
+
pathToExtensionsFolder = [pathToExtensions, folderName, extensionVersion];
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const manifestPath = path.join(...pathToExtensionsFolder, 'manifest.json');
|
|
268
|
+
const manifestString = await readFile(manifestPath, 'utf8').catch(() => '');
|
|
269
|
+
if (!manifestString) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const manifestObject = JSON.parse(manifestString);
|
|
274
|
+
let name;
|
|
275
|
+
if (manifestObject.name.includes('__MSG')) {
|
|
276
|
+
const manifestName = manifestObject.name || '';
|
|
277
|
+
const fieldNameInLocale = manifestName.replace(/__/g, '').split('MSG_')[1];
|
|
278
|
+
const localePath = path.join(...pathToExtensionsFolder, '_locales', manifestObject.default_locale, 'messages.json');
|
|
279
|
+
const localeString = await readFile(localePath, 'utf8').catch(() => {});
|
|
280
|
+
|
|
281
|
+
try {
|
|
282
|
+
const parsedLocale = JSON.parse(localeString.trim());
|
|
283
|
+
name = parsedLocale[fieldNameInLocale].message;
|
|
284
|
+
} catch (e) {}
|
|
285
|
+
} else {
|
|
286
|
+
name = manifestObject.name;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (!name) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const iconObject = manifestObject.icons;
|
|
294
|
+
let iconPath = manifestObject.browser_action?.default_icon;
|
|
295
|
+
if (iconObject) {
|
|
296
|
+
iconPath = iconObject['128'];
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
let iconBSON = '';
|
|
300
|
+
if (iconPath) {
|
|
301
|
+
const iconPathFull = path.join(...pathToExtensionsFolder, iconPath);
|
|
302
|
+
iconBSON = await readFile(iconPathFull, 'base64').catch(() => {});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
name,
|
|
307
|
+
extId: extensionsId,
|
|
308
|
+
iconBinary: iconBSON,
|
|
309
|
+
};
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
const extensionsArray = await Promise.all(namesPromise);
|
|
313
|
+
return extensionsArray.filter(Boolean);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
generateExtensionId() {
|
|
317
|
+
let result = '';
|
|
318
|
+
let extensionIdLength = 32;
|
|
319
|
+
const characters = 'abcdefghijklmnopqrstuvwxyz';
|
|
320
|
+
const charactersLength = characters.length;
|
|
321
|
+
while (extensionIdLength--) {
|
|
322
|
+
result += characters.charAt(Math.floor(Math.random() *
|
|
323
|
+
charactersLength));
|
|
324
|
+
}
|
|
325
|
+
return result;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const checkFileSizeSync = async (pathToFile) => {
|
|
330
|
+
try {
|
|
331
|
+
const [fileName] = pathToFile.split(path.sep).reverse();
|
|
332
|
+
if (fileName === '.DS_Store') {
|
|
333
|
+
return 0;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const fileStats = await stat(pathToFile);
|
|
337
|
+
if (!fileStats.isDirectory()) {
|
|
338
|
+
return fileStats.size;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const files = await readdir(pathToFile);
|
|
342
|
+
const promises = files.map(async file => checkFileSizeSync(path.join(pathToFile, file)));
|
|
343
|
+
|
|
344
|
+
return (await Promise.all(promises)).reduce((result, value) => result + value, 0);
|
|
345
|
+
} catch {
|
|
346
|
+
return -1;
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
const copyFolder = async (fromPath, destPath) => {
|
|
351
|
+
const stats = await stat(fromPath);
|
|
352
|
+
|
|
353
|
+
if (!stats.isDirectory()) {
|
|
354
|
+
return copyFile(fromPath, destPath);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
await mkdir(destPath, { recursive: true }).catch(() => null);
|
|
358
|
+
const files = await readdir(fromPath);
|
|
359
|
+
const promises = files.map(async file => {
|
|
360
|
+
await mkdir(destPath, { recursive: true }).catch(() => null);
|
|
361
|
+
|
|
362
|
+
return copyFolder(path.join(fromPath, file), path.join(destPath, file));
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
return Promise.all(promises);
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
module.exports = UserExtensionsManager;
|