gologin 2.0.1 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -23,11 +23,11 @@ To have an access to the page below you need <a href="https://app.gologin.com/#/
23
23
  ### Example
24
24
 
25
25
  ```js
26
- import pkg from 'puppeteer-core';
26
+ import puppeteer from 'puppeteer-core';
27
27
 
28
28
  import GoLogin from './gologin.js';
29
29
 
30
- const { connect } = pkg;
30
+ const { connect } = puppeteer;
31
31
 
32
32
  (async () => {
33
33
  const GL = new GoLogin({
@@ -128,11 +128,11 @@ stop current browser without removing archived profile
128
128
  ### example-local-profile.js
129
129
 
130
130
  ```js
131
- import pkg from 'puppeteer-core';
131
+ import puppeteer from 'puppeteer-core';
132
132
 
133
133
  import GoLogin from './gologin.js';
134
134
 
135
- const { connect } = pkg;
135
+ const { connect } = puppeteer;
136
136
 
137
137
  (async () => {
138
138
  const GL = new GoLogin({
@@ -62,7 +62,7 @@ export const downloadFonts = async (fontsList = [], profilePath) => {
62
62
  const files = await readdir(browserFontsPath);
63
63
  const fontsToDownload = fontsList.filter(font => !files.includes(font));
64
64
 
65
- let promises = fontsToDownload.map(font => get(FONTS_URL + font, {
65
+ let promises = fontsToDownload.map(font => requestretry.get(FONTS_URL + font, {
66
66
  maxAttempts: 5,
67
67
  retryDelay: 2000,
68
68
  timeout: 30 * 1000,
@@ -1,6 +1,6 @@
1
1
  import { open } from 'sqlite';
2
- import pkg from 'sqlite3';
3
- const { Database, OPEN_READONLY } = pkg;
2
+ import sqlite3 from 'sqlite3';
3
+ const { Database, OPEN_READONLY } = sqlite3;
4
4
 
5
5
  const MAX_SQLITE_VARIABLES = 76;
6
6
 
@@ -1,8 +1,8 @@
1
- import pkg from 'puppeteer-core';
1
+ import puppeteer from 'puppeteer-core';
2
2
 
3
3
  import GoLogin from './gologin.js';
4
4
 
5
- const { connect } = pkg;
5
+ const { connect } = puppeteer;
6
6
 
7
7
  (async () => {
8
8
  const GL = new GoLogin({
@@ -1,8 +1,8 @@
1
- import pkg from 'puppeteer-core';
1
+ import puppeteer from 'puppeteer-core';
2
2
 
3
3
  import GoLogin from './gologin.js';
4
4
 
5
- const { connect } = pkg;
5
+ const { connect } = puppeteer;
6
6
 
7
7
  const delay = (time) => new Promise((resolve) => setTimeout(resolve, time));
8
8
 
@@ -1,8 +1,8 @@
1
- import pkg from 'puppeteer-core';
1
+ import puppeteer from 'puppeteer-core';
2
2
 
3
3
  import GoLogin from './gologin.js';
4
4
 
5
- const { connect } = pkg;
5
+ const { connect } = puppeteer;
6
6
 
7
7
  (async () => {
8
8
  const GL = new GoLogin({
@@ -1,8 +1,8 @@
1
- import pkg from 'puppeteer-core';
1
+ import puppeteer from 'puppeteer-core';
2
2
 
3
3
  import GoLogin from './gologin.js';
4
4
 
5
- const { connect } = pkg;
5
+ const { connect } = puppeteer;
6
6
 
7
7
  const delay = ms => new Promise(res => setTimeout(res, ms));
8
8
 
@@ -1,8 +1,8 @@
1
- import pkg from 'puppeteer-core';
1
+ import puppeteer from 'puppeteer-core';
2
2
 
3
3
  import GoLogin from './gologin.js';
4
4
 
5
- const { connect } = pkg;
5
+ const { connect } = puppeteer;
6
6
 
7
7
  (async () => {
8
8
  const GL = new GoLogin({
@@ -1,8 +1,8 @@
1
- import pkg from 'puppeteer-core';
1
+ import puppeteer from 'puppeteer-core';
2
2
 
3
3
  import GoLogin from './gologin.js';
4
4
 
5
- const { connect } = pkg;
5
+ const { connect } = puppeteer;
6
6
 
7
7
  const delay = ms => new Promise(res => setTimeout(res, ms));
8
8
 
@@ -1,8 +1,8 @@
1
- import pkg from 'puppeteer-core';
1
+ import puppeteer from 'puppeteer-core';
2
2
 
3
3
  import GoLogin from './gologin.js';
4
4
 
5
- const { connect } = pkg;
5
+ const { connect } = puppeteer;
6
6
 
7
7
  const delay = ms => new Promise(res => setTimeout(res, ms));
8
8
 
package/gologin.js CHANGED
@@ -4,25 +4,25 @@ import decompress from 'decompress';
4
4
  import decompressUnzip from 'decompress-unzip';
5
5
  import { existsSync, mkdirSync, promises as _promises } from 'fs';
6
6
  import { get as _get } from 'https';
7
- import pkg from 'lodash';
7
+ import lodash from 'lodash';
8
8
  import { tmpdir } from 'os';
9
9
  import { join, resolve as _resolve,sep } from 'path';
10
10
  import requests from 'requestretry';
11
11
  import rimraf from 'rimraf';
12
12
  import ProxyAgent from 'simple-proxy-agent';
13
13
  import util from 'util';
14
- import zipdir from 'zip-dir';
15
14
 
16
15
  import BrowserChecker from './browser-checker.js';
17
16
  import { composeFonts, downloadCookies, setExtPathsAndRemoveDeleted, setOriginalExtPaths, uploadCookies } from './browser-user-data-manager.js';
18
17
  import { getChunckedInsertValues, getDB, loadCookiesFromFile } from './cookies-manager.js';
19
18
  import ExtensionsManager from './extensions-manager.js';
20
19
  import { fontsCollection } from './fonts.js';
20
+ import { archiveProfile } from './profile-archiver.js';
21
21
 
22
22
  const exec = util.promisify(execNonPromise);
23
23
 
24
24
  const { access, unlink, writeFile, readFile } = _promises;
25
- const { get, merge } = pkg;
25
+ const { get, merge } = lodash;
26
26
 
27
27
  const SEPARATOR = sep;
28
28
  const API_URL = 'https://api.gologin.com';
@@ -442,7 +442,7 @@ export class GoLogin {
442
442
 
443
443
  return { profileExtensionsCheckRes: [] };
444
444
  }),
445
- ExtensionsManagerInst.checkLocalUserChromeExtensions(userChromeExtensions)
445
+ ExtensionsManagerInst.checkLocalUserChromeExtensions(userChromeExtensions, this.profile_id)
446
446
  .then(res => ({ profileUserExtensionsCheckRes: res }))
447
447
  .catch((error) => {
448
448
  console.log('checkUserChromeExtensions error: ', error);
@@ -1014,20 +1014,7 @@ export class GoLogin {
1014
1014
  debug('profile sanitized');
1015
1015
 
1016
1016
  const profilePath = this.profilePath();
1017
- const fileBuff = await new Promise((resolve, reject) => zipdir(profilePath,
1018
- {
1019
- saveTo: zipPath,
1020
- filter: (path) => !/RunningChromeVersion/.test(path),
1021
- }, (err, buffer) => {
1022
- if (err) {
1023
- reject(err);
1024
-
1025
- return;
1026
- }
1027
-
1028
- resolve(buffer);
1029
- }),
1030
- );
1017
+ const fileBuff = await archiveProfile(profilePath);
1031
1018
 
1032
1019
  debug('PROFILE ZIP CREATED', profilePath, zipPath);
1033
1020
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gologin",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "A high-level API to control Orbita browser over GoLogin API",
5
5
  "main": "./gologin.js",
6
6
  "repository": {
@@ -15,6 +15,7 @@
15
15
  "license": "GPL-3.0",
16
16
  "dependencies": {
17
17
  "archiver": "^3.1.1",
18
+ "adm-zip": "^0.5.1",
18
19
  "child_process": "^1.0.2",
19
20
  "decompress": "^4.2.1",
20
21
  "decompress-unzip": "^4.0.1",
@@ -27,8 +28,7 @@
27
28
  "rimraf": "^3.0.2",
28
29
  "simple-proxy-agent": "^1.1.0",
29
30
  "sqlite": "^4.0.23",
30
- "sqlite3": "^5.0.2",
31
- "zip-dir": "^2.0.0"
31
+ "sqlite3": "^5.0.2"
32
32
  },
33
33
  "bugs": {
34
34
  "url": "https://github.com/gologinapp/gologin/issues"
@@ -0,0 +1,81 @@
1
+ import AdmZip from 'adm-zip';
2
+ import { promises as _promises } from 'fs';
3
+ import path from 'path';
4
+
5
+ import { getDirectoriesForArchiver } from './profile-directories-to-remove.js';
6
+
7
+ const { access } = _promises;
8
+
9
+ export const archiveProfile = async (profileFolder = '', tryAgain = true) => {
10
+ const folderExists = await access(profileFolder).then(() => true, () => false);
11
+ if (!folderExists) {
12
+ throw new Error('Invalid profile folder path: ' + profileFolder);
13
+ }
14
+
15
+ const archive = new AdmZip();
16
+ archive.addLocalFolder(path.join(profileFolder, 'Default'), 'Default');
17
+ archive.addLocalFile(path.join(profileFolder, 'First Run'));
18
+ const dirsToRemove = getDirectoriesForArchiver();
19
+ dirsToRemove.forEach(entry => archive.deleteFile(entry));
20
+
21
+ const archiveIsValid = checkProfileArchiveIsValid(archive);
22
+ if (tryAgain && !archiveIsValid) {
23
+ await new Promise(r => setTimeout(() => r(), 300));
24
+
25
+ return archiveProfile(profileFolder, false);
26
+ }
27
+
28
+ return new Promise((resolve, reject) => archive.toBuffer(resolve, reject));
29
+ };
30
+
31
+ export const decompressProfile = async (zipPath = '', profileFolder = '') => {
32
+ const zipExists = await access(zipPath).then(() => true, () => false);
33
+ if (!zipExists) {
34
+ throw new Error('Invalid zip path: ' + zipPath);
35
+ }
36
+
37
+ const archive = new AdmZip(zipPath);
38
+ archive
39
+ .getEntries()
40
+ .forEach((elem) => {
41
+ if (
42
+ !elem.isDirectory &&
43
+ (
44
+ elem.entryName.includes('RunningChromeVersion') ||
45
+ elem.entryName.includes('SingletonLock') ||
46
+ elem.entryName.includes('SingletonSocket') ||
47
+ elem.entryName.includes('SingletonCookie')
48
+ )
49
+ ) {
50
+ archive.deleteFile(elem);
51
+ }
52
+ });
53
+
54
+ archive.extractAllTo(profileFolder, true);
55
+ };
56
+
57
+ export const checkProfileArchiveIsValid = (zipObject) => {
58
+ if (!zipObject) {
59
+ throw new Error('No zip object provided');
60
+ }
61
+
62
+ return zipObject
63
+ .getEntries()
64
+ .map(elem => {
65
+ if (elem.isDirectory) {
66
+ return false;
67
+ }
68
+
69
+ return elem.entryName.includes('Preferences') || elem.entryName.includes('Cookies');
70
+ })
71
+ .filter(Boolean)
72
+ .length >= 2;
73
+ };
74
+
75
+ const flatArray = (array = []) => array.map((elem) => {
76
+ if (Array.isArray(elem)) {
77
+ return flatArray(elem).flat();
78
+ }
79
+
80
+ return elem;
81
+ }).flat().filter(Boolean);
@@ -0,0 +1,55 @@
1
+ const DEFAULT_FOLDER_USELESS_FILE = [
2
+ {
3
+ name: 'Cache',
4
+ subs: [],
5
+ isDirectory: true,
6
+ },
7
+ {
8
+ name: 'fonts_config',
9
+ subs: [],
10
+ isDirectory: false,
11
+ },
12
+ {
13
+ name: 'Service Worker',
14
+ subs: ['CacheStorage'],
15
+ isDirectory: true,
16
+ },
17
+ {
18
+ name: 'Code Cache',
19
+ subs: [],
20
+ isDirectory: true,
21
+ },
22
+ {
23
+ name: 'GPUCache',
24
+ subs: [],
25
+ isDirectory: true,
26
+ },
27
+ ];
28
+
29
+ export const getDirectoriesToDeleteForNode = (routerSlash = '/') =>
30
+ DEFAULT_FOLDER_USELESS_FILE.reduce((res, el) => {
31
+ const basePath = routerSlash + 'Default' + routerSlash + el.name;
32
+ if (el.subs.length) {
33
+ el.subs.forEach(sub => res.push(basePath + routerSlash + sub));
34
+ } else {
35
+ res.push(basePath);
36
+ }
37
+
38
+ return res;
39
+ }, []);
40
+
41
+ export const getDirectoriesForArchiver = () => DEFAULT_FOLDER_USELESS_FILE.reduce((res, el) => {
42
+ const { name, subs, isDirectory } = el;
43
+ const basePath = 'Default/' + name;
44
+
45
+ if (subs.length) {
46
+ subs.forEach((sub) => {
47
+ const resPath = basePath + '/' + (isDirectory ? sub + '/' : sub);
48
+ res.push(resPath);
49
+ });
50
+ } else {
51
+ res.push(basePath + (isDirectory ? '/' : ''));
52
+ }
53
+
54
+ return res;
55
+ }, []);
@@ -1,15 +1,10 @@
1
1
  import { createWriteStream, promises as _promises } from 'fs';
2
2
  import { join, sep } from 'path';
3
3
  import request from 'requestretry';
4
- import zipdir from 'zip-dir';
5
4
 
6
5
  import { CHROME_EXTENSIONS_PATH, composeExtractionPromises, USER_EXTENSIONS_PATH } from './common.js';
7
- import { extractExtension } from './extensions-extractor.js';
8
6
 
9
- const { readdir, rmdir, readFile, stat, mkdir, copyFile } = _promises;
10
-
11
- const MAX_FILE_SIZE = 80 * 1024 * 1024;
12
- const MAX_FILE_SIZE_MB = MAX_FILE_SIZE / 1024 / 1024;
7
+ const { readdir, readFile, stat, mkdir, copyFile } = _promises;
13
8
 
14
9
  export class UserExtensionsManager {
15
10
  #existedUserExtensions = [];
@@ -78,127 +73,7 @@ export class UserExtensionsManager {
78
73
  this.#existedUserExtensions = fileList;
79
74
  }
80
75
 
81
- async addCustomExtension(pathToFiles) {
82
- try {
83
- const filesSize = await checkFileSizeSync(pathToFiles);
84
- const isZip = pathToFiles.endsWith('.zip');
85
-
86
- if (filesSize > MAX_FILE_SIZE) {
87
- throw new Error(`The maximum file size is ${MAX_FILE_SIZE_MB}MB`);
88
- }
89
-
90
- const customId = this.generateExtensionId();
91
-
92
- if (isZip) {
93
- const pathToExtract = join(USER_EXTENSIONS_PATH, customId);
94
- await extractExtension(pathToFiles, pathToExtract);
95
- pathToFiles = pathToExtract;
96
- }
97
-
98
- let fileList = (await readdir(pathToFiles).catch(() => ['cantReadError']))
99
- .filter(folderContent => folderContent !== '.DS_Store');
100
-
101
- if (fileList.length === 1 && !fileList.includes('cantReadError')) {
102
- const isFolder = (await stat(pathToFiles)).isDirectory();
103
- if (isFolder) {
104
- const [folderName] = fileList;
105
- pathToFiles = join(pathToFiles, folderName);
106
- fileList = await readdir(pathToFiles).catch(() => ['cantReadError']);
107
- }
108
- }
109
-
110
- if (fileList.includes('cantReadError')) {
111
- throw new Error('Can\'t access folder');
112
- }
113
-
114
- if (!fileList.includes('manifest.json')) {
115
- if (isZip) {
116
- rmdir(pathToFiles);
117
- }
118
-
119
- throw new Error('There is no manifest.json in the extension folder');
120
- }
121
-
122
- if (!isZip) {
123
- const destPath = join(USER_EXTENSIONS_PATH, customId);
124
- await copyFolder(pathToFiles, destPath).catch(() => {
125
- throw new Error('Something went wrong coping your folder');
126
- });
127
- }
128
-
129
- const [nameIconId] = await this.getExtensionsNameAndImage([customId], USER_EXTENSIONS_PATH);
130
-
131
- if (!nameIconId) {
132
- throw new Error('Something went wrong. Please try again later');
133
- }
134
-
135
- const dbResult = await request(`${this.#API_BASE_URL}/extensions/create_user_extension`, {
136
- method: 'POST',
137
- headers: {
138
- Authorization: `Bearer ${this.#ACCESS_TOKEN}`,
139
- 'user-agent': this.#USER_AGENT,
140
- 'x-two-factor-token': this.#TWO_FA_KEY || '',
141
- },
142
- body: {
143
- extensionInfo: nameIconId,
144
- },
145
- json: true,
146
- });
147
-
148
- // if success - there is no body
149
- if (dbResult.body) {
150
- throw new Error('Something went wrong inserting your data to database');
151
- }
152
-
153
- const fileBuffer = await zipdir(pathToFiles).catch(() => null);
154
- if (!fileBuffer) {
155
- throw new Error('Something went wrong. Please try again later');
156
- }
157
-
158
- const signedUrl = await request.get(`${this.#API_BASE_URL}/extensions/upload_url?extId=${customId}`, {
159
- headers: {
160
- Authorization: `Bearer ${this.#ACCESS_TOKEN}`,
161
- 'user-agent': this.#USER_AGENT,
162
- 'x-two-factor-token': this.#TWO_FA_KEY || '',
163
- },
164
- maxAttempts: 3,
165
- retryDelay: 2000,
166
- timeout: 10 * 1000,
167
- fullResponse: false,
168
- });
169
-
170
- const uploadResponse = await request.put(signedUrl, {
171
- headers: {
172
- 'Content-Type': 'application/zip',
173
- 'Content-Length': Buffer.byteLength(fileBuffer),
174
- },
175
- body: fileBuffer,
176
- maxBodyLength: Infinity,
177
- maxContentLength: Infinity,
178
- maxAttempts: 3,
179
- retryDelay: 2000,
180
- timeout: 30 * 1000,
181
- fullResponse: true,
182
- });
183
-
184
- // if success - there is no body, in case of error - there will be an error in the body
185
- if (uploadResponse.body) {
186
- throw new Error('Your extension is added locally but we couldn\'t upload it to the cloud');
187
- }
188
-
189
- return {
190
- status: 'success',
191
- message: nameIconId,
192
- };
193
- } catch (e) {
194
- return {
195
- status: 'error',
196
- message: e.message,
197
- };
198
- }
199
- }
200
-
201
- checkLocalUserChromeExtensions = async (userChromeExtensions) => {
76
+ checkLocalUserChromeExtensions = async (userChromeExtensions, profileId) => {
202
77
  if (!userChromeExtensions.length) {
203
78
  return;
204
79
  }
@@ -213,6 +88,8 @@ export class UserExtensionsManager {
213
88
  },
214
89
  body: {
215
90
  existedUserChromeExtensions: this.#existedUserExtensions,
91
+ profileId,
92
+ userChromeExtensions,
216
93
  },
217
94
  }) || [];
218
95