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.
Files changed (49) hide show
  1. package/package.json +1 -1
  2. package/src/gologin.js +35 -5
  3. package/.sentry-native/15e25388-76f1-4a90-cea4-9a33e95e5be5.run/af276dd3-14b9-4b32-df19-69cb16a9a85e.envelope +0 -5
  4. package/.sentry-native/15e25388-76f1-4a90-cea4-9a33e95e5be5.run.lock +0 -0
  5. package/.sentry-native/last_crash +0 -1
  6. package/gologin/.eslintrc.json +0 -290
  7. package/gologin/README.md +0 -163
  8. package/gologin/example.js +0 -36
  9. package/gologin/examples/example-amazon-cloud-browser.js +0 -37
  10. package/gologin/examples/example-amazon-headless.js +0 -50
  11. package/gologin/examples/example-amazon.js +0 -47
  12. package/gologin/examples/example-create-custom-profile.js +0 -39
  13. package/gologin/examples/example-create-profile.js +0 -40
  14. package/gologin/examples/example-custom-args.js +0 -34
  15. package/gologin/examples/example-fast-profile-settings.js +0 -69
  16. package/gologin/examples/example-gmail.js +0 -67
  17. package/gologin/examples/example-iphey.js +0 -17
  18. package/gologin/examples/example-local-profile.js +0 -26
  19. package/gologin/examples/example-login-walmart.js +0 -35
  20. package/gologin/examples/example-stopremote.js +0 -20
  21. package/gologin/examples/example-timezone.js +0 -44
  22. package/gologin/fonts.js +0 -4293
  23. package/gologin/fonts_config +0 -104
  24. package/gologin/gologin-browser-ext.zip +0 -0
  25. package/gologin/gologin_zeroprofile.b64 +0 -1
  26. package/gologin/index.d.ts +0 -61
  27. package/gologin/package.json +0 -49
  28. package/gologin/profile_export_example.csv +0 -2
  29. package/gologin/run.sh +0 -1
  30. package/gologin/src/bookmarks/utils.js +0 -15
  31. package/gologin/src/browser/browser-api.js +0 -95
  32. package/gologin/src/browser/browser-checker.js +0 -392
  33. package/gologin/src/browser/browser-user-data-manager.js +0 -335
  34. package/gologin/src/cookies/cookies-manager.js +0 -189
  35. package/gologin/src/extensions/extensions-extractor.js +0 -56
  36. package/gologin/src/extensions/extensions-manager.js +0 -384
  37. package/gologin/src/extensions/user-extensions-manager.js +0 -295
  38. package/gologin/src/gologin-api.js +0 -110
  39. package/gologin/src/gologin.js +0 -1472
  40. package/gologin/src/profile/profile-archiver.js +0 -86
  41. package/gologin/src/profile/profile-directories-to-remove.js +0 -75
  42. package/gologin/src/utils/browser.js +0 -62
  43. package/gologin/src/utils/common.js +0 -76
  44. package/gologin/src/utils/constants.js +0 -1
  45. package/gologin/src/utils/utils.js +0 -49
  46. package/gologin/zero_profile.zip +0 -0
  47. package/gologin-canvas.zip +0 -0
  48. package/gologin.zip +0 -0
  49. package/test.html +0 -1
@@ -1,1472 +0,0 @@
1
- import { execFile, spawn } from 'child_process';
2
- import debugDefault from 'debug';
3
- import decompress from 'decompress';
4
- import decompressUnzip from 'decompress-unzip';
5
- import { existsSync, mkdirSync,promises as _promises } from 'fs';
6
- import { get as _get } from 'https';
7
- import { tmpdir } from 'os';
8
- import { dirname, join, resolve as _resolve, sep } from 'path';
9
- import requests from 'requestretry';
10
- import rimraf from 'rimraf';
11
- import { SocksProxyAgent } from 'socks-proxy-agent';
12
-
13
- import { fontsCollection } from '../fonts.js';
14
- import { getCurrentProfileBookmarks } from './bookmarks/utils.js';
15
- import { updateProfileBookmarks, updateProfileProxy, updateProfileResolution, updateProfileUserAgent } from './browser/browser-api.js';
16
- import BrowserChecker from './browser/browser-checker.js';
17
- import {
18
- composeFonts, downloadCookies, setExtPathsAndRemoveDeleted, setOriginalExtPaths, uploadCookies,
19
- } from './browser/browser-user-data-manager.js';
20
- import {
21
- getChunckedInsertValues,
22
- getCookiesFilePath,
23
- getDB,
24
- loadCookiesFromFile,
25
- } from './cookies/cookies-manager.js';
26
- import ExtensionsManager from './extensions/extensions-manager.js';
27
- import { archiveProfile } from './profile/profile-archiver.js';
28
- import { checkAutoLang } from './utils/browser.js';
29
- import { API_URL, getOsAdvanced } from './utils/common.js';
30
- import { STORAGE_GATEWAY_BASE_URL } from './utils/constants.js';
31
- import { get, isPortReachable } from './utils/utils.js';
32
- export { exitAll, GologinApi } from './gologin-api.js';
33
-
34
- const { access, unlink, writeFile, readFile } = _promises;
35
-
36
- const SEPARATOR = sep;
37
- const OS_PLATFORM = process.platform;
38
- const TIMEZONE_URL = 'https://geo.myip.link';
39
- const PROXY_NONE = 'none';
40
-
41
- const debug = debugDefault('gologin');
42
- const delay = (time) => new Promise((resolve) => setTimeout(resolve, time));
43
-
44
- export class GoLogin {
45
- constructor(options = {}) {
46
- this.browserLang = 'en-US';
47
- this.access_token = options.token;
48
- this.profile_id = options.profile_id;
49
- this.password = options.password;
50
- this.extra_params = options.extra_params;
51
- this.executablePath = options.executablePath;
52
- this.vnc_port = options.vncPort;
53
- this.fontsMasking = false;
54
- this.is_active = false;
55
- this.is_stopping = false;
56
- this.differentOs = false;
57
- this.profileOs = 'lin';
58
- this.waitWebsocket = options.waitWebsocket ?? true;
59
-
60
- this.isCloudHeadless = options.isCloudHeadless ?? true;
61
-
62
- this.tmpdir = tmpdir();
63
- this.autoUpdateBrowser = !!options.autoUpdateBrowser;
64
- this.checkBrowserUpdate = options.checkBrowserUpdate ?? true;
65
- this.browserChecker = new BrowserChecker(options.skipOrbitaHashChecking);
66
- this.uploadCookiesToServer = options.uploadCookiesToServer || false;
67
- this.writeCookiesFromServer = options.writeCookiesFromServer;
68
- this.remote_debugging_port = options.remote_debugging_port || 0;
69
- this.timezone = options.timezone;
70
- this.extensionPathsToInstall = [];
71
- this.customArgs = options.args || [];
72
- this.restoreLastSession = options.restoreLastSession || false;
73
- this.processSpawned = null;
74
- this.processKillTimeout = 1 * 1000;
75
-
76
- if (options.tmpdir) {
77
- this.tmpdir = options.tmpdir;
78
- if (!existsSync(this.tmpdir)) {
79
- debug('making tmpdir', this.tmpdir);
80
- mkdirSync(this.tmpdir, { recursive: true });
81
- }
82
- }
83
-
84
- this.profile_zip_path = join(this.tmpdir, `gologin_${this.profile_id}.zip`);
85
- this.bookmarksFilePath = join(this.tmpdir, `gologin_profile_${this.profile_id}`, 'Default', 'Bookmarks');
86
- debug('INIT GOLOGIN', this.profile_id);
87
- }
88
-
89
- async checkBrowser() {
90
- return this.browserChecker.checkBrowser(this.autoUpdateBrowser, this.checkBrowserUpdate);
91
- }
92
-
93
- async setProfileId(profile_id) {
94
- this.profile_id = profile_id;
95
- this.cookiesFilePath = await getCookiesFilePath(profile_id, this.tmpdir);
96
- this.profile_zip_path = join(this.tmpdir, `gologin_${this.profile_id}.zip`);
97
- this.bookmarksFilePath = join(this.tmpdir, `gologin_profile_${this.profile_id}`, 'Default', 'Bookmarks');
98
- }
99
-
100
- async getToken(username, password) {
101
- const data = await requests.post(`${API_URL}/user/login`, {
102
- json: {
103
- username,
104
- password,
105
- },
106
- });
107
-
108
- if (!Reflect.has(data, 'body.access_token')) {
109
- throw new Error(`gologin auth failed with status code, ${data.statusCode} DATA ${JSON.stringify(data)}`);
110
- }
111
- }
112
-
113
- async getNewFingerPrint(os) {
114
- debug('GETTING FINGERPRINT');
115
-
116
- const fpResponse = await requests.get(`${API_URL}/browser/fingerprint?os=${os}`, {
117
- json: true,
118
- headers: {
119
- 'Authorization': `Bearer ${this.access_token}`,
120
- 'User-Agent': 'gologin-api',
121
- },
122
- });
123
-
124
- return fpResponse?.body || {};
125
- }
126
-
127
- async profiles() {
128
- const profilesResponse = await requests.get(`${API_URL}/browser/v2`, {
129
- headers: {
130
- 'Authorization': `Bearer ${this.access_token}`,
131
- 'User-Agent': 'gologin-api',
132
-
133
- },
134
- });
135
-
136
- if (profilesResponse.statusCode !== 200) {
137
- throw new Error('Gologin /browser response error');
138
- }
139
-
140
- return JSON.parse(profilesResponse.body);
141
- }
142
-
143
- async getProfile(profile_id) {
144
- const id = profile_id || this.profile_id;
145
- debug('getProfile', this.access_token, id);
146
- const profileResponse = await requests.get(`${API_URL}/browser/${id}`, {
147
- headers: {
148
- 'Authorization': `Bearer ${this.access_token}`,
149
- 'User-Agent': 'gologin-api',
150
- },
151
- });
152
-
153
- debug('profileResponse', profileResponse.statusCode, profileResponse.body);
154
-
155
- const { body: errorBody = '' } = profileResponse;
156
- const backendErrorHeader = 'backend@error::';
157
- if (errorBody.includes(backendErrorHeader)) {
158
- const errorData =
159
- errorBody
160
- .replace(backendErrorHeader, '')
161
- .slice(1, -1);
162
-
163
- throw new Error(errorData);
164
- }
165
-
166
- if (profileResponse.statusCode === 404) {
167
- throw new Error(JSON.parse(profileResponse.body).message);
168
- }
169
-
170
- if (profileResponse.statusCode === 403) {
171
- throw new Error(JSON.parse(profileResponse.body).message);
172
- }
173
-
174
- if (profileResponse.statusCode !== 200) {
175
- throw new Error(`Gologin /browser/${id} response error ${profileResponse.statusCode} INVALID TOKEN OR PROFILE NOT FOUND`);
176
- }
177
-
178
- if (profileResponse.statusCode === 401) {
179
- throw new Error('invalid token');
180
- }
181
-
182
- return JSON.parse(profileResponse.body);
183
- }
184
-
185
- async emptyProfile() {
186
- return readFile(_resolve(__dirname, 'gologin_zeroprofile.b64')).then(res => res.toString());
187
- }
188
-
189
- async getProfileS3(s3path) {
190
- if (!s3path) {
191
- throw new Error('s3path not found');
192
- }
193
-
194
- const token = this.access_token;
195
- debug('getProfileS3 token=', token, 'profile=', this.profile_id, 's3path=', s3path);
196
- const downloadURL = `${STORAGE_GATEWAY_BASE_URL}/download`;
197
-
198
- debug('loading profile from public s3 bucket, url=', downloadURL);
199
- const profileResponse = await requests.get(downloadURL, {
200
- encoding: null,
201
- headers: {
202
- Authorization: `Bearer ${token}`,
203
- browserId: this.profile_id,
204
- },
205
- });
206
-
207
- if (profileResponse.statusCode !== 200) {
208
- debug(`Gologin S3 BUCKET ${downloadURL} response error ${profileResponse.statusCode} - use empty`);
209
-
210
- return '';
211
- }
212
-
213
- return Buffer.from(profileResponse.body);
214
- }
215
-
216
- async postFile(fileName, fileBuff) {
217
- debug('POSTING FILE', fileBuff.length);
218
- debug('Getting signed URL for S3');
219
- const apiUrl = `${STORAGE_GATEWAY_BASE_URL}/upload`;
220
-
221
- const bodyBufferBiteLength = Buffer.byteLength(fileBuff);
222
- console.log('BUFFER SIZE', bodyBufferBiteLength);
223
-
224
- await requests.put(apiUrl, {
225
- headers: {
226
- Authorization: `Bearer ${this.access_token}`,
227
- browserId: this.profile_id,
228
- 'Content-Type': 'application/zip',
229
- 'Content-Length': bodyBufferBiteLength,
230
- },
231
- body: fileBuff,
232
- maxBodyLength: Infinity,
233
- maxContentLength: Infinity,
234
- maxAttempts: 3,
235
- retryDelay: 2000,
236
- timeout: 30 * 1000,
237
- fullResponse: false,
238
- });
239
-
240
- console.log('Profile has been uploaded to S3 successfully');
241
- }
242
-
243
- async emptyProfileFolder() {
244
- debug('get emptyProfileFolder');
245
- const currentDir = dirname(new URL(import.meta.url).pathname);
246
- const zeroProfilePath = join(currentDir, '..', 'zero_profile.zip');
247
- const profile = await readFile(zeroProfilePath);
248
- debug('emptyProfileFolder LENGTH ::', profile.length);
249
-
250
- return profile;
251
- }
252
-
253
- convertPreferences(preferences) {
254
- if (get(preferences, 'navigator.userAgent')) {
255
- preferences.userAgent = get(preferences, 'navigator.userAgent');
256
- }
257
-
258
- if (get(preferences, 'navigator.doNotTrack')) {
259
- preferences.doNotTrack = get(preferences, 'navigator.doNotTrack');
260
- }
261
-
262
- if (get(preferences, 'navigator.hardwareConcurrency')) {
263
- preferences.hardwareConcurrency = get(preferences, 'navigator.hardwareConcurrency');
264
- }
265
-
266
- if (get(preferences, 'navigator.deviceMemory')) {
267
- preferences.deviceMemory = get(preferences, 'navigator.deviceMemory') * 1024;
268
- }
269
-
270
- if (get(preferences, 'navigator.language')) {
271
- preferences.langHeader = get(preferences, 'navigator.language');
272
- preferences.languages = get(preferences, 'navigator.language').replace(/;|q=[\d\.]+/img, '');
273
- }
274
-
275
- if (get(preferences, 'navigator.maxTouchPoints')) {
276
- preferences.navigator.max_touch_points = get(preferences, 'navigator.maxTouchPoints');
277
- }
278
-
279
- if (get(preferences, 'isM1')) {
280
- preferences.is_m1 = get(preferences, 'isM1');
281
- }
282
-
283
- if (get(preferences, 'os') == 'android') {
284
- const devicePixelRatio = get(preferences, 'devicePixelRatio');
285
- const deviceScaleFactorCeil = Math.ceil(devicePixelRatio || 3.5);
286
- let deviceScaleFactor = devicePixelRatio;
287
- if (deviceScaleFactorCeil === devicePixelRatio) {
288
- deviceScaleFactor += 0.00000001;
289
- }
290
-
291
- preferences.mobile = {
292
- enable: true,
293
- width: parseInt(this.resolution.width, 10),
294
- height: parseInt(this.resolution.height, 10),
295
- device_scale_factor: deviceScaleFactor,
296
- };
297
- }
298
-
299
- preferences.mediaDevices = {
300
- enable: preferences.mediaDevices.enableMasking,
301
- videoInputs: preferences.mediaDevices.videoInputs,
302
- audioInputs: preferences.mediaDevices.audioInputs,
303
- audioOutputs: preferences.mediaDevices.audioOutputs,
304
- };
305
-
306
- preferences.webRtc = {
307
- ...preferences.webRtc,
308
- fill_based_on_ip: !!get(preferences, 'webRTC.fillBasedOnIp'),
309
- local_ip_masking: !!get(preferences, 'webRTC.local_ip_masking'),
310
- };
311
-
312
- return preferences;
313
- }
314
-
315
- async createBrowserExtension() {
316
- const that = this;
317
- debug('start createBrowserExtension');
318
- await rimraf(this.orbitaExtensionPath(), () => null);
319
- const extPath = this.orbitaExtensionPath();
320
- debug('extension folder sanitized');
321
- const extension_source = _resolve(__dirname, 'gologin-browser-ext.zip');
322
- await decompress(extension_source, extPath,
323
- {
324
- plugins: [decompressUnzip()],
325
- filter: file => !file.path.endsWith('/'),
326
- },
327
- )
328
- .then(() => {
329
- debug('extraction done');
330
- debug('create uid.json');
331
-
332
- return writeFile(join(extPath, 'uid.json'), JSON.stringify({ uid: that.profile_id }, null, 2))
333
- .then(() => extPath);
334
- })
335
- .catch(async (e) => {
336
- debug('orbita extension error', e);
337
- });
338
-
339
- debug('createBrowserExtension done');
340
- }
341
-
342
- extractProfile(path, zipfile) {
343
- debug(`extactProfile ${zipfile}, ${path}`);
344
-
345
- return decompress(zipfile, path,
346
- {
347
- plugins: [decompressUnzip()],
348
- filter: file => !file.path.endsWith('/'),
349
- },
350
- );
351
- }
352
-
353
- async createStartup(local = false) {
354
- const profilePath = join(this.tmpdir, `gologin_profile_${this.profile_id}`);
355
- let profile;
356
- let profile_folder;
357
- await rimraf(profilePath, () => null);
358
- debug('-', profilePath, 'dropped');
359
- profile = await this.getProfile();
360
- const { navigator = {}, fonts, os: profileOs } = profile;
361
- this.fontsMasking = fonts?.enableMasking;
362
- this.profileOs = profileOs;
363
- this.differentOs =
364
- profileOs !== 'android' && (
365
- OS_PLATFORM === 'win32' && profileOs !== 'win' ||
366
- OS_PLATFORM === 'darwin' && profileOs !== 'mac' ||
367
- OS_PLATFORM === 'linux' && profileOs !== 'lin'
368
- );
369
-
370
- const {
371
- resolution = '1920x1080',
372
- language = 'en-US,en;q=0.9',
373
- } = navigator;
374
-
375
- this.language = language;
376
- const [screenWidth, screenHeight] = resolution.split('x');
377
- this.resolution = {
378
- width: parseInt(screenWidth, 10),
379
- height: parseInt(screenHeight, 10),
380
- };
381
-
382
- const profileZipExists = await access(this.profile_zip_path).then(() => true).catch(() => false);
383
- if (!(local && profileZipExists)) {
384
- try {
385
- profile_folder = await this.getProfileS3(get(profile, 's3Path', ''));
386
- } catch (e) {
387
- debug('Cannot get profile - using empty', e);
388
- }
389
-
390
- debug('FILE READY', this.profile_zip_path);
391
- if (!profile_folder.length) {
392
- profile_folder = await this.emptyProfileFolder();
393
- }
394
-
395
- await writeFile(this.profile_zip_path, profile_folder);
396
-
397
- debug('PROFILE LENGTH', profile_folder.length);
398
- } else {
399
- debug('PROFILE LOCAL HAVING', this.profile_zip_path);
400
- }
401
-
402
- debug('Cleaning up..', profilePath);
403
-
404
- try {
405
- await this.extractProfile(profilePath, this.profile_zip_path);
406
- debug('extraction done');
407
- } catch (e) {
408
- console.trace(e);
409
- profile_folder = await this.emptyProfileFolder();
410
- await writeFile(this.profile_zip_path, profile_folder);
411
- await this.extractProfile(profilePath, this.profile_zip_path);
412
- }
413
-
414
- const singletonLockPath = join(profilePath, 'SingletonLock');
415
- const singletonLockExists = await access(singletonLockPath).then(() => true).catch(() => false);
416
- if (singletonLockExists) {
417
- debug('removing SingletonLock');
418
- await unlink(singletonLockPath);
419
- debug('SingletonLock removed');
420
- }
421
-
422
- const pref_file_name = join(profilePath, 'Default', 'Preferences');
423
- debug('reading', pref_file_name);
424
-
425
- const prefFileExists = await access(pref_file_name).then(() => true).catch(() => false);
426
- if (!prefFileExists) {
427
- debug('Preferences file not exists waiting', pref_file_name, '. Using empty profile');
428
- await writeFile(pref_file_name, '{}');
429
- }
430
-
431
- const preferences_raw = await readFile(pref_file_name);
432
- const preferences = JSON.parse(preferences_raw.toString());
433
- let proxy = get(profile, 'proxy');
434
- const name = get(profile, 'name');
435
- const chromeExtensions = get(profile, 'chromeExtensions') || [];
436
- const userChromeExtensions = get(profile, 'userChromeExtensions') || [];
437
- const allExtensions = [...chromeExtensions, ...userChromeExtensions];
438
-
439
- if (allExtensions.length) {
440
- const ExtensionsManagerInst = new ExtensionsManager();
441
- ExtensionsManagerInst.apiUrl = API_URL;
442
- await ExtensionsManagerInst.init()
443
- .then(() => ExtensionsManagerInst.updateExtensions())
444
- .catch(() => { });
445
- ExtensionsManagerInst.accessToken = this.access_token;
446
-
447
- await ExtensionsManagerInst.getExtensionsPolicies();
448
- let profileExtensionsCheckRes = [];
449
-
450
- if (ExtensionsManagerInst.useLocalExtStorage) {
451
- const promises = [
452
- ExtensionsManagerInst.checkChromeExtensions(allExtensions)
453
- .then(res => ({ profileExtensionsCheckRes: res }))
454
- .catch((e) => {
455
- console.log('checkChromeExtensions error: ', e);
456
-
457
- return { profileExtensionsCheckRes: [] };
458
- }),
459
- ExtensionsManagerInst.checkLocalUserChromeExtensions(userChromeExtensions, this.profile_id)
460
- .then(res => ({ profileUserExtensionsCheckRes: res }))
461
- .catch((error) => {
462
- console.log('checkUserChromeExtensions error: ', error);
463
-
464
- return null;
465
- }),
466
- ];
467
-
468
- const extensionsResult = await Promise.all(promises);
469
-
470
- const profileExtensionPathRes = extensionsResult.find(el => 'profileExtensionsCheckRes' in el) || {};
471
- const profileUserExtensionPathRes = extensionsResult.find(el => 'profileUserExtensionsCheckRes' in el);
472
- profileExtensionsCheckRes =
473
- (profileExtensionPathRes?.profileExtensionsCheckRes || []).concat(profileUserExtensionPathRes?.profileUserExtensionsCheckRes || []);
474
- }
475
-
476
- let extSettings;
477
- if (ExtensionsManagerInst.useLocalExtStorage) {
478
- extSettings = await setExtPathsAndRemoveDeleted(preferences, profileExtensionsCheckRes, this.profile_id);
479
- } else {
480
- const originalExtensionsFolder = join(profilePath, 'Default', 'Extensions');
481
- extSettings = await setOriginalExtPaths(preferences, originalExtensionsFolder);
482
- }
483
-
484
- this.extensionPathsToInstall =
485
- ExtensionsManagerInst.getExtensionsToInstall(extSettings, profileExtensionsCheckRes);
486
-
487
- if (extSettings) {
488
- const currentExtSettings = preferences.extensions || {};
489
- currentExtSettings.settings = extSettings;
490
- preferences.extensions = currentExtSettings;
491
- }
492
- }
493
-
494
- if (proxy.mode === 'gologin' || proxy.mode === 'tor') {
495
- const autoProxyServer = get(profile, 'autoProxyServer');
496
- const splittedAutoProxyServer = autoProxyServer.split('://');
497
- const splittedProxyAddress = splittedAutoProxyServer[1].split(':');
498
- const port = splittedProxyAddress[1];
499
-
500
- proxy = {
501
- 'mode': splittedAutoProxyServer[0],
502
- 'host': splittedProxyAddress[0],
503
- port,
504
- 'username': get(profile, 'autoProxyUsername'),
505
- 'password': get(profile, 'autoProxyPassword'),
506
- };
507
-
508
- profile.proxy.username = get(profile, 'autoProxyUsername');
509
- profile.proxy.password = get(profile, 'autoProxyPassword');
510
- }
511
-
512
- if (proxy.mode === 'geolocation') {
513
- proxy.mode = 'http';
514
- }
515
-
516
- if (proxy.mode === PROXY_NONE) {
517
- proxy = null;
518
- }
519
-
520
- this.proxy = proxy;
521
-
522
- await this.getTimeZone(proxy).catch((e) => {
523
- console.error('Proxy Error. Check it and try again.');
524
- throw new Error(`Proxy Error. ${e.message}`);
525
- });
526
-
527
- const [latitude, longitude] = this._tz.ll;
528
- const { accuracy } = this._tz;
529
-
530
- const profileGeolocation = profile.geolocation;
531
- const tzGeoLocation = {
532
- latitude,
533
- longitude,
534
- accuracy,
535
- };
536
-
537
- profile.geoLocation = this.getGeolocationParams(profileGeolocation, tzGeoLocation);
538
- profile.name = name;
539
- profile.name_base64 = Buffer.from(name).toString('base64');
540
- profile.profile_id = this.profile_id;
541
-
542
- profile.webRtc = {
543
- mode: get(profile, 'webRTC.mode') === 'alerted' ? 'public' : get(profile, 'webRTC.mode'),
544
- publicIP: get(profile, 'webRTC.fillBasedOnIp') ? this._tz.ip : get(profile, 'webRTC.publicIp'),
545
- localIps: get(profile, 'webRTC.localIps', []),
546
- };
547
-
548
- debug('profile.webRtc=', profile.webRtc);
549
- debug('profile.timezone=', profile.timezone);
550
- debug('profile.mediaDevices=', profile.mediaDevices);
551
-
552
- const audioContext = profile.audioContext || {};
553
- const { mode: audioCtxMode = 'off', noise: audioCtxNoise } = audioContext;
554
- if (profile.timezone.fillBasedOnIp === false) {
555
- profile.timezone = { id: profile.timezone.timezone };
556
- } else {
557
- profile.timezone = { id: this._tz.timezone };
558
- }
559
-
560
- profile.webgl_noise_value = profile.webGL.noise;
561
- profile.get_client_rects_noise = profile.webGL.getClientRectsNoise;
562
- profile.canvasMode = profile.canvas.mode;
563
- profile.canvasNoise = profile.canvas.noise;
564
- profile.audioContext = {
565
- enable: audioCtxMode !== 'off',
566
- noiseValue: audioCtxNoise,
567
- };
568
- profile.webgl = {
569
- metadata: {
570
- vendor: get(profile, 'webGLMetadata.vendor'),
571
- renderer: get(profile, 'webGLMetadata.renderer'),
572
- mode: get(profile, 'webGLMetadata.mode') === 'mask',
573
- },
574
- };
575
-
576
- profile.custom_fonts = {
577
- enable: !!fonts?.enableMasking,
578
- };
579
-
580
- const gologin = this.convertPreferences(profile);
581
-
582
- debug(`Writing profile for screenWidth ${profilePath}`, JSON.stringify(gologin));
583
- gologin.screenWidth = this.resolution.width;
584
- gologin.screenHeight = this.resolution.height;
585
- debug('writeCookiesFromServer', this.writeCookiesFromServer);
586
- this.cookiesFilePath = await getCookiesFilePath(this.profile_id, this.tmpdir);
587
- if (this.writeCookiesFromServer) {
588
- await this.writeCookiesToFile();
589
- }
590
-
591
- if (this.fontsMasking) {
592
- const families = fonts?.families || [];
593
- if (!families.length) {
594
- throw new Error('No fonts list provided');
595
- }
596
-
597
- try {
598
- await composeFonts(families, profilePath, this.differentOs);
599
- } catch (e) {
600
- console.trace(e);
601
- }
602
- }
603
-
604
- const languages = this.language.replace(/;|q=[\d\.]+/img, '');
605
-
606
- if (preferences.gologin == null) {
607
- preferences.gologin = {};
608
- }
609
-
610
- preferences.gologin.langHeader = gologin.navigator.language;
611
- preferences.gologin.language = languages;
612
-
613
- const [splittedLangs] = gologin.navigator.language.split(';');
614
- const [browserLang] = splittedLangs.split(',');
615
- gologin.browserLang = browserLang;
616
-
617
- const isMAC = OS_PLATFORM === 'darwin';
618
- const checkAutoLangResult = checkAutoLang(gologin, this._tz);
619
- this.browserLang = isMAC ? 'en-US' : checkAutoLangResult;
620
-
621
- await writeFile(join(profilePath, 'Default', 'Preferences'), JSON.stringify(Object.assign(preferences, {
622
- gologin,
623
- })));
624
-
625
- const bookmarksParsedData = await getCurrentProfileBookmarks(this.bookmarksFilePath);
626
- const bookmarksFromDb = profile.bookmarks?.bookmark_bar;
627
- bookmarksParsedData.roots = bookmarksFromDb ? profile.bookmarks : bookmarksParsedData.roots;
628
- await writeFile(this.bookmarksFilePath, JSON.stringify(bookmarksParsedData));
629
-
630
- debug('Profile ready. Path: ', profilePath, 'PROXY', JSON.stringify(get(preferences, 'gologin.proxy')));
631
-
632
- return profilePath;
633
- }
634
-
635
- async commitProfile() {
636
- const dataBuff = await this.getProfileDataToUpdate();
637
-
638
- debug('begin updating', dataBuff.length);
639
- if (!dataBuff.length) {
640
- debug('WARN: profile zip data empty - SKIPPING PROFILE COMMIT');
641
-
642
- return;
643
- }
644
-
645
- try {
646
- debug('Patching profile');
647
- await this.postFile('profile', dataBuff);
648
- } catch (e) {
649
- debug('CANNOT COMMIT PROFILE', e);
650
- }
651
-
652
- debug('COMMIT COMPLETED');
653
- }
654
-
655
- profilePath() {
656
- return join(this.tmpdir, `gologin_profile_${this.profile_id}`);
657
- }
658
-
659
- orbitaExtensionPath() {
660
- return join(this.tmpdir, `orbita_extension_${this.profile_id}`);
661
- }
662
-
663
- getRandomInt(min, max) {
664
- min = Math.ceil(min);
665
- max = Math.floor(max);
666
-
667
- return Math.floor(Math.random() * (max - min + 1)) + min;
668
- }
669
-
670
- async checkPortAvailable(port) {
671
- debug('CHECKING PORT AVAILABLE', port);
672
-
673
- try {
674
- const portAvailable = await isPortReachable(port, { host: 'localhost' });
675
- if (portAvailable) {
676
- debug(`PORT ${port} IS OPEN`);
677
-
678
- return true;
679
- }
680
- } catch (e) {
681
- console.log(e);
682
- }
683
-
684
- debug(`PORT ${port} IS BUSY`);
685
-
686
- return false;
687
- }
688
-
689
- async getRandomPort() {
690
- let port = this.getRandomInt(20000, 40000);
691
- let portAvailable = await this.checkPortAvailable(port);
692
- while (!portAvailable) {
693
- port = this.getRandomInt(20000, 40000);
694
- portAvailable = await this.checkPortAvailable(port);
695
- }
696
-
697
- return port;
698
- }
699
-
700
- async getTimeZone(proxy) {
701
- debug('getting timeZone proxy=', proxy);
702
-
703
- if (this.timezone) {
704
- debug('getTimeZone from options', this.timezone);
705
- this._tz = this.timezone;
706
-
707
- return this._tz.timezone;
708
- }
709
-
710
- let data = null;
711
- if (proxy && proxy.mode !== PROXY_NONE) {
712
- if (proxy.mode.includes('socks')) {
713
- for (let i = 0; i < 5; i++) {
714
- try {
715
- debug('getting timeZone socks try', i + 1);
716
-
717
- return this.getTimezoneWithSocks(proxy);
718
- } catch (e) {
719
- console.log(e.message);
720
- }
721
- }
722
- throw new Error('Socks proxy connection timed out');
723
- }
724
-
725
- const proxyUrl = `${proxy.mode}://${proxy.username}:${proxy.password}@${proxy.host}:${proxy.port}`;
726
- debug(`getTimeZone start ${TIMEZONE_URL}`, proxyUrl);
727
- data = await requests.get(TIMEZONE_URL, { proxy: proxyUrl, timeout: 20 * 1000, maxAttempts: 5 });
728
- } else {
729
- data = await requests.get(TIMEZONE_URL, { timeout: 20 * 1000, maxAttempts: 5 });
730
- }
731
-
732
- debug('getTimeZone finish', data.body);
733
- this._tz = JSON.parse(data.body);
734
-
735
- return this._tz.timezone;
736
- }
737
-
738
- async getTimezoneWithSocks(params) {
739
- const { host, port, username = '', password = '' } = params;
740
- let body;
741
-
742
- let proxy = 'socks://';
743
- if (username) {
744
- const resultPassword = password ? ':' + password + '@' : '@';
745
- proxy += username + resultPassword;
746
- }
747
-
748
- proxy += host + ':' + port;
749
- const agent = new SocksProxyAgent(proxy);
750
- const checkData = await new Promise((resolve, reject) => {
751
- _get(TIMEZONE_URL, { agent, timeout: 10000 }, (res) => {
752
- let resultResponse = '';
753
- res.on('data', (data) => resultResponse += data);
754
-
755
- res.on('end', () => {
756
- let parsedData;
757
- try {
758
- parsedData = JSON.parse(resultResponse);
759
- } catch (e) {
760
- reject(e);
761
- }
762
-
763
- resolve({
764
- ...res,
765
- body: parsedData,
766
- });
767
- });
768
- }).on('error', (err) => reject(err));
769
- });
770
-
771
- body = checkData.body || {};
772
- if (!body.ip && checkData.statusCode.toString().startsWith('4')) {
773
- throw checkData;
774
- }
775
-
776
- debug('getTimeZone finish', body.body);
777
- this._tz = body;
778
-
779
- return this._tz.timezone;
780
- }
781
-
782
- async spawnArguments() {
783
- const profile_path = this.profilePath();
784
-
785
- let { proxy } = this;
786
- proxy = `${proxy.mode}://${proxy.host}:${proxy.port}`;
787
-
788
- const env = {};
789
- Object.keys(process.env).forEach((key) => {
790
- env[key] = process.env[key];
791
- });
792
-
793
- const tz = await this.getTimeZone(this.proxy).catch((e) => {
794
- console.error('Proxy Error. Check it and try again.');
795
- throw e;
796
- });
797
-
798
- env.TZ = tz;
799
-
800
- let params = [`--proxy-server=${proxy}`, `--user-data-dir=${profile_path}`, '--password-store=basic', `--tz=${tz}`, '--lang=en'];
801
- if (Array.isArray(this.extra_params) && this.extra_params.length) {
802
- params = params.concat(this.extra_params);
803
- }
804
-
805
- if (this.remote_debugging_port) {
806
- params.push(`--remote-debugging-port=${this.remote_debugging_port}`);
807
- }
808
-
809
- return params;
810
- }
811
-
812
- async spawnBrowser() {
813
- let { remote_debugging_port, customArgs } = this;
814
- if (!remote_debugging_port) {
815
- remote_debugging_port = await this.getRandomPort();
816
- }
817
-
818
- const profile_path = this.profilePath();
819
-
820
- let { proxy } = this;
821
- let proxy_host = '';
822
- if (proxy) {
823
- proxy_host = this.proxy.host;
824
- proxy = `${proxy.mode}://${proxy.host}:${proxy.port}`;
825
- }
826
-
827
- this.port = remote_debugging_port;
828
-
829
- const ORBITA_BROWSER = this.executablePath || this.browserChecker.getOrbitaPath;
830
- debug(`ORBITA_BROWSER=${ORBITA_BROWSER}`);
831
- const env = {};
832
- Object.keys(process.env).forEach((key) => {
833
- env[key] = process.env[key];
834
- });
835
-
836
- const tz = await this.getTimeZone(this.proxy).catch((e) => {
837
- console.error('Proxy Error. Check it and try again.');
838
- throw e;
839
- });
840
-
841
- env.TZ = tz;
842
-
843
- if (this.vnc_port) {
844
- const script_path = _resolve(__dirname, './run.sh');
845
- debug('RUNNING', script_path, ORBITA_BROWSER, remote_debugging_port, proxy, profile_path, this.vnc_port);
846
- execFile(
847
- script_path,
848
- [ORBITA_BROWSER, remote_debugging_port, proxy, profile_path, this.vnc_port, tz],
849
- { env },
850
- );
851
- } else {
852
- let params = [
853
- `--remote-debugging-port=${remote_debugging_port}`,
854
- `--user-data-dir=${profile_path}`,
855
- '--password-store=basic',
856
- `--tz=${tz}`,
857
- `--lang=${this.browserLang}`,
858
- ];
859
-
860
- if (this.extensionPathsToInstall.length) {
861
- if (Array.isArray(this.extra_params) && this.extra_params.length) {
862
- this.extra_params.forEach((param, index) => {
863
- if (!param.includes('--load-extension=')) {
864
- return;
865
- }
866
-
867
- const [_, extPathsString] = param.split('=');
868
- const extPathsArray = extPathsString.split(',');
869
- this.extensionPathsToInstall = [...this.extensionPathsToInstall, ...extPathsArray];
870
- this.extra_params.splice(index, 1);
871
- });
872
- }
873
-
874
- params.push(`--load-extension=${this.extensionPathsToInstall.join(',')}`);
875
- }
876
-
877
- if (this.fontsMasking) {
878
- let arg = '--font-masking-mode=2';
879
- if (this.differentOs) {
880
- arg = '--font-masking-mode=3';
881
- }
882
-
883
- if (this.profileOs === 'android') {
884
- arg = '--font-masking-mode=1';
885
- }
886
-
887
- params.push(arg);
888
- }
889
-
890
- if (proxy) {
891
- const hr_rules = `"MAP * 0.0.0.0 , EXCLUDE ${proxy_host}"`;
892
- params.push(`--proxy-server=${proxy}`);
893
- params.push(`--host-resolver-rules=${hr_rules}`);
894
- }
895
-
896
- if (Array.isArray(this.extra_params) && this.extra_params.length) {
897
- params = params.concat(this.extra_params);
898
- }
899
-
900
- if (this.restoreLastSession) {
901
- params.push('--restore-last-session');
902
- }
903
-
904
- params.push(...new Set(customArgs));
905
-
906
- console.log(params);
907
- const child = execFile(ORBITA_BROWSER, params, { env });
908
- this.processSpawned = child;
909
- // const child = spawn(ORBITA_BROWSER, params, { env, shell: true });
910
- child.stdout.on('data', (data) => debug(data.toString()));
911
- debug('SPAWN CMD', ORBITA_BROWSER, params.join(' '));
912
- }
913
-
914
- if (this.waitWebsocket) {
915
- debug('GETTING WS URL FROM BROWSER');
916
- const data = await requests.get(`http://127.0.0.1:${remote_debugging_port}/json/version`, { json: true });
917
-
918
- debug('WS IS', get(data, 'body.webSocketDebuggerUrl', ''));
919
- this.is_active = true;
920
-
921
- return get(data, 'body.webSocketDebuggerUrl', '');
922
- }
923
-
924
- return '';
925
- }
926
-
927
- async createStartupAndSpawnBrowser() {
928
- await this.createStartup();
929
-
930
- return this.spawnBrowser();
931
- }
932
-
933
- async clearProfileFiles() {
934
- await rimraf(join(this.tmpdir, `gologin_profile_${this.profile_id}`), () => null);
935
- await rimraf(join(this.tmpdir, `gologin_${this.profile_id}_upload.zip`), () => null);
936
- }
937
-
938
- async stopAndCommit(options, local = false) {
939
- if (this.is_stopping) {
940
- return true;
941
- }
942
-
943
- const is_posting = options.posting ||
944
- options.postings || // backward compability
945
- false;
946
-
947
- if (this.uploadCookiesToServer) {
948
- await this.uploadProfileCookiesToServer();
949
- }
950
-
951
- await this.saveBookmarksToDb();
952
-
953
- this.is_stopping = true;
954
- await this.sanitizeProfile();
955
-
956
- if (is_posting) {
957
- await this.commitProfile();
958
- }
959
-
960
- this.is_stopping = false;
961
- this.is_active = false;
962
- await delay(3000);
963
- await this.clearProfileFiles();
964
-
965
- if (!local) {
966
- await rimraf(join(this.tmpdir, `gologin_${this.profile_id}.zip`), () => null);
967
- }
968
-
969
- debug(`PROFILE ${this.profile_id} STOPPED AND CLEAR`);
970
-
971
- return false;
972
- }
973
-
974
- async stopBrowser() {
975
- if (!this.port) {
976
- throw new Error('Empty GoLogin port');
977
- }
978
-
979
- const ls = await spawn('fuser',
980
- [
981
- '-k TERM',
982
- `-n tcp ${this.port}`,
983
- ],
984
- {
985
- shell: true,
986
- },
987
- );
988
-
989
- debug('browser killed');
990
- }
991
-
992
- killBrowser() {
993
- if (!this.processSpawned.pid) {
994
- return;
995
- }
996
-
997
- try {
998
- this.processSpawned.kill();
999
- debug('browser killed');
1000
- } catch (error) {
1001
- console.error(error);
1002
- }
1003
- }
1004
-
1005
- async killAndCommit(options, local = false) {
1006
- this.killBrowser();
1007
- await delay(this.processKillTimeout);
1008
- await this.stopAndCommit(options, local).catch(console.error);
1009
- }
1010
-
1011
- async sanitizeProfile() {
1012
- const remove_dirs = [
1013
- `${SEPARATOR}Default${SEPARATOR}Cache`,
1014
- `${SEPARATOR}Default${SEPARATOR}Service Worker`,
1015
- `${SEPARATOR}Default${SEPARATOR}Code Cache`,
1016
- `${SEPARATOR}Default${SEPARATOR}GPUCache`,
1017
- `${SEPARATOR}Default${SEPARATOR}Extensions`,
1018
- `${SEPARATOR}Default${SEPARATOR}IndexedDB`,
1019
- `${SEPARATOR}Default${SEPARATOR}GPUCache`,
1020
- `${SEPARATOR}Default${SEPARATOR}DawnCache`,
1021
- `${SEPARATOR}Default${SEPARATOR}fonts_config`,
1022
- `${SEPARATOR}GrShaderCache`,
1023
- `${SEPARATOR}ShaderCache`,
1024
- `${SEPARATOR}biahpgbdmdkfgndcmfiipgcebobojjkp`,
1025
- `${SEPARATOR}afalakplffnnnlkncjhbmahjfjhmlkal`,
1026
- `${SEPARATOR}cffkpbalmllkdoenhmdmpbkajipdjfam`,
1027
- `${SEPARATOR}Dictionaries`,
1028
- `${SEPARATOR}enkheaiicpeffbfgjiklngbpkilnbkoi`,
1029
- `${SEPARATOR}oofiananboodjbbmdelgdommihjbkfag`,
1030
- `${SEPARATOR}SafetyTips`,
1031
- `${SEPARATOR}fonts`,
1032
- `${SEPARATOR}BrowserMetrics`,
1033
- `${SEPARATOR}BrowserMetrics-spare.pma`,
1034
- ];
1035
-
1036
- const that = this;
1037
-
1038
- await Promise.all(remove_dirs.map(d => {
1039
- const path_to_remove = `${that.profilePath()}${d}`;
1040
-
1041
- return new Promise(resolve => {
1042
- debug('DROPPING', path_to_remove);
1043
- rimraf(path_to_remove, { maxBusyTries: 100 }, (e) => {
1044
- // debug('DROPPING RESULT', e);
1045
- resolve();
1046
- });
1047
- });
1048
- }));
1049
- }
1050
-
1051
- async getProfileDataToUpdate() {
1052
- const zipPath = join(this.tmpdir, `gologin_${this.profile_id}_upload.zip`);
1053
- const zipExists = await access(zipPath).then(() => true).catch(() => false);
1054
- if (zipExists) {
1055
- await unlink(zipPath);
1056
- }
1057
-
1058
- await this.sanitizeProfile();
1059
- debug('profile sanitized');
1060
-
1061
- const profilePath = this.profilePath();
1062
- const fileBuff = await archiveProfile(profilePath);
1063
-
1064
- debug('PROFILE ZIP CREATED', profilePath, zipPath);
1065
-
1066
- return fileBuff;
1067
- }
1068
-
1069
- async profileExists() {
1070
- const profileResponse = await requests.post(`${API_URL}/browser`, {
1071
- headers: {
1072
- 'Authorization': `Bearer ${this.access_token}`,
1073
- 'User-Agent': 'gologin-api',
1074
- },
1075
- json: {
1076
-
1077
- },
1078
- });
1079
-
1080
- if (profileResponse.statusCode !== 200) {
1081
- return false;
1082
- }
1083
-
1084
- debug('profile is', profileResponse.body);
1085
-
1086
- return true;
1087
- }
1088
-
1089
- async getRandomFingerprint(options) {
1090
- let os = 'lin';
1091
-
1092
- if (options.os) {
1093
- os = options.os;
1094
- }
1095
-
1096
- let url = `${API_URL}/browser/fingerprint?os=${os}`;
1097
- if (options.isM1) {
1098
- url += '&isM1=true';
1099
- }
1100
-
1101
- const fingerprint = await requests.get(url, {
1102
- headers: {
1103
- 'Authorization': `Bearer ${this.access_token}`,
1104
- 'User-Agent': 'gologin-api',
1105
- },
1106
- });
1107
-
1108
- return JSON.parse(fingerprint.body);
1109
- }
1110
-
1111
- async create(options) {
1112
- debug('createProfile', options);
1113
-
1114
- const fingerprint = await this.getRandomFingerprint(options);
1115
- debug('fingerprint=', fingerprint);
1116
-
1117
- if (fingerprint.statusCode === 500) {
1118
- throw new Error('no valid random fingerprint check os param');
1119
- }
1120
-
1121
- if (fingerprint.statusCode === 401) {
1122
- throw new Error('invalid token');
1123
- }
1124
-
1125
- const { navigator, fonts, webGLMetadata, webRTC } = fingerprint;
1126
- let deviceMemory = navigator.deviceMemory || 2;
1127
- if (deviceMemory < 1) {
1128
- deviceMemory = 1;
1129
- }
1130
-
1131
- navigator.deviceMemory = deviceMemory * 1024;
1132
- webGLMetadata.mode = webGLMetadata.mode === 'noise' ? 'mask' : 'off';
1133
-
1134
- const json = {
1135
- ...fingerprint,
1136
- navigator,
1137
- webGLMetadata,
1138
- browserType: 'chrome',
1139
- name: 'default_name',
1140
- notes: 'auto generated',
1141
- fonts: {
1142
- families: fonts,
1143
- },
1144
- webRTC: {
1145
- ...webRTC,
1146
- mode: 'alerted',
1147
- },
1148
- };
1149
-
1150
- const user_agent = options.navigator?.userAgent;
1151
- const orig_user_agent = json.navigator.userAgent;
1152
- Object.keys(options).forEach((key) => {
1153
- if (typeof json[key] === 'object') {
1154
- json[key] = { ...json[key], ...options[key] };
1155
-
1156
- return;
1157
- }
1158
-
1159
- json[key] = options[key];
1160
- });
1161
-
1162
- if (user_agent === 'random') {
1163
- json.navigator.userAgent = orig_user_agent;
1164
- }
1165
-
1166
- const response = await requests.post(`${API_URL}/browser`, {
1167
- headers: {
1168
- 'Authorization': `Bearer ${this.access_token}`,
1169
- 'User-Agent': 'gologin-api',
1170
- },
1171
- json,
1172
- });
1173
-
1174
- if (response.statusCode === 400) {
1175
- throw new Error(`gologin failed account creation with status code, ${response.statusCode} DATA ${JSON.stringify(response.body.message)}`);
1176
- }
1177
-
1178
- if (response.statusCode === 500) {
1179
- throw new Error(`gologin failed account creation with status code, ${response.statusCode}`);
1180
- }
1181
-
1182
- debug(JSON.stringify(response.body));
1183
-
1184
- return response.body.id;
1185
- }
1186
-
1187
- async createCustom(options) {
1188
- debug('createCustomProfile', options);
1189
- const response = await requests.post(`${API_URL}/browser/custom`, {
1190
- headers: {
1191
- 'Authorization': `Bearer ${this.access_token}`,
1192
- 'User-Agent': 'gologin-api',
1193
- },
1194
- json: options,
1195
- });
1196
-
1197
- if (response.statusCode === 400) {
1198
- throw new Error(`gologin failed account creation with status code, ${response.statusCode} DATA ${JSON.stringify(response.body.message)}`);
1199
- }
1200
-
1201
- if (response.statusCode === 500) {
1202
- throw new Error(`gologin failed account creation with status code, ${response.statusCode}`);
1203
- }
1204
-
1205
- debug(JSON.stringify(response));
1206
-
1207
- return response.body.id;
1208
- }
1209
-
1210
- async quickCreateProfile(name = '') {
1211
- const osInfo = await getOsAdvanced();
1212
- const { os, osSpec } = osInfo;
1213
- const resultName = name || 'api-generated';
1214
-
1215
- return requests.post(`${API_URL}/browser/quick`, {
1216
- headers: {
1217
- 'Authorization': `Bearer ${this.access_token}`,
1218
- 'User-Agent': 'gologin-api',
1219
- },
1220
- json: {
1221
- os,
1222
- osSpec,
1223
- name: resultName,
1224
- },
1225
- })
1226
- .then(res => res.body);
1227
- }
1228
-
1229
- async delete(pid) {
1230
- const profile_id = pid || this.profile_id;
1231
- await requests.delete(`${API_URL}/browser/${profile_id}`, {
1232
- headers: {
1233
- 'Authorization': `Bearer ${this.access_token}`,
1234
- 'User-Agent': 'gologin-api',
1235
- },
1236
- });
1237
- }
1238
-
1239
- async update(options) {
1240
- this.profile_id = options.id;
1241
- const profile = await this.getProfile();
1242
-
1243
- if (options.navigator) {
1244
- Object.keys(options.navigator).map((e) => {
1245
- profile.navigator[e] = options.navigator[e];
1246
- });
1247
- }
1248
-
1249
- Object.keys(options).filter(el => el !== 'navigator').forEach((el) => {
1250
- profile[el] = options[el];
1251
- });
1252
-
1253
- debug('update profile', profile);
1254
- const response = await requests.put(`${API_URL}/browser/${options.id}`, {
1255
- json: profile,
1256
- headers: {
1257
- 'Authorization': `Bearer ${this.access_token}`,
1258
- 'User-Agent': 'gologin-api',
1259
- },
1260
- });
1261
-
1262
- debug('response', JSON.stringify(response.body));
1263
-
1264
- return response.body;
1265
- }
1266
-
1267
- setActive(is_active) {
1268
- this.is_active = is_active;
1269
- }
1270
-
1271
- getGeolocationParams(profileGeolocationParams, tzGeolocationParams) {
1272
- if (profileGeolocationParams.fillBasedOnIp) {
1273
- return {
1274
- mode: profileGeolocationParams.mode,
1275
- latitude: Number(tzGeolocationParams.latitude),
1276
- longitude: Number(tzGeolocationParams.longitude),
1277
- accuracy: Number(tzGeolocationParams.accuracy),
1278
- };
1279
- }
1280
-
1281
- return {
1282
- mode: profileGeolocationParams.mode,
1283
- latitude: profileGeolocationParams.latitude,
1284
- longitude: profileGeolocationParams.longitude,
1285
- accuracy: profileGeolocationParams.accuracy,
1286
- };
1287
- }
1288
-
1289
- getViewPort() {
1290
- return { ...this.resolution };
1291
- }
1292
-
1293
- async postCookies(profileId, cookies) {
1294
- const formattedCookies = cookies.map(cookie => {
1295
- if (!['no_restriction', 'lax', 'strict', 'unspecified'].includes(cookie.sameSite)) {
1296
- cookie.sameSite = 'unspecified';
1297
- }
1298
-
1299
- return cookie;
1300
- });
1301
-
1302
- const response = await uploadCookies({
1303
- profileId,
1304
- cookies: formattedCookies,
1305
- API_BASE_URL: API_URL,
1306
- ACCESS_TOKEN: this.access_token,
1307
- });
1308
-
1309
- if (response.statusCode === 200) {
1310
- return response.body;
1311
- }
1312
-
1313
- return { status: 'failure', status_code: response.statusCode, body: response.body };
1314
- }
1315
-
1316
- async getCookies(profileId) {
1317
- const response = await downloadCookies({
1318
- profileId,
1319
- API_BASE_URL: API_URL,
1320
- ACCESS_TOKEN: this.access_token,
1321
- });
1322
-
1323
- return response.body;
1324
- }
1325
-
1326
- async writeCookiesToFile() {
1327
- const cookies = await this.getCookies(this.profile_id);
1328
- const resultCookies = cookies.map((el) => ({ ...el, value: Buffer.from(el.value) }));
1329
-
1330
- let db;
1331
- try {
1332
- db = await getDB(this.cookiesFilePath, false);
1333
- if (resultCookies.length) {
1334
- const chunckInsertValues = getChunckedInsertValues(resultCookies);
1335
-
1336
- for (const [query, queryParams] of chunckInsertValues) {
1337
- const insertStmt = await db.prepare(query);
1338
- await insertStmt.run(queryParams);
1339
- await insertStmt.finalize();
1340
- }
1341
- } else {
1342
- const query = 'delete from cookies';
1343
- const insertStmt = await db.prepare(query);
1344
- await insertStmt.run();
1345
- await insertStmt.finalize();
1346
- }
1347
- } catch (error) {
1348
- console.log(error.message);
1349
- } finally {
1350
- db && await db.close();
1351
- }
1352
- }
1353
-
1354
- async uploadProfileCookiesToServer() {
1355
- const cookies = await loadCookiesFromFile(this.cookiesFilePath);
1356
- if (!cookies.length) {
1357
- return;
1358
- }
1359
-
1360
- return this.postCookies(this.profile_id, cookies);
1361
- }
1362
-
1363
- async saveBookmarksToDb() {
1364
- const bookmarksData = await getCurrentProfileBookmarks(this.bookmarksFilePath);
1365
- const bookmarks = bookmarksData.roots || {};
1366
- await updateProfileBookmarks([this.profile_id], this.access_token, bookmarks);
1367
- }
1368
-
1369
- async start() {
1370
- if (!this.executablePath) {
1371
- await this.checkBrowser();
1372
- }
1373
-
1374
- const ORBITA_BROWSER = this.executablePath || this.browserChecker.getOrbitaPath;
1375
-
1376
- const orbitaBrowserExists = await access(ORBITA_BROWSER).then(() => true).catch(() => false);
1377
- if (!orbitaBrowserExists) {
1378
- throw new Error(`Orbita browser is not exists on path ${ORBITA_BROWSER}, check executablePath param`);
1379
- }
1380
-
1381
- await this.createStartup();
1382
- // await this.createBrowserExtension();
1383
- const wsUrl = await this.spawnBrowser();
1384
- this.setActive(true);
1385
-
1386
- return { status: 'success', wsUrl };
1387
- }
1388
-
1389
- async startLocal() {
1390
- await this.createStartup(true);
1391
- // await this.createBrowserExtension();
1392
- const wsUrl = await this.spawnBrowser();
1393
- this.setActive(true);
1394
-
1395
- return { status: 'success', wsUrl };
1396
- }
1397
-
1398
- async stop() {
1399
- await new Promise(resolve => setTimeout(resolve, 500));
1400
-
1401
- await this.stopAndCommit({ posting: true }, false);
1402
- }
1403
-
1404
- async stopLocal(options) {
1405
- const opts = options || { posting: false };
1406
- await this.stopAndCommit(opts, true);
1407
- }
1408
-
1409
- async waitDebuggingUrl(delay_ms, try_count = 0, remoteOrbitaUrl) {
1410
- await delay(delay_ms);
1411
- const url = `${remoteOrbitaUrl}/json/version`;
1412
- console.log('try_count=', try_count, 'url=', url);
1413
- const response = await requests.get(url);
1414
- let wsUrl = '';
1415
- console.log('response', response.body);
1416
-
1417
- if (!response.body) {
1418
- return wsUrl;
1419
- }
1420
-
1421
- try {
1422
- const parsedBody = JSON.parse(response.body);
1423
- wsUrl = parsedBody.webSocketDebuggerUrl;
1424
- } catch (e) {
1425
- if (try_count < 3) {
1426
- return this.waitDebuggingUrl(delay_ms, try_count + 1, remoteOrbitaUrl);
1427
- }
1428
-
1429
- return { status: 'failure', wsUrl, message: 'Check proxy settings', 'profile_id': this.profile_id };
1430
- }
1431
-
1432
- const remoteOrbitaUrlWithoutProtocol = remoteOrbitaUrl.replace('https://', '');
1433
- wsUrl = wsUrl.replace('ws://', 'wss://').replace('127.0.0.1', remoteOrbitaUrlWithoutProtocol);
1434
-
1435
- return wsUrl;
1436
- }
1437
-
1438
- async stopRemote() {
1439
- debug(`stopRemote ${this.profile_id}`);
1440
- const profileResponse = await requests.delete(`${API_URL}/browser/${this.profile_id}/web`, {
1441
- headers: {
1442
- 'Authorization': `Bearer ${this.access_token}`,
1443
- 'User-Agent': 'gologin-api',
1444
- },
1445
- });
1446
-
1447
- console.log(`stopRemote ${profileResponse.body}`);
1448
- if (profileResponse.body) {
1449
- return JSON.parse(profileResponse.body);
1450
- }
1451
- }
1452
-
1453
- getAvailableFonts() {
1454
- return fontsCollection
1455
- .filter(elem => elem.fileNames)
1456
- .map(elem => elem.name);
1457
- }
1458
-
1459
- async changeProfileResolution(resolution) {
1460
- return updateProfileResolution(this.profile_id, this.access_token, resolution);
1461
- }
1462
-
1463
- async changeProfileUserAgent(userAgent) {
1464
- return updateProfileUserAgent(this.profile_id, this.access_token, userAgent);
1465
- }
1466
-
1467
- async changeProfileProxy(proxyData) {
1468
- return updateProfileProxy(this.profile_id, this.access_token, proxyData);
1469
- }
1470
- }
1471
-
1472
- export default GoLogin;