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 +5 -5
- package/src/controllers/databaseConnections.js +2 -2
- package/src/controllers/jsldata.js +14 -0
- package/src/controllers/plugins.js +4 -1
- package/src/controllers/runners.js +1 -0
- package/src/currentVersion.js +2 -2
- package/src/proc/databaseConnectionProcess.js +2 -2
- package/src/proc/index.js +2 -0
- package/src/proc/sshForwardProcess.js +68 -0
- package/src/utility/JsonLinesDatastore.js +13 -0
- package/src/utility/directories.js +1 -1
- package/src/utility/sshTunnel.js +33 -28
- package/src/utility/useController.js +9 -4
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.
|
|
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.
|
|
29
|
-
"dbgate-sqltree": "^4.8.
|
|
30
|
-
"dbgate-tools": "^4.8.
|
|
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.
|
|
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,
|
package/src/currentVersion.js
CHANGED
|
@@ -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
|
}
|
package/src/utility/sshTunnel.js
CHANGED
|
@@ -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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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({
|
|
76
|
+
res.status(500).json({ apiErrorMessage: e.message });
|
|
72
77
|
}
|
|
73
78
|
});
|
|
74
79
|
}
|