gologin 2.1.12 → 2.1.14
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/package.json +1 -1
- package/src/gologin.js +35 -5
- package/.sentry-native/15e25388-76f1-4a90-cea4-9a33e95e5be5.run/af276dd3-14b9-4b32-df19-69cb16a9a85e.envelope +0 -5
- package/.sentry-native/15e25388-76f1-4a90-cea4-9a33e95e5be5.run.lock +0 -0
- package/.sentry-native/last_crash +0 -1
- package/gologin/.eslintrc.json +0 -290
- package/gologin/README.md +0 -163
- package/gologin/example.js +0 -36
- package/gologin/examples/example-amazon-cloud-browser.js +0 -37
- package/gologin/examples/example-amazon-headless.js +0 -50
- package/gologin/examples/example-amazon.js +0 -47
- package/gologin/examples/example-create-custom-profile.js +0 -39
- package/gologin/examples/example-create-profile.js +0 -40
- package/gologin/examples/example-custom-args.js +0 -34
- package/gologin/examples/example-fast-profile-settings.js +0 -69
- package/gologin/examples/example-gmail.js +0 -67
- package/gologin/examples/example-iphey.js +0 -17
- package/gologin/examples/example-local-profile.js +0 -26
- package/gologin/examples/example-login-walmart.js +0 -35
- package/gologin/examples/example-stopremote.js +0 -20
- package/gologin/examples/example-timezone.js +0 -44
- package/gologin/fonts.js +0 -4293
- package/gologin/fonts_config +0 -104
- package/gologin/gologin-browser-ext.zip +0 -0
- package/gologin/gologin_zeroprofile.b64 +0 -1
- package/gologin/index.d.ts +0 -61
- package/gologin/package.json +0 -49
- package/gologin/profile_export_example.csv +0 -2
- package/gologin/run.sh +0 -1
- package/gologin/src/bookmarks/utils.js +0 -15
- package/gologin/src/browser/browser-api.js +0 -95
- package/gologin/src/browser/browser-checker.js +0 -392
- package/gologin/src/browser/browser-user-data-manager.js +0 -335
- package/gologin/src/cookies/cookies-manager.js +0 -189
- package/gologin/src/extensions/extensions-extractor.js +0 -56
- package/gologin/src/extensions/extensions-manager.js +0 -384
- package/gologin/src/extensions/user-extensions-manager.js +0 -295
- package/gologin/src/gologin-api.js +0 -110
- package/gologin/src/gologin.js +0 -1472
- package/gologin/src/profile/profile-archiver.js +0 -86
- package/gologin/src/profile/profile-directories-to-remove.js +0 -75
- package/gologin/src/utils/browser.js +0 -62
- package/gologin/src/utils/common.js +0 -76
- package/gologin/src/utils/constants.js +0 -1
- package/gologin/src/utils/utils.js +0 -49
- package/gologin/zero_profile.zip +0 -0
- package/gologin-canvas.zip +0 -0
- package/gologin.zip +0 -0
- package/test.html +0 -1
|
@@ -1,384 +0,0 @@
|
|
|
1
|
-
import { createWriteStream, promises as _promises } from 'fs';
|
|
2
|
-
import { join, sep } from 'path';
|
|
3
|
-
import request from 'requestretry';
|
|
4
|
-
|
|
5
|
-
import { CHROME_EXTENSIONS_PATH, composeExtractionPromises, USER_EXTENSIONS_PATH } from '../utils/common.js';
|
|
6
|
-
import UserExtensionsManager from './user-extensions-manager.js';
|
|
7
|
-
|
|
8
|
-
const { mkdir, readdir, rmdir, unlink } = _promises;
|
|
9
|
-
|
|
10
|
-
const EXTENSION_URL =
|
|
11
|
-
'https://clients2.google.com/service/update2/crx?response=redirect&acceptformat=crx2,crx3&x=id%3D{ext_id}%26uc&prodversion=97.0.4692.71';
|
|
12
|
-
|
|
13
|
-
export class ExtensionsManager extends UserExtensionsManager {
|
|
14
|
-
#existedChromeExtensions = [];
|
|
15
|
-
#inited = false;
|
|
16
|
-
#useLocalExtStorage = false;
|
|
17
|
-
#useCookiesExt = false;
|
|
18
|
-
#deleteProfileExtFolders = false;
|
|
19
|
-
#extensionsUpdating = true;
|
|
20
|
-
|
|
21
|
-
constructor() {
|
|
22
|
-
super();
|
|
23
|
-
if (!ExtensionsManager.instance) {
|
|
24
|
-
ExtensionsManager.instance = this;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return ExtensionsManager.instance;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async init() {
|
|
31
|
-
if (this.#inited) {
|
|
32
|
-
return Promise.resolve();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const promises = [
|
|
36
|
-
mkdir(CHROME_EXTENSIONS_PATH, { recursive: true })
|
|
37
|
-
.then(() => readdir(CHROME_EXTENSIONS_PATH))
|
|
38
|
-
.then(filesList => {
|
|
39
|
-
this.#existedChromeExtensions = filesList.filter(extPath => !extPath.includes('.zip'));
|
|
40
|
-
|
|
41
|
-
return filesList.map(fileName => fileName.includes('.zip') ?
|
|
42
|
-
unlink(join(CHROME_EXTENSIONS_PATH, fileName)) :
|
|
43
|
-
Promise.resolve());
|
|
44
|
-
})
|
|
45
|
-
.then(promisesToDelete => Promise.all(promisesToDelete))
|
|
46
|
-
.catch((e) => console.log('ExtensionsManager init error:', e)),
|
|
47
|
-
mkdir(USER_EXTENSIONS_PATH, { recursive: true })
|
|
48
|
-
.then(() => readdir(USER_EXTENSIONS_PATH))
|
|
49
|
-
.then(filesList => {
|
|
50
|
-
this.existedUserExtensions = filesList.filter(extPath => !extPath.includes('.zip'));
|
|
51
|
-
|
|
52
|
-
return filesList.map(fileName => fileName.includes('.zip') ?
|
|
53
|
-
unlink(join(USER_EXTENSIONS_PATH, fileName)) :
|
|
54
|
-
Promise.resolve());
|
|
55
|
-
})
|
|
56
|
-
.then((promisesToDelete) => Promise.all(promisesToDelete))
|
|
57
|
-
.catch((e) => console.log('error creating user extensions folder:', e)),
|
|
58
|
-
];
|
|
59
|
-
|
|
60
|
-
return Promise.all(promises).then(() => this.#inited = true);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
get isInited() {
|
|
64
|
-
return this.#inited;
|
|
65
|
-
}
|
|
66
|
-
get useLocalExtStorage() {
|
|
67
|
-
return this.#useLocalExtStorage;
|
|
68
|
-
}
|
|
69
|
-
get deleteProfileExtFolders() {
|
|
70
|
-
return this.#deleteProfileExtFolders;
|
|
71
|
-
}
|
|
72
|
-
get useCookiesExt() {
|
|
73
|
-
return this.#useCookiesExt;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
get existedChromeExtensionsList() {
|
|
77
|
-
return this.#existedChromeExtensions;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
async checkChromeExtensions(profileExtensions = []) {
|
|
81
|
-
if (!(Array.isArray(profileExtensions) && profileExtensions.length)) {
|
|
82
|
-
return [];
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const extensionsToDownload = this.#getExtensionsToDownload(profileExtensions);
|
|
86
|
-
|
|
87
|
-
const downloadedArchives = await this.downloadChromeExtensions(extensionsToDownload);
|
|
88
|
-
const filteredArchives = downloadedArchives.filter(Boolean);
|
|
89
|
-
|
|
90
|
-
if (filteredArchives.length) {
|
|
91
|
-
const [downloadedFolders] = filteredArchives.map(archivePath => archivePath.split(sep).reverse());
|
|
92
|
-
this.#existedChromeExtensions = [...this.#existedChromeExtensions, ...downloadedFolders];
|
|
93
|
-
|
|
94
|
-
const promises = composeExtractionPromises(filteredArchives);
|
|
95
|
-
|
|
96
|
-
await Promise.all(promises);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return this.getExtensionsStrToIncludeAsOrbitaParam(profileExtensions);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
#getExtensionsToDownload(profileExtensions) {
|
|
103
|
-
const existedExtensionsFolders = [...this.#existedChromeExtensions, ...this.existedUserExtensions];
|
|
104
|
-
const existedOriginalIds = existedExtensionsFolders.map((val) => {
|
|
105
|
-
const [originalId] = val.split('@');
|
|
106
|
-
|
|
107
|
-
return originalId;
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
return profileExtensions.reduce((res, val) => {
|
|
111
|
-
const [originalId] = val.split('@');
|
|
112
|
-
const extensionExists = existedOriginalIds.includes(originalId);
|
|
113
|
-
if (!extensionExists) {
|
|
114
|
-
res.push(val);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return res;
|
|
118
|
-
}, []);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async downloadChromeExtensions(idsToDownload = []) {
|
|
122
|
-
if (!(Array.isArray(idsToDownload) && idsToDownload.length)) {
|
|
123
|
-
return [];
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const promises = idsToDownload.map(async (id) => {
|
|
127
|
-
const [originalId] = id.split('@');
|
|
128
|
-
const extUrl = EXTENSION_URL.replace('{ext_id}', originalId);
|
|
129
|
-
|
|
130
|
-
const uploadedProfileMetadata = await getExtMetadata(extUrl);
|
|
131
|
-
|
|
132
|
-
const reqPath = uploadedProfileMetadata.req.path;
|
|
133
|
-
const extVer = getExtVersion(reqPath);
|
|
134
|
-
|
|
135
|
-
const buffer = await new Promise((res) => {
|
|
136
|
-
const chunks = [];
|
|
137
|
-
request.get(extUrl, {
|
|
138
|
-
maxAttempts: 3,
|
|
139
|
-
retryDelay: 1000,
|
|
140
|
-
timeout: 8 * 1000,
|
|
141
|
-
fullResponse: false,
|
|
142
|
-
})
|
|
143
|
-
.on('data', (data) => chunks.push(data))
|
|
144
|
-
.on('end', () => res(Buffer.concat(chunks)));
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
let zipExt;
|
|
148
|
-
try {
|
|
149
|
-
zipExt = crxToZip(buffer);
|
|
150
|
-
} catch (e) {
|
|
151
|
-
console.log(e);
|
|
152
|
-
|
|
153
|
-
return '';
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const archiveZipPath = join(CHROME_EXTENSIONS_PATH, originalId + '@' + extVer + '.zip');
|
|
157
|
-
|
|
158
|
-
const archiveZip = createWriteStream(archiveZipPath);
|
|
159
|
-
archiveZip.write(zipExt);
|
|
160
|
-
archiveZip.close();
|
|
161
|
-
|
|
162
|
-
return new Promise(r => archiveZip.on('close', () => r(archiveZipPath)));
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
return Promise.all(promises);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
async getExtensionsPolicies() {
|
|
169
|
-
const globalExtConfig = await request.get(`${this.apiBaseUrl}/gologin-settings/chrome_ext_policies`, {
|
|
170
|
-
headers: {
|
|
171
|
-
Authorization: `Bearer ${this.accessToken}`,
|
|
172
|
-
'user-agent': this.userAgent,
|
|
173
|
-
'x-two-factor-token': this.twoFaKey || '',
|
|
174
|
-
},
|
|
175
|
-
json: true,
|
|
176
|
-
maxAttempts: 2,
|
|
177
|
-
retryDelay: 1000,
|
|
178
|
-
timeout: 10 * 1000,
|
|
179
|
-
fullResponse: false,
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
const chromeExtPolicies = globalExtConfig?.chromeExtPolicies || {};
|
|
183
|
-
const {
|
|
184
|
-
useLocalExtStorage = false,
|
|
185
|
-
deleteProfileExtFolders = false,
|
|
186
|
-
useCookiesExt = true,
|
|
187
|
-
} = chromeExtPolicies;
|
|
188
|
-
|
|
189
|
-
this.#useLocalExtStorage = useLocalExtStorage;
|
|
190
|
-
this.#deleteProfileExtFolders = deleteProfileExtFolders;
|
|
191
|
-
this.#useCookiesExt = useCookiesExt;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
async updateExtensions() {
|
|
195
|
-
const fileList = await readdir(CHROME_EXTENSIONS_PATH).catch(() => []);
|
|
196
|
-
if (!fileList.length) {
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
const oldFolders = [];
|
|
201
|
-
|
|
202
|
-
const versionCheckPromises = fileList.map(async (extension) => {
|
|
203
|
-
if (!extension.includes('@') || extension.includes('.zip')) {
|
|
204
|
-
return '';
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const [originalId, currentVersion] = extension.split('@');
|
|
208
|
-
const extUrl = EXTENSION_URL.replace('{ext_id}', originalId);
|
|
209
|
-
const uploadedProfileMetadata = await getExtMetadata(extUrl);
|
|
210
|
-
const reqPath = uploadedProfileMetadata.req.path;
|
|
211
|
-
const availableVersion = getExtVersion(reqPath);
|
|
212
|
-
|
|
213
|
-
if (currentVersion === availableVersion) {
|
|
214
|
-
return '';
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
oldFolders.push(join(CHROME_EXTENSIONS_PATH, extension));
|
|
218
|
-
|
|
219
|
-
return originalId;
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
const extensionsNames = (await Promise.all(versionCheckPromises)).filter(Boolean);
|
|
223
|
-
const archivesPaths = (await this.downloadChromeExtensions(extensionsNames)).filter(Boolean);
|
|
224
|
-
const extractionPromises = composeExtractionPromises(archivesPaths);
|
|
225
|
-
await Promise.all(extractionPromises);
|
|
226
|
-
|
|
227
|
-
const removeFoldersPromises = oldFolders.map(folder => (
|
|
228
|
-
rmdir(folder, { recursive: true, maxRetries: 3 }).catch(() => {})
|
|
229
|
-
));
|
|
230
|
-
|
|
231
|
-
await Promise.all(removeFoldersPromises).then(() => this.#extensionsUpdating = false);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
async checkLocalExtensions() {
|
|
235
|
-
if (this.#extensionsUpdating || !this.accessToken) {
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
const fileList = await readdir(CHROME_EXTENSIONS_PATH).catch(() => []);
|
|
240
|
-
if (!fileList.length) {
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const extensionsIds = fileList.filter(folderName => folderName.includes('@') && !folderName.includes('.zip'))
|
|
245
|
-
.map(folderName => {
|
|
246
|
-
const [name] = folderName.split('@');
|
|
247
|
-
|
|
248
|
-
return name;
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
if (!extensionsIds.length) {
|
|
252
|
-
return;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
this.insertExtensionsToDb(extensionsIds);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
async insertExtensionsToDb(extensionsIds, pathToExtensions = CHROME_EXTENSIONS_PATH) {
|
|
259
|
-
if (!extensionsIds?.length) {
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
const checkResponse = await request(`${this.apiBaseUrl}/extensions/check`, {
|
|
264
|
-
method: 'POST',
|
|
265
|
-
headers: {
|
|
266
|
-
Authorization: `Bearer ${this.accessToken}`,
|
|
267
|
-
'user-agent': this.userAgent,
|
|
268
|
-
'x-two-factor-token': this.twoFaKey || '',
|
|
269
|
-
},
|
|
270
|
-
body: {
|
|
271
|
-
extensionsIds,
|
|
272
|
-
},
|
|
273
|
-
json: true,
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
const { extensionsToAdd = [] } = checkResponse.body;
|
|
277
|
-
|
|
278
|
-
if (!extensionsToAdd.length) {
|
|
279
|
-
return;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
const extensionsToUpdate = await this.getExtensionsNameAndImage(extensionsToAdd, pathToExtensions);
|
|
283
|
-
|
|
284
|
-
request(`${this.apiBaseUrl}/extensions/create`, {
|
|
285
|
-
method: 'POST',
|
|
286
|
-
headers: {
|
|
287
|
-
Authorization: `Bearer ${this.accessToken}`,
|
|
288
|
-
'user-agent': this.userAgent,
|
|
289
|
-
'x-two-factor-token': this.twoFaKey || '',
|
|
290
|
-
},
|
|
291
|
-
body: {
|
|
292
|
-
extensionsInfo: extensionsToUpdate,
|
|
293
|
-
},
|
|
294
|
-
json: true,
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
getExtensionsToInstall(extensionsFromPref, extensionsFromDB) {
|
|
299
|
-
if (!extensionsFromPref) {
|
|
300
|
-
return [];
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const objectEntries = Object.entries(extensionsFromPref);
|
|
304
|
-
const extensionsInPref = objectEntries?.map(([_, settings]) => {
|
|
305
|
-
const [extFolderName] = settings.path.split(sep).reverse();
|
|
306
|
-
const [originalId] = extFolderName.split('@');
|
|
307
|
-
|
|
308
|
-
return originalId;
|
|
309
|
-
}) || [];
|
|
310
|
-
|
|
311
|
-
return extensionsFromDB.reduce((acc, extension) => {
|
|
312
|
-
const [extFolderName] = extension.split(sep).reverse();
|
|
313
|
-
const [originalId] = extFolderName.split('@');
|
|
314
|
-
if (!extensionsInPref.includes(originalId)) {
|
|
315
|
-
acc.push(extension);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
return acc;
|
|
319
|
-
}, []);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
const crxToZip = (buf) => {
|
|
324
|
-
if (buf[0] === 80 && buf[1] === 75 && buf[2] === 3 && buf[3] === 4) {
|
|
325
|
-
return buf;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
if (!(buf[0] === 67 || buf[1] === 114 || buf[2] === 50 || buf[3] === 52)) {
|
|
329
|
-
throw new Error('Invalid header: Does not start with Cr24');
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
const isV3 = buf[4] === 3;
|
|
333
|
-
const isV2 = buf[4] === 2;
|
|
334
|
-
|
|
335
|
-
if (!(isV2 || isV3) || buf[5] || buf[6] || buf[7]) {
|
|
336
|
-
throw new Error('Unexpected crx format version number.');
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
if (isV2) {
|
|
340
|
-
const publicKeyLength = calcLength(buf[8], buf[9], buf[10], buf[11]);
|
|
341
|
-
const signatureLength = calcLength(buf[12], buf[13], buf[14], buf[15]);
|
|
342
|
-
|
|
343
|
-
const zipStartOffset = 16 + publicKeyLength + signatureLength;
|
|
344
|
-
|
|
345
|
-
return buf.slice(zipStartOffset, buf.length);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
const headerSize = calcLength(buf[8], buf[9], buf[10], buf[11]);
|
|
349
|
-
const zipStartOffset = 12 + headerSize;
|
|
350
|
-
|
|
351
|
-
return buf.slice(zipStartOffset, buf.length);
|
|
352
|
-
};
|
|
353
|
-
|
|
354
|
-
const calcLength = (a, b, c, d) => {
|
|
355
|
-
let length = 0;
|
|
356
|
-
|
|
357
|
-
length += a << 0;
|
|
358
|
-
length += b << 8;
|
|
359
|
-
length += c << 16;
|
|
360
|
-
length += d << 24 >>> 0;
|
|
361
|
-
|
|
362
|
-
return length;
|
|
363
|
-
};
|
|
364
|
-
|
|
365
|
-
const getExtMetadata = (extUrl) => (
|
|
366
|
-
request.head(extUrl, {
|
|
367
|
-
maxAttempts: 3,
|
|
368
|
-
retryDelay: 2000,
|
|
369
|
-
timeout: 2 * 1000,
|
|
370
|
-
fullResponse: true,
|
|
371
|
-
})
|
|
372
|
-
);
|
|
373
|
-
|
|
374
|
-
const getExtVersion = (metadata) => {
|
|
375
|
-
const [extFullName = ''] = metadata.split('/').reverse();
|
|
376
|
-
const [extName = ''] = extFullName.split('.');
|
|
377
|
-
const splitExtName = extName.split('_');
|
|
378
|
-
splitExtName.shift();
|
|
379
|
-
|
|
380
|
-
return splitExtName.join('_');
|
|
381
|
-
};
|
|
382
|
-
|
|
383
|
-
export default ExtensionsManager;
|
|
384
|
-
|
|
@@ -1,295 +0,0 @@
|
|
|
1
|
-
import { createWriteStream, promises as _promises } from 'fs';
|
|
2
|
-
import { join, sep } from 'path';
|
|
3
|
-
import request from 'requestretry';
|
|
4
|
-
|
|
5
|
-
import { CHROME_EXTENSIONS_PATH, composeExtractionPromises, USER_EXTENSIONS_PATH } from '../utils/common.js';
|
|
6
|
-
|
|
7
|
-
const { readdir, readFile, stat, mkdir, copyFile } = _promises;
|
|
8
|
-
|
|
9
|
-
export class UserExtensionsManager {
|
|
10
|
-
#existedUserExtensions = [];
|
|
11
|
-
#API_BASE_URL = '';
|
|
12
|
-
#ACCESS_TOKEN = '';
|
|
13
|
-
#USER_AGENT = '';
|
|
14
|
-
#TWO_FA_KEY = '';
|
|
15
|
-
|
|
16
|
-
set userAgent(userAgent) {
|
|
17
|
-
if (!userAgent) {
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
this.#USER_AGENT = userAgent;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
set accessToken(accessToken) {
|
|
25
|
-
if (!accessToken) {
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
this.#ACCESS_TOKEN = accessToken;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
set twoFaKey(twoFaKey) {
|
|
33
|
-
if (!twoFaKey) {
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
this.#TWO_FA_KEY = twoFaKey;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
set apiUrl(apiUrl) {
|
|
41
|
-
if (!apiUrl) {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
this.#API_BASE_URL = apiUrl;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
get apiBaseUrl() {
|
|
49
|
-
return this.#API_BASE_URL;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
get existedUserExtensions() {
|
|
53
|
-
return this.#existedUserExtensions;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
get accessToken() {
|
|
57
|
-
return this.#ACCESS_TOKEN;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
get twoFaKey() {
|
|
61
|
-
return this.#TWO_FA_KEY;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
get userAgent() {
|
|
65
|
-
return this.#USER_AGENT;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
set existedUserExtensions(fileList) {
|
|
69
|
-
if (!fileList) {
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
this.#existedUserExtensions = fileList;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
checkLocalUserChromeExtensions = async (userChromeExtensions, profileId) => {
|
|
77
|
-
if (!userChromeExtensions.length) {
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const extensionsToDownloadPaths = await request.post(`${this.#API_BASE_URL}/extensions/user_chrome_extensions_paths`, {
|
|
82
|
-
json: true,
|
|
83
|
-
fullResponse: false,
|
|
84
|
-
headers: {
|
|
85
|
-
Authorization: `Bearer ${this.#ACCESS_TOKEN}`,
|
|
86
|
-
'user-agent': this.#USER_AGENT,
|
|
87
|
-
'x-two-factor-token': this.#TWO_FA_KEY || '',
|
|
88
|
-
},
|
|
89
|
-
body: {
|
|
90
|
-
existedUserChromeExtensions: this.#existedUserExtensions,
|
|
91
|
-
profileId,
|
|
92
|
-
userChromeExtensions,
|
|
93
|
-
},
|
|
94
|
-
}) || [];
|
|
95
|
-
|
|
96
|
-
const extensionsToDownloadPathsFiltered =
|
|
97
|
-
extensionsToDownloadPaths.filter(extPath => userChromeExtensions.some(extId => extPath.includes(extId)));
|
|
98
|
-
|
|
99
|
-
if (!extensionsToDownloadPathsFiltered.length) {
|
|
100
|
-
return this.getExtensionsStrToIncludeAsOrbitaParam(userChromeExtensions, USER_EXTENSIONS_PATH);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const promises = extensionsToDownloadPathsFiltered.map(async awsPath => {
|
|
104
|
-
const [basePath] = awsPath.split('?');
|
|
105
|
-
const [extId] = basePath.split('/').reverse();
|
|
106
|
-
const zipPath = `${join(USER_EXTENSIONS_PATH, extId)}.zip`;
|
|
107
|
-
const archiveZip = createWriteStream(zipPath);
|
|
108
|
-
|
|
109
|
-
await request(awsPath, {
|
|
110
|
-
retryDelay: 2 * 1000,
|
|
111
|
-
maxAttempts: 3,
|
|
112
|
-
}).pipe(archiveZip);
|
|
113
|
-
|
|
114
|
-
await new Promise(r => archiveZip.on('close', () => r()));
|
|
115
|
-
|
|
116
|
-
return zipPath;
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
const zipPaths = await Promise.all(promises).catch(() => []);
|
|
120
|
-
|
|
121
|
-
if (!zipPaths) {
|
|
122
|
-
return this.getExtensionsStrToIncludeAsOrbitaParam(userChromeExtensions, USER_EXTENSIONS_PATH);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const extractionPromises = composeExtractionPromises(zipPaths, USER_EXTENSIONS_PATH);
|
|
126
|
-
const isExtensionsExtracted = await Promise.all(extractionPromises).catch(() => 'error');
|
|
127
|
-
|
|
128
|
-
if (isExtensionsExtracted !== 'error') {
|
|
129
|
-
const [downloadedFolders] = zipPaths.map(archivePath => archivePath.split(sep).reverse());
|
|
130
|
-
this.#existedUserExtensions = [...this.#existedUserExtensions, ...downloadedFolders];
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return this.getExtensionsStrToIncludeAsOrbitaParam(userChromeExtensions, USER_EXTENSIONS_PATH);
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
async getExtensionsStrToIncludeAsOrbitaParam(profileExtensions = [], folderPath = CHROME_EXTENSIONS_PATH) {
|
|
137
|
-
if (!(Array.isArray(profileExtensions) && profileExtensions.length)) {
|
|
138
|
-
return [];
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const folders = await readdir(folderPath).then(folderNames => folderNames.map(folderName => join(folderPath, folderName)));
|
|
142
|
-
|
|
143
|
-
if (!folders.length) {
|
|
144
|
-
return [];
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const formattedIdsList = folders.map((el) => {
|
|
148
|
-
const [folderName] = el.split(sep).reverse();
|
|
149
|
-
const [originalId] = folderName.split('@');
|
|
150
|
-
|
|
151
|
-
return {
|
|
152
|
-
originalId,
|
|
153
|
-
path: el,
|
|
154
|
-
};
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
return profileExtensions.map((el) => {
|
|
158
|
-
const extExisted = formattedIdsList.find(chromeExtPathElem => chromeExtPathElem.originalId === el);
|
|
159
|
-
|
|
160
|
-
if (!extExisted) {
|
|
161
|
-
return '';
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return extExisted.path;
|
|
165
|
-
}).filter(Boolean);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
async getExtensionsNameAndImage(extensionsIds, pathToExtensions) {
|
|
169
|
-
const isCheckLocalFiles = [CHROME_EXTENSIONS_PATH, USER_EXTENSIONS_PATH].includes(pathToExtensions);
|
|
170
|
-
const extensionFolderNames = await readdir(pathToExtensions).catch(() => {});
|
|
171
|
-
const filteredExtensionFolderNames = extensionFolderNames.filter(extensionFolder => extensionsIds.some(extensionId => !extensionFolder.includes('.zip') && extensionFolder.includes(extensionId)));
|
|
172
|
-
|
|
173
|
-
if (!filteredExtensionFolderNames.length) {
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const namesPromise = extensionsIds.map(async (extensionsId) => {
|
|
178
|
-
const folderName = filteredExtensionFolderNames.find(folderName => folderName.includes(extensionsId));
|
|
179
|
-
|
|
180
|
-
if (!folderName) {
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
let pathToExtensionsFolder = [pathToExtensions, folderName];
|
|
185
|
-
if (!isCheckLocalFiles) {
|
|
186
|
-
const [extensionVersion] = await readdir(join(pathToExtensions, folderName));
|
|
187
|
-
pathToExtensionsFolder = [pathToExtensions, folderName, extensionVersion];
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const manifestPath = join(...pathToExtensionsFolder, 'manifest.json');
|
|
191
|
-
const manifestString = await readFile(manifestPath, 'utf8').catch(() => '');
|
|
192
|
-
if (!manifestString) {
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const manifestObject = JSON.parse(manifestString);
|
|
197
|
-
let name;
|
|
198
|
-
if (manifestObject.name.includes('__MSG')) {
|
|
199
|
-
const manifestName = manifestObject.name || '';
|
|
200
|
-
const fieldNameInLocale = manifestName.replace(/__/g, '').split('MSG_')[1];
|
|
201
|
-
const localePath = join(...pathToExtensionsFolder, '_locales', manifestObject.default_locale, 'messages.json');
|
|
202
|
-
const localeString = await readFile(localePath, 'utf8').catch(() => {});
|
|
203
|
-
|
|
204
|
-
try {
|
|
205
|
-
const parsedLocale = JSON.parse(localeString.trim());
|
|
206
|
-
name = parsedLocale[fieldNameInLocale].message;
|
|
207
|
-
} catch (e) {
|
|
208
|
-
console.log(e);
|
|
209
|
-
}
|
|
210
|
-
} else {
|
|
211
|
-
name = manifestObject.name;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (!name) {
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const iconObject = manifestObject.icons;
|
|
219
|
-
let iconPath = manifestObject.browser_action?.default_icon;
|
|
220
|
-
if (iconObject) {
|
|
221
|
-
iconPath = iconObject['128'];
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
let iconBSON = '';
|
|
225
|
-
if (iconPath) {
|
|
226
|
-
const iconPathFull = join(...pathToExtensionsFolder, iconPath);
|
|
227
|
-
iconBSON = await readFile(iconPathFull, 'base64').catch(() => {});
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return {
|
|
231
|
-
name,
|
|
232
|
-
extId: extensionsId,
|
|
233
|
-
iconBinary: iconBSON,
|
|
234
|
-
};
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
const extensionsArray = await Promise.all(namesPromise);
|
|
238
|
-
|
|
239
|
-
return extensionsArray.filter(Boolean);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
generateExtensionId() {
|
|
243
|
-
let result = '';
|
|
244
|
-
let extensionIdLength = 32;
|
|
245
|
-
const characters = 'abcdefghijklmnopqrstuvwxyz';
|
|
246
|
-
const charactersLength = characters.length;
|
|
247
|
-
while (extensionIdLength--) {
|
|
248
|
-
result += characters.charAt(Math.floor(Math.random() *
|
|
249
|
-
charactersLength));
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return result;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const checkFileSizeSync = async (pathToFile) => {
|
|
257
|
-
try {
|
|
258
|
-
const [fileName] = pathToFile.split(sep).reverse();
|
|
259
|
-
if (fileName === '.DS_Store') {
|
|
260
|
-
return 0;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
const fileStats = await stat(pathToFile);
|
|
264
|
-
if (!fileStats.isDirectory()) {
|
|
265
|
-
return fileStats.size;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
const files = await readdir(pathToFile);
|
|
269
|
-
const promises = files.map(async file => checkFileSizeSync(join(pathToFile, file)));
|
|
270
|
-
|
|
271
|
-
return (await Promise.all(promises)).reduce((result, value) => result + value, 0);
|
|
272
|
-
} catch {
|
|
273
|
-
return -1;
|
|
274
|
-
}
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
const copyFolder = async (fromPath, destPath) => {
|
|
278
|
-
const stats = await stat(fromPath);
|
|
279
|
-
|
|
280
|
-
if (!stats.isDirectory()) {
|
|
281
|
-
return copyFile(fromPath, destPath);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
await mkdir(destPath, { recursive: true }).catch(() => null);
|
|
285
|
-
const files = await readdir(fromPath);
|
|
286
|
-
const promises = files.map(async file => {
|
|
287
|
-
await mkdir(destPath, { recursive: true }).catch(() => null);
|
|
288
|
-
|
|
289
|
-
return copyFolder(join(fromPath, file), join(destPath, file));
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
return Promise.all(promises);
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
export default UserExtensionsManager;
|