gologin 1.0.31 → 1.0.32

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 (2) hide show
  1. package/gologin.js +121 -68
  2. package/package.json +3 -3
package/gologin.js CHANGED
@@ -5,6 +5,7 @@ const fs = require('fs');
5
5
  const os = require('os');
6
6
  const util = require('util');
7
7
  const rimraf = util.promisify(require('rimraf'));
8
+ const { access, unlink, writeFile, readFile } = require('fs').promises;
8
9
  const exec = util.promisify(require('child_process').exec);
9
10
  const { spawn, execFile } = require('child_process');
10
11
  const FormData = require('form-data');
@@ -12,7 +13,7 @@ const ProxyAgent = require('simple-proxy-agent');
12
13
  const decompress = require('decompress');
13
14
  const decompressUnzip = require('decompress-unzip');
14
15
  const path = require('path');
15
- const shell = require('shelljs');
16
+ const zipdir = require('zip-dir');
16
17
 
17
18
  const BrowserChecker = require('./browser-checker');
18
19
  const { BrowserUserDataManager } = require('./browser-user-data-manager');
@@ -54,7 +55,7 @@ class GoLogin {
54
55
  this.tmpdir = options.tmpdir;
55
56
  if (!fs.existsSync(this.tmpdir)) {
56
57
  debug('making tmpdir', this.tmpdir);
57
- shell.mkdir('-p', this.tmpdir);
58
+ fs.mkdirSync(this.tmpdir, { recursive: true })
58
59
  }
59
60
  }
60
61
 
@@ -127,7 +128,7 @@ class GoLogin {
127
128
  throw new Error(`Gologin /browser/${id} response error ${profileResponse.statusCode} INVALID TOKEN OR PROFILE NOT FOUND`);
128
129
  }
129
130
 
130
- if(profileResponse.statusCode == 401){
131
+ if (profileResponse.statusCode === 401) {
131
132
  throw new Error("invalid token");
132
133
  }
133
134
 
@@ -135,12 +136,13 @@ class GoLogin {
135
136
  }
136
137
 
137
138
  async emptyProfile() {
138
- return fs.readFileSync(path.resolve(__dirname, 'gologin_zeroprofile.b64')).toString();
139
+ return readFile(path.resolve(__dirname, 'gologin_zeroprofile.b64')).then(res => res.toString());
139
140
  }
140
141
 
141
142
  async getProfileS3(s3path) {
142
143
  const token = this.access_token;
143
144
  debug('getProfileS3 token=', token, 'profile=', this.profile_id, 's3path=', s3path);
145
+
144
146
  if (s3path) { //загрузка профиля из публичного бакета s3 быстрее
145
147
  const s3url = `https://gprofiles.gologin.com/${s3path}`.replace(/\s+/mg, '+');
146
148
  debug('loading profile from public s3 bucket, url=', s3url);
@@ -151,6 +153,7 @@ class GoLogin {
151
153
  debug(`Gologin S3 BUCKET ${s3url} response error ${profileResponse.statusCode} - use empty`);
152
154
  return '';
153
155
  }
156
+
154
157
  return Buffer.from(profileResponse.body);
155
158
  }
156
159
 
@@ -161,15 +164,33 @@ class GoLogin {
161
164
  },
162
165
  encoding: null
163
166
  });
167
+
164
168
  if (profileResponse.statusCode !== 200) {
165
169
  debug(`Gologin /browser/${this.profile_id} response error ${profileResponse.statusCode} - use empty`);
166
170
  return '';
167
171
  }
172
+
168
173
  return Buffer.from(profileResponse.body);
169
174
  }
170
175
 
171
176
  async postFile(fileName, fileBody) {
172
177
  debug('POSTING FILE', fileBody.length);
178
+ debug('Getting signed URL for S3');
179
+ const apiUrl = `${API_URL}/browser/${this.profile_id}/storage-signature`;
180
+
181
+ const signedUrl = await requests.get(apiUrl, {
182
+ headers: {
183
+ Authorization: `Bearer ${this.access_token}`,
184
+ 'user-agent': 'gologin-api',
185
+ },
186
+ maxAttempts: 3,
187
+ retryDelay: 2000,
188
+ timeout: 10 * 1000,
189
+ fullResponse: false,
190
+ });
191
+
192
+ const [uploadedProfileUrl] = signedUrl.split('?');
193
+
173
194
  const fd = new FormData();
174
195
  const boundary = fd.getBoundary();
175
196
  const body = Buffer.concat([
@@ -186,26 +207,44 @@ class GoLogin {
186
207
  Buffer.from('--'),
187
208
  Buffer.from('\r\n')
188
209
  ]);
189
- await new Promise((resolve) => {
190
- let options = {
191
- method: 'PATCH',
192
- headers: {
193
- 'Authorization': `Bearer ${this.access_token}`,
194
- 'Content-Type': 'multipart/form-data; boundary=' + boundary,
195
- 'Content-Length': body.length
196
- },
197
- body: body,
198
- url: `${API_URL}/browser/${this.profile_id}/profile-s3`,
199
- };
200
- requests(_.merge(options, {}), () => {
201
- resolve();
202
- });
210
+
211
+ console.log('Uploading profile by signed URL to S3');
212
+ const bodyBufferBiteLength = Buffer.byteLength(body);
213
+ console.log('BUFFER SIZE', bodyBufferBiteLength);
214
+
215
+ await requests.put(signedUrl, {
216
+ headers: {
217
+ 'Content-Type': 'application/zip',
218
+ 'Content-Length': bodyBufferBiteLength,
219
+ },
220
+ body,
221
+ maxBodyLength: Infinity,
222
+ maxContentLength: Infinity,
223
+ maxAttempts: 3,
224
+ retryDelay: 2000,
225
+ timeout: 30 * 1000,
226
+ fullResponse: false,
227
+ });
228
+
229
+ const uploadedProfileMetadata = await requests.head(uploadedProfileUrl, {
230
+ maxAttempts: 3,
231
+ retryDelay: 2000,
232
+ timeout: 10 * 1000,
233
+ fullResponse: true,
203
234
  });
235
+
236
+ const uploadedFileLength = +uploadedProfileMetadata.headers['content-length'];
237
+ if (uploadedFileLength !== bodyBufferBiteLength) {
238
+ console.log('Uploaded file is incorrect. Retry with China File size:', uploadedFileLength);
239
+ throw new Error('Uploaded file is incorrect. Retry with China File size: ' + uploadedFileLength);
240
+ }
241
+
242
+ console.log('Profile has been uploaded to S3 successfully');
204
243
  }
205
244
 
206
245
  async emptyProfileFolder() {
207
246
  debug('get emptyProfileFolder');
208
- const profile = fs.readFileSync(path.resolve(__dirname, 'gologin_zeroprofile.zip'));
247
+ const profile = await readFile(path.resolve(__dirname, 'gologin_zeroprofile.zip'));
209
248
  debug('emptyProfileFolder LENGTH ::', profile.length);
210
249
  return profile;
211
250
  }
@@ -246,9 +285,8 @@ class GoLogin {
246
285
  .then(() => {
247
286
  debug('extraction done');
248
287
  debug('create uid.json');
249
- fs.writeFileSync(path.join(extPath, 'uid.json'), JSON.stringify({uid: that.profile_id}, null, 2))
250
- debug('uid.json created', fs.readFileSync(path.join(extPath, 'uid.json')).toString())
251
- return extPath;
288
+ return writeFile(path.join(extPath, 'uid.json'), JSON.stringify({ uid: that.profile_id }, null, 2))
289
+ .then(() => extPath);
252
290
  })
253
291
  .catch(async (e) => {
254
292
  debug('orbita extension error', e);
@@ -299,7 +337,8 @@ class GoLogin {
299
337
  height: parseInt(screenHeight, 10),
300
338
  };
301
339
 
302
- if (!(local && fs.existsSync(this.profile_zip_path))) {
340
+ const profileZipExists = await access(this.profile_zip_path).then(() => true).catch(() => false);
341
+ if (!(local && profileZipExists)) {
303
342
  try {
304
343
  profile_folder = await this.getProfileS3(_.get(profile, 's3Path', ''));
305
344
  }
@@ -310,8 +349,8 @@ class GoLogin {
310
349
  if (!profile_folder.length) {
311
350
  profile_folder = await this.emptyProfileFolder();
312
351
  }
313
-
314
- fs.writeFileSync(this.profile_zip_path, profile_folder);
352
+
353
+ await writeFile(this.profile_zip_path, profile_folder);
315
354
 
316
355
  debug('PROFILE LENGTH', profile_folder.length);
317
356
  } else {
@@ -323,20 +362,23 @@ class GoLogin {
323
362
  await this.extractProfile(profilePath, this.profile_zip_path);
324
363
  debug('extraction done');
325
364
 
326
- if (fs.existsSync(path.join(profilePath, 'SingletonLock'))) {
365
+ const singletonLockPath = path.join(profilePath, 'SingletonLock');
366
+ const singletonLockExists = await access(singletonLockPath).then(() => true).catch(() => false);
367
+ if (singletonLockExists) {
327
368
  debug('removing SingletonLock');
328
- fs.unlinkSync(path.join(profilePath, 'SingletonLock'));
369
+ await unlink(singletonLockPath);
329
370
  debug('SingletonLock removed');
330
371
  }
331
372
 
332
373
  const pref_file_name = path.join(profilePath, 'Default', 'Preferences');
333
374
  debug('reading', pref_file_name);
334
375
 
335
- if (!fs.existsSync(pref_file_name)) {
376
+ const prefFileExists = await access(pref_file_name).then(() => true).catch(() => false);
377
+ if (!prefFileExists) {
336
378
  debug('Preferences file not exists waiting', pref_file_name);
337
379
  }
338
380
 
339
- const preferences_raw = fs.readFileSync(pref_file_name);
381
+ const preferences_raw = await readFile(pref_file_name);
340
382
  let preferences = JSON.parse(preferences_raw.toString());
341
383
  let proxy = _.get(profile, 'proxy');
342
384
  let name = _.get(profile, 'name');
@@ -436,7 +478,7 @@ class GoLogin {
436
478
  await BrowserUserDataManager.composeFonts(families, profilePath, this.differentOs);
437
479
  }
438
480
 
439
- fs.writeFileSync(path.join(profilePath, 'Default', 'Preferences'), JSON.stringify(_.merge(preferences, {
481
+ await writeFile(path.join(profilePath, 'Default', 'Preferences'), JSON.stringify(_.merge(preferences, {
440
482
  gologin
441
483
  })));
442
484
 
@@ -450,12 +492,14 @@ class GoLogin {
450
492
 
451
493
  async commitProfile() {
452
494
  const data = await this.getProfileDataToUpdate();
495
+
453
496
  debug('begin updating', data.length);
454
497
  if (!data.length) {
455
498
  debug('WARN: profile zip data empty - SKIPPING PROFILE COMMIT');
456
499
 
457
500
  return;
458
501
  }
502
+
459
503
  try {
460
504
  debug('Patching profile');
461
505
  await this.postFile('profile', data);
@@ -463,6 +507,7 @@ class GoLogin {
463
507
  catch (e) {
464
508
  debug('CANNOT COMMIT PROFILE', e);
465
509
  }
510
+
466
511
  debug('COMMIT COMPLETED');
467
512
  }
468
513
 
@@ -482,16 +527,16 @@ class GoLogin {
482
527
 
483
528
  async checkPortAvailable(port) {
484
529
  debug('CHECKING PORT AVAILABLE', port);
530
+
485
531
  try {
486
532
  const { stdout, stderr } = await exec(`lsof -i:${port}`);
487
- if (
488
- stdout && stdout.match(/LISTEN/gmi)
489
- ) {
533
+ if (stdout && stdout.match(/LISTEN/gmi)) {
490
534
  debug(`PORT ${port} IS BUSY`)
491
535
  return false;
492
536
  }
493
- } catch (e) { }
537
+ } catch (e) {}
494
538
  debug(`PORT ${port} IS OPEN`);
539
+
495
540
  return true;
496
541
  }
497
542
 
@@ -507,7 +552,7 @@ class GoLogin {
507
552
 
508
553
  async getTimeZone(proxy) {
509
554
  debug('getting timeZone proxy=', proxy);
510
- if(this.timezone){
555
+ if (this.timezone) {
511
556
  debug('getTimeZone from options', this.timezone);
512
557
  this._tz = this.timezone;
513
558
  return this._tz.timezone;
@@ -604,7 +649,7 @@ class GoLogin {
604
649
 
605
650
  async spawnBrowser() {
606
651
  let remote_debugging_port = this.remote_debugging_port;
607
- if(!remote_debugging_port){
652
+ if (!remote_debugging_port) {
608
653
  remote_debugging_port = await this.getRandomPort();
609
654
  }
610
655
 
@@ -706,7 +751,9 @@ class GoLogin {
706
751
  if (this.is_stopping) {
707
752
  return true;
708
753
  }
709
- const is_posting = options.postings || false;
754
+ const is_posting = options.posting ||
755
+ options.postings || // backward compability
756
+ false;
710
757
 
711
758
  if (this.uploadCookiesToServer) {
712
759
  await this.uploadProfileCookiesToServer();
@@ -782,34 +829,39 @@ class GoLogin {
782
829
 
783
830
  async getProfileDataToUpdate() {
784
831
  const zipPath = path.join(this.tmpdir, `gologin_${this.profile_id}_upload.zip`);
785
- try {
786
- fs.unlinkSync(zipPath);
787
- }
788
- catch (e) {
832
+ const zipExists = await access(zipPath).then(() => true).catch(() => false);
833
+ if (zipExists) {
834
+ await unlink(zipPath);
789
835
  }
836
+
790
837
  await this.sanitizeProfile();
791
838
  debug('profile sanitized');
792
- await new Promise(resolve => {
793
- debug('begin zipping');
794
- execFile(`cd ${this.profilePath()}; /usr/bin/zip`, [
795
- `-r ${zipPath}`,
796
- '*'
797
- ], {
798
- shell: true
799
- }, () => {
800
- debug('zipping done');
801
- resolve();
802
- });
803
- });
804
- debug('PROFILE ZIP CREATED', this.profilePath(), zipPath);
839
+
840
+ const profilePath = this.profilePath();
841
+ await new Promise((resolve, reject) => zipdir(profilePath,
842
+ {
843
+ saveTo: zipPath,
844
+ filter: (path) => !/RunningChromeVersion/.test(path),
845
+ }, (err, buffer) => {
846
+ if (err) {
847
+ reject(err);
848
+ return;
849
+ }
850
+
851
+ resolve(buffer);
852
+ })
853
+ )
854
+
855
+ debug('PROFILE ZIP CREATED', profilePath, zipPath);
856
+
857
+ let data = '';
805
858
  try {
806
- const data = fs.readFileSync(zipPath);
807
- return data;
808
- }
809
- catch (e) {
859
+ data = await readFile(zipPath);
860
+ } catch (e) {
810
861
  debug('saveprofile error', e);
811
- return '';
812
862
  }
863
+
864
+ return data;
813
865
  }
814
866
 
815
867
  async profileExists() {
@@ -853,11 +905,11 @@ class GoLogin {
853
905
  const fingerprint = await this.getRandomFingerprint(options);
854
906
  debug("fingerprint=", fingerprint)
855
907
 
856
- if(fingerprint.statusCode == 500){
908
+ if (fingerprint.statusCode === 500) {
857
909
  throw new Error("no valid random fingerprint check os param");
858
910
  }
859
911
 
860
- if(fingerprint.statusCode == 401){
912
+ if (fingerprint.statusCode === 401) {
861
913
  throw new Error("invalid token");
862
914
  }
863
915
 
@@ -887,7 +939,7 @@ class GoLogin {
887
939
  let user_agent = options.navigator?.userAgent;
888
940
  let orig_user_agent = json.navigator.userAgent;
889
941
  Object.keys(options).map((e)=>{ json[e] = options[e] });
890
- if(user_agent=='random'){
942
+ if (user_agent === 'random') {
891
943
  json.navigator.userAgent = orig_user_agent;
892
944
  }
893
945
  // console.log('profileOptions', json);
@@ -900,11 +952,11 @@ class GoLogin {
900
952
  json,
901
953
  });
902
954
 
903
- if(response.body.statusCode==400){
955
+ if (response.body.statusCode === 400) {
904
956
  throw new Error(`gologin failed account creation with status code, ${data.statusCode} DATA ${JSON.stringify(response.body.message)}`);
905
957
  }
906
958
 
907
- if(response.body.statusCode==500){
959
+ if (response.body.statusCode === 500) {
908
960
  throw new Error(`gologin failed account creation with status code, ${data.statusCode}`);
909
961
  }
910
962
  debug(JSON.stringify(response.body));
@@ -1045,7 +1097,8 @@ class GoLogin {
1045
1097
 
1046
1098
  const ORBITA_BROWSER = this.executablePath || this.browserChecker.getOrbitaPath;
1047
1099
 
1048
- if(!fs.existsSync(ORBITA_BROWSER)){
1100
+ const orbitaBrowserExists = await access(ORBITA_BROWSER).then(() => true).catch(() => false);
1101
+ if (!orbitaBrowserExists) {
1049
1102
  throw new Error(`Orbita browser is not exists on path ${ORBITA_BROWSER}, check executablePath param`);
1050
1103
  }
1051
1104
 
@@ -1071,12 +1124,12 @@ class GoLogin {
1071
1124
  return this.stopRemote();
1072
1125
  }
1073
1126
 
1074
- await this.stopAndCommit({ posting: false }, false);
1127
+ await this.stopAndCommit({ posting: true }, false);
1075
1128
  }
1076
1129
 
1077
1130
  async stopLocal(options) {
1078
1131
  const opts = options || { posting: false };
1079
- await this.stopAndCommit(options, true);
1132
+ await this.stopAndCommit(opts, true);
1080
1133
  }
1081
1134
 
1082
1135
  async waitDebuggingUrl(delay_ms, try_count=0) {
@@ -1113,7 +1166,7 @@ class GoLogin {
1113
1166
  }
1114
1167
  });
1115
1168
 
1116
- if(profileResponse.statusCode == 401){
1169
+ if (profileResponse.statusCode === 401){
1117
1170
  throw new Error("invalid token");
1118
1171
  }
1119
1172
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gologin",
3
- "version": "1.0.31",
3
+ "version": "1.0.32",
4
4
  "description": "A high-level API to control Orbita browser over GoLogin API",
5
5
  "main": "./gologin.js",
6
6
  "repository": {
@@ -26,10 +26,10 @@
26
26
  "requestretry": "^4.1.0",
27
27
  "rimraf": "^3.0.2",
28
28
  "selenium-webdriver": "^4.0.0-alpha.7",
29
- "shelljs": "^0.8.4",
30
29
  "simple-proxy-agent": "^1.1.0",
31
30
  "sqlite": "^4.0.23",
32
- "sqlite3": "^5.0.2"
31
+ "sqlite3": "^5.0.2",
32
+ "zip-dir": "^2.0.0"
33
33
  },
34
34
  "bugs": {
35
35
  "url": "https://github.com/gologinapp/gologin/issues"