dbgate-api 4.8.0 → 4.8.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dbgate-api",
3
3
  "main": "src/index.js",
4
- "version": "4.8.0",
4
+ "version": "4.8.3",
5
5
  "homepage": "https://dbgate.org/",
6
6
  "repository": {
7
7
  "type": "git",
@@ -25,9 +25,9 @@
25
25
  "compare-versions": "^3.6.0",
26
26
  "cors": "^2.8.5",
27
27
  "cross-env": "^6.0.3",
28
- "dbgate-query-splitter": "^4.8.0",
29
- "dbgate-sqltree": "^4.8.0",
30
- "dbgate-tools": "^4.8.0",
28
+ "dbgate-query-splitter": "^4.8.3",
29
+ "dbgate-sqltree": "^4.8.3",
30
+ "dbgate-tools": "^4.8.3",
31
31
  "diff": "^5.0.0",
32
32
  "diff2html": "^3.4.13",
33
33
  "eslint": "^6.8.0",
@@ -63,7 +63,7 @@
63
63
  "devDependencies": {
64
64
  "@types/fs-extra": "^9.0.11",
65
65
  "@types/lodash": "^4.14.149",
66
- "dbgate-types": "^4.8.0",
66
+ "dbgate-types": "^4.8.3",
67
67
  "env-cmd": "^10.1.0",
68
68
  "node-loader": "^1.0.2",
69
69
  "nodemon": "^2.0.2",
@@ -176,8 +176,8 @@ module.exports = {
176
176
  },
177
177
 
178
178
  loadKeys_meta: true,
179
- async loadKeys({ conid, database, root }) {
180
- return this.loadDataCore('loadKeys', { conid, database, root });
179
+ async loadKeys({ conid, database, root, filter }) {
180
+ return this.loadDataCore('loadKeys', { conid, database, root, filter });
181
181
  },
182
182
 
183
183
  exportKeys_meta: true,
@@ -1,3 +1,4 @@
1
+ const { filterName } = require('dbgate-tools');
1
2
  const fs = require('fs');
2
3
  const lineReader = require('line-reader');
3
4
  const _ = require('lodash');
@@ -148,6 +149,19 @@ module.exports = {
148
149
  return {};
149
150
  },
150
151
 
152
+ loadFieldValues_meta: true,
153
+ async loadFieldValues({ jslid, field, search }) {
154
+ const datastore = await this.ensureDatastore(jslid);
155
+ const res = new Set();
156
+ await datastore.enumRows(row => {
157
+ if (!filterName(search, row[field])) return true;
158
+ res.add(row[field]);
159
+ return res.size < 100;
160
+ });
161
+ // @ts-ignore
162
+ return [...res].map(value => ({ value }));
163
+ },
164
+
151
165
  async notifyChangedStats(stats) {
152
166
  // console.log('SENDING STATS', JSON.stringify(stats));
153
167
  const datastore = this.datastores[stats.jslid];
@@ -125,6 +125,7 @@ module.exports = {
125
125
  socket.emitChanged(`installed-plugins-changed`);
126
126
  // this.removedPlugins = this.removedPlugins.filter(x => x != packageName);
127
127
  // await this.saveRemovePlugins();
128
+ return true;
128
129
  },
129
130
 
130
131
  uninstall_meta: true,
@@ -134,7 +135,8 @@ module.exports = {
134
135
  await fs.rmdir(dir, { recursive: true });
135
136
  socket.emitChanged(`installed-plugins-changed`);
136
137
  // this.removedPlugins.push(packageName);
137
- await this.saveRemovePlugins();
138
+ // await this.saveRemovePlugins();
139
+ return true;
138
140
  },
139
141
 
140
142
  upgrade_meta: true,
@@ -148,6 +150,7 @@ module.exports = {
148
150
  }
149
151
 
150
152
  socket.emitChanged(`installed-plugins-changed`);
153
+ return true;
151
154
  },
152
155
 
153
156
  command_meta: true,
@@ -104,6 +104,7 @@ module.exports = {
104
104
  scriptFile,
105
105
  [
106
106
  '--checkParent', // ...process.argv.slice(3)
107
+ '--is-forked-api',
107
108
  ...processArgs.getPassArgs(),
108
109
  ],
109
110
  {
@@ -1,5 +1,5 @@
1
1
 
2
2
  module.exports = {
3
- version: '4.8.0',
4
- buildTime: '2022-03-28T17:32:31.391Z'
3
+ version: '4.8.3',
4
+ buildTime: '2022-04-05T20:11:01.385Z'
5
5
  };
@@ -197,8 +197,8 @@ async function handleCollectionData({ msgid, options }) {
197
197
  return handleDriverDataCore(msgid, driver => driver.readCollection(systemConnection, options));
198
198
  }
199
199
 
200
- async function handleLoadKeys({ msgid, root }) {
201
- return handleDriverDataCore(msgid, driver => driver.loadKeys(systemConnection, root));
200
+ async function handleLoadKeys({ msgid, root, filter }) {
201
+ return handleDriverDataCore(msgid, driver => driver.loadKeys(systemConnection, root, filter));
202
202
  }
203
203
 
204
204
  async function handleExportKeys({ msgid, options }) {
package/src/proc/index.js CHANGED
@@ -3,6 +3,7 @@ const databaseConnectionProcess = require('./databaseConnectionProcess');
3
3
  const serverConnectionProcess = require('./serverConnectionProcess');
4
4
  const sessionProcess = require('./sessionProcess');
5
5
  const jslDatastoreProcess = require('./jslDatastoreProcess');
6
+ const sshForwardProcess = require('./sshForwardProcess');
6
7
 
7
8
  module.exports = {
8
9
  connectProcess,
@@ -10,4 +11,5 @@ module.exports = {
10
11
  serverConnectionProcess,
11
12
  sessionProcess,
12
13
  jslDatastoreProcess,
14
+ sshForwardProcess,
13
15
  };
@@ -0,0 +1,68 @@
1
+ const fs = require('fs-extra');
2
+ const platformInfo = require('../utility/platformInfo');
3
+ const childProcessChecker = require('../utility/childProcessChecker');
4
+ const { SSHConnection } = require('node-ssh-forward');
5
+ const { handleProcessCommunication } = require('../utility/processComm');
6
+
7
+ async function getSshConnection(connection) {
8
+ const sshConfig = {
9
+ endHost: connection.sshHost || '',
10
+ endPort: connection.sshPort || 22,
11
+ bastionHost: connection.sshBastionHost || '',
12
+ agentForward: connection.sshMode == 'agent',
13
+ passphrase: connection.sshMode == 'keyFile' ? connection.sshKeyfilePassword : undefined,
14
+ username: connection.sshLogin,
15
+ password: connection.sshMode == 'userPassword' ? connection.sshPassword : undefined,
16
+ agentSocket: connection.sshMode == 'agent' ? platformInfo.sshAuthSock : undefined,
17
+ privateKey:
18
+ connection.sshMode == 'keyFile' && connection.sshKeyfile ? await fs.readFile(connection.sshKeyfile) : undefined,
19
+ skipAutoPrivateKey: true,
20
+ noReadline: true,
21
+ };
22
+
23
+ const sshConn = new SSHConnection(sshConfig);
24
+ return sshConn;
25
+ }
26
+
27
+ async function handleStart({ connection, tunnelConfig }) {
28
+ try {
29
+ const sshConn = await getSshConnection(connection);
30
+ await sshConn.forward(tunnelConfig);
31
+
32
+ process.send({
33
+ msgtype: 'connected',
34
+ connection,
35
+ tunnelConfig,
36
+ });
37
+ } catch (err) {
38
+ process.send({
39
+ msgtype: 'error',
40
+ connection,
41
+ tunnelConfig,
42
+ errorMessage: err.message,
43
+ });
44
+ }
45
+ }
46
+
47
+ const messageHandlers = {
48
+ connect: handleStart,
49
+ };
50
+
51
+ async function handleMessage({ msgtype, ...other }) {
52
+ const handler = messageHandlers[msgtype];
53
+ await handler(other);
54
+ }
55
+
56
+ function start() {
57
+ childProcessChecker();
58
+ process.on('message', async message => {
59
+ if (handleProcessCommunication(message)) return;
60
+ try {
61
+ await handleMessage(message);
62
+ } catch (e) {
63
+ console.error('sshForwardProcess - unhandled error', e);
64
+ }
65
+ });
66
+ }
67
+
68
+ module.exports = { start };
@@ -159,10 +159,23 @@ class JsonLinesDatastore {
159
159
  }
160
160
  }
161
161
 
162
+ async enumRows(eachRow) {
163
+ await lock.acquire('reader', async () => {
164
+ await this._ensureReader(0, null);
165
+ for (;;) {
166
+ const line = await this._readLine(true);
167
+ if (line == null) break;
168
+ const shouldContinue = eachRow(line);
169
+ if (!shouldContinue) break;
170
+ }
171
+ });
172
+ }
173
+
162
174
  async getRows(offset, limit, filter) {
163
175
  const res = [];
164
176
  await lock.acquire('reader', async () => {
165
177
  await this._ensureReader(offset, filter);
178
+ // console.log(JSON.stringify(this.currentFilter, undefined, 2));
166
179
  for (let i = 0; i < limit; i += 1) {
167
180
  const line = await this._readLine(true);
168
181
  if (line == null) break;
@@ -8,7 +8,7 @@ const processArgs = require('./processArgs');
8
8
  const createDirectories = {};
9
9
  const ensureDirectory = (dir, clean) => {
10
10
  if (!createDirectories[dir]) {
11
- if (clean && fs.existsSync(dir)) {
11
+ if (clean && fs.existsSync(dir) && !platformInfo.isForkedApi) {
12
12
  console.log(`Cleaning directory ${dir}`);
13
13
  cleanDirectory(dir);
14
14
  }
@@ -1,13 +1,11 @@
1
- const { SSHConnection } = require('node-ssh-forward');
2
- const fs = require('fs-extra');
3
1
  const portfinder = require('portfinder');
4
2
  const stableStringify = require('json-stable-stringify');
5
3
  const _ = require('lodash');
6
- const platformInfo = require('./platformInfo');
7
4
  const AsyncLock = require('async-lock');
8
5
  const lock = new AsyncLock();
6
+ const { fork } = require('child_process');
7
+ const processArgs = require('../utility/processArgs');
9
8
 
10
- const sshConnectionCache = {};
11
9
  const sshTunnelCache = {};
12
10
 
13
11
  const CONNECTION_FIELDS = [
@@ -22,37 +20,42 @@ const CONNECTION_FIELDS = [
22
20
  ];
23
21
  const TUNNEL_FIELDS = [...CONNECTION_FIELDS, 'server', 'port'];
24
22
 
25
- async function getSshConnection(connection) {
26
- const connectionCacheKey = stableStringify(_.pick(connection, CONNECTION_FIELDS));
27
- if (sshConnectionCache[connectionCacheKey]) return sshConnectionCache[connectionCacheKey];
23
+ function callForwardProcess(connection, tunnelConfig, tunnelCacheKey) {
24
+ let subprocess = fork(global['API_PACKAGE'] || process.argv[1], [
25
+ '--is-forked-api',
26
+ '--start-process',
27
+ 'sshForwardProcess',
28
+ ...processArgs.getPassArgs(),
29
+ ]);
28
30
 
29
- const sshConfig = {
30
- endHost: connection.sshHost || '',
31
- endPort: connection.sshPort || 22,
32
- bastionHost: connection.sshBastionHost || '',
33
- agentForward: connection.sshMode == 'agent',
34
- passphrase: connection.sshMode == 'keyFile' ? connection.sshKeyfilePassword : undefined,
35
- username: connection.sshLogin,
36
- password: connection.sshMode == 'userPassword' ? connection.sshPassword : undefined,
37
- agentSocket: connection.sshMode == 'agent' ? platformInfo.sshAuthSock : undefined,
38
- privateKey:
39
- connection.sshMode == 'keyFile' && connection.sshKeyfile ? await fs.readFile(connection.sshKeyfile) : undefined,
40
- skipAutoPrivateKey: true,
41
- noReadline: true,
42
- };
43
-
44
- const sshConn = new SSHConnection(sshConfig);
45
- sshConnectionCache[connectionCacheKey] = sshConn;
46
- return sshConn;
31
+ subprocess.send({
32
+ msgtype: 'connect',
33
+ connection,
34
+ tunnelConfig,
35
+ });
36
+ return new Promise((resolve, reject) => {
37
+ subprocess.on('message', resp => {
38
+ // @ts-ignore
39
+ const { msgtype, errorMessage } = resp;
40
+ if (msgtype == 'connected') {
41
+ resolve(subprocess);
42
+ }
43
+ if (msgtype == 'error') {
44
+ reject(errorMessage);
45
+ }
46
+ });
47
+ subprocess.on('exit', code => {
48
+ console.log('SSH forward process exited');
49
+ delete sshTunnelCache[tunnelCacheKey];
50
+ });
51
+ });
47
52
  }
48
53
 
49
54
  async function getSshTunnel(connection) {
50
55
  const tunnelCacheKey = stableStringify(_.pick(connection, TUNNEL_FIELDS));
51
56
 
52
57
  return await lock.acquire(tunnelCacheKey, async () => {
53
- const sshConn = await getSshConnection(connection);
54
58
  if (sshTunnelCache[tunnelCacheKey]) return sshTunnelCache[tunnelCacheKey];
55
-
56
59
  const localPort = await portfinder.getPortPromise({ port: 10000, stopPort: 60000 });
57
60
  // workaround for `getPortPromise` not releasing the port quickly enough
58
61
  await new Promise(resolve => setTimeout(resolve, 500));
@@ -66,7 +69,8 @@ async function getSshTunnel(connection) {
66
69
  `Creating SSH tunnel to ${connection.sshHost}-${connection.server}:${connection.port}, using local port ${localPort}`
67
70
  );
68
71
 
69
- const tunnel = await sshConn.forward(tunnelConfig);
72
+ const subprocess = await callForwardProcess(connection, tunnelConfig, tunnelCacheKey);
73
+
70
74
  console.log(
71
75
  `Created SSH tunnel to ${connection.sshHost}-${connection.server}:${connection.port}, using local port ${localPort}`
72
76
  );
@@ -74,6 +78,7 @@ async function getSshTunnel(connection) {
74
78
  sshTunnelCache[tunnelCacheKey] = {
75
79
  state: 'ok',
76
80
  localPort,
81
+ subprocess,
77
82
  };
78
83
  return sshTunnelCache[tunnelCacheKey];
79
84
  } catch (err) {
@@ -31,9 +31,14 @@ module.exports = function useController(app, electron, route, controller) {
31
31
  const handler = `${route.substring(1)}-${_.kebabCase(key)}`;
32
32
  // console.log('REGISTERING HANDLER', handler);
33
33
  electron.ipcMain.handle(handler, async (event, args) => {
34
- const data = await controller[key](args);
35
- // console.log('HANDLED API', handler, data);
36
- return data;
34
+ try {
35
+ const data = await controller[key](args);
36
+ // console.log('HANDLED API', handler, data);
37
+ if (data === undefined) return null;
38
+ return data;
39
+ } catch (err) {
40
+ return { apiErrorMessage: err.message };
41
+ }
37
42
  });
38
43
  }
39
44
 
@@ -68,7 +73,7 @@ module.exports = function useController(app, electron, route, controller) {
68
73
  res.json(data);
69
74
  } catch (e) {
70
75
  console.log(e);
71
- res.status(500).json({ error: e.message });
76
+ res.status(500).json({ apiErrorMessage: e.message });
72
77
  }
73
78
  });
74
79
  }