dbgate-api 4.7.3 → 4.7.4-alpha.12
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/.env +13 -0
- package/env/singledb/.env +2 -0
- package/package.json +5 -5
- package/src/controllers/config.js +19 -5
- package/src/controllers/connections.js +22 -5
- package/src/controllers/databaseConnections.js +72 -5
- package/src/controllers/files.js +22 -17
- package/src/controllers/jsldata.js +14 -10
- package/src/controllers/plugins.js +8 -8
- package/src/controllers/runners.js +13 -2
- package/src/controllers/scheduler.js +3 -3
- package/src/controllers/serverConnections.js +1 -1
- package/src/controllers/sessions.js +42 -3
- package/src/currentVersion.js +2 -2
- package/src/index.js +1 -1
- package/src/main.js +21 -12
- package/src/proc/connectProcess.js +1 -1
- package/src/proc/databaseConnectionProcess.js +58 -3
- package/src/proc/serverConnectionProcess.js +6 -3
- package/src/proc/sessionProcess.js +92 -7
- package/src/shell/executeQuery.js +1 -1
- package/src/shell/generateDeploySql.js +1 -1
- package/src/shell/queryReader.js +17 -5
- package/src/shell/tableReader.js +2 -2
- package/src/shell/tableWriter.js +1 -1
- package/src/utility/connectUtility.js +39 -3
- package/src/utility/crypting.js +7 -1
- package/src/utility/directories.js +1 -1
- package/src/utility/hasPermission.js +50 -6
- package/src/utility/platformInfo.js +3 -1
- package/src/utility/useController.js +1 -1
package/src/main.js
CHANGED
|
@@ -29,6 +29,8 @@ const queryHistory = require('./controllers/queryHistory');
|
|
|
29
29
|
const { rundir } = require('./utility/directories');
|
|
30
30
|
const platformInfo = require('./utility/platformInfo');
|
|
31
31
|
const getExpressPath = require('./utility/getExpressPath');
|
|
32
|
+
const { getLogins } = require('./utility/hasPermission');
|
|
33
|
+
const _ = require('lodash');
|
|
32
34
|
|
|
33
35
|
function start() {
|
|
34
36
|
// console.log('process.argv', process.argv);
|
|
@@ -37,12 +39,11 @@ function start() {
|
|
|
37
39
|
|
|
38
40
|
const server = http.createServer(app);
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
const logins = getLogins();
|
|
43
|
+
if (logins) {
|
|
41
44
|
app.use(
|
|
42
45
|
basicAuth({
|
|
43
|
-
users:
|
|
44
|
-
[process.env.LOGIN]: process.env.PASSWORD,
|
|
45
|
-
},
|
|
46
|
+
users: _.fromPairs(logins.map(x => [x.login, x.password])),
|
|
46
47
|
challenge: true,
|
|
47
48
|
realm: 'DbGate Web App',
|
|
48
49
|
})
|
|
@@ -85,15 +86,11 @@ function start() {
|
|
|
85
86
|
if (platformInfo.isDocker) {
|
|
86
87
|
// server static files inside docker container
|
|
87
88
|
app.use(getExpressPath('/'), express.static('/home/dbgate-docker/public'));
|
|
88
|
-
} else {
|
|
89
|
-
if (!platformInfo.isNpmDist) {
|
|
90
|
-
app.get(getExpressPath('/'), (req, res) => {
|
|
91
|
-
res.send('DbGate API');
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
89
|
|
|
96
|
-
|
|
90
|
+
const port = process.env.PORT || 3000;
|
|
91
|
+
console.log('DbGate API listening on port', port);
|
|
92
|
+
server.listen(port);
|
|
93
|
+
} else if (platformInfo.isNpmDist) {
|
|
97
94
|
app.use(getExpressPath('/'), express.static(path.join(__dirname, '../../dbgate-web/public')));
|
|
98
95
|
getPort({
|
|
99
96
|
port: parseInt(
|
|
@@ -105,7 +102,19 @@ function start() {
|
|
|
105
102
|
console.log(`DbGate API listening on port ${port}`);
|
|
106
103
|
});
|
|
107
104
|
});
|
|
105
|
+
} else if (process.env.DEVWEB) {
|
|
106
|
+
console.log('__dirname', __dirname);
|
|
107
|
+
console.log(path.join(__dirname, '../../web/public/build'));
|
|
108
|
+
app.use(getExpressPath('/'), express.static(path.join(__dirname, '../../web/public')));
|
|
109
|
+
|
|
110
|
+
const port = process.env.PORT || 3000;
|
|
111
|
+
console.log('DbGate API & web listening on port', port);
|
|
112
|
+
server.listen(port);
|
|
108
113
|
} else {
|
|
114
|
+
app.get(getExpressPath('/'), (req, res) => {
|
|
115
|
+
res.send('DbGate API');
|
|
116
|
+
});
|
|
117
|
+
|
|
109
118
|
const port = process.env.PORT || 3000;
|
|
110
119
|
console.log('DbGate API listening on port', port);
|
|
111
120
|
server.listen(port);
|
|
@@ -20,7 +20,7 @@ function start() {
|
|
|
20
20
|
if (handleProcessCommunication(connection)) return;
|
|
21
21
|
try {
|
|
22
22
|
const driver = requireEngineDriver(connection);
|
|
23
|
-
const conn = await connectUtility(driver, connection);
|
|
23
|
+
const conn = await connectUtility(driver, connection, 'app');
|
|
24
24
|
const res = await driver.getVersion(conn);
|
|
25
25
|
process.send({ msgtype: 'connected', ...res });
|
|
26
26
|
} catch (e) {
|
|
@@ -7,6 +7,7 @@ const connectUtility = require('../utility/connectUtility');
|
|
|
7
7
|
const { handleProcessCommunication } = require('../utility/processComm');
|
|
8
8
|
const { SqlGenerator } = require('dbgate-tools');
|
|
9
9
|
const generateDeploySql = require('../shell/generateDeploySql');
|
|
10
|
+
const { dumpSqlSelect } = require('dbgate-sqltree');
|
|
10
11
|
|
|
11
12
|
let systemConnection;
|
|
12
13
|
let storedConnection;
|
|
@@ -107,7 +108,7 @@ async function handleConnect({ connection, structure, globalSettings }) {
|
|
|
107
108
|
|
|
108
109
|
if (!structure) setStatusName('pending');
|
|
109
110
|
const driver = requireEngineDriver(storedConnection);
|
|
110
|
-
systemConnection = await checkedAsyncCall(connectUtility(driver, storedConnection));
|
|
111
|
+
systemConnection = await checkedAsyncCall(connectUtility(driver, storedConnection, 'app'));
|
|
111
112
|
await checkedAsyncCall(readVersion());
|
|
112
113
|
if (structure) {
|
|
113
114
|
analysedStructure = structure;
|
|
@@ -154,6 +155,7 @@ async function handleRunScript({ msgid, sql }) {
|
|
|
154
155
|
await waitConnected();
|
|
155
156
|
const driver = requireEngineDriver(storedConnection);
|
|
156
157
|
try {
|
|
158
|
+
ensureExecuteCustomScript(driver);
|
|
157
159
|
await driver.script(systemConnection, sql);
|
|
158
160
|
process.send({ msgtype: 'response', msgid });
|
|
159
161
|
} catch (err) {
|
|
@@ -165,6 +167,7 @@ async function handleQueryData({ msgid, sql }) {
|
|
|
165
167
|
await waitConnected();
|
|
166
168
|
const driver = requireEngineDriver(storedConnection);
|
|
167
169
|
try {
|
|
170
|
+
ensureExecuteCustomScript(driver);
|
|
168
171
|
const res = await driver.query(systemConnection, sql);
|
|
169
172
|
process.send({ msgtype: 'response', msgid, ...res });
|
|
170
173
|
} catch (err) {
|
|
@@ -172,21 +175,67 @@ async function handleQueryData({ msgid, sql }) {
|
|
|
172
175
|
}
|
|
173
176
|
}
|
|
174
177
|
|
|
175
|
-
async function
|
|
178
|
+
async function handleSqlSelect({ msgid, select }) {
|
|
179
|
+
const driver = requireEngineDriver(storedConnection);
|
|
180
|
+
const dmp = driver.createDumper();
|
|
181
|
+
dumpSqlSelect(dmp, select);
|
|
182
|
+
return handleQueryData({ msgid, sql: dmp.s });
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async function handleDriverDataCore(msgid, callMethod) {
|
|
176
186
|
await waitConnected();
|
|
177
187
|
const driver = requireEngineDriver(storedConnection);
|
|
178
188
|
try {
|
|
179
|
-
const result = await driver
|
|
189
|
+
const result = await callMethod(driver);
|
|
180
190
|
process.send({ msgtype: 'response', msgid, result });
|
|
181
191
|
} catch (err) {
|
|
182
192
|
process.send({ msgtype: 'response', msgid, errorMessage: err.message });
|
|
183
193
|
}
|
|
184
194
|
}
|
|
185
195
|
|
|
196
|
+
async function handleCollectionData({ msgid, options }) {
|
|
197
|
+
return handleDriverDataCore(msgid, driver => driver.readCollection(systemConnection, options));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async function handleLoadKeys({ msgid, root }) {
|
|
201
|
+
return handleDriverDataCore(msgid, driver => driver.loadKeys(systemConnection, root));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async function handleLoadKeyInfo({ msgid, key }) {
|
|
205
|
+
return handleDriverDataCore(msgid, driver => driver.loadKeyInfo(systemConnection, key));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async function handleCallMethod({ msgid, method, args }) {
|
|
209
|
+
return handleDriverDataCore(msgid, driver => {
|
|
210
|
+
ensureExecuteCustomScript(driver);
|
|
211
|
+
return driver.callMethod(systemConnection, method, args);
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function handleLoadKeyTableRange({ msgid, key, cursor, count }) {
|
|
216
|
+
return handleDriverDataCore(msgid, driver => driver.loadKeyTableRange(systemConnection, key, cursor, count));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async function handleLoadFieldValues({ msgid, schemaName, pureName, field, search }) {
|
|
220
|
+
return handleDriverDataCore(msgid, driver =>
|
|
221
|
+
driver.loadFieldValues(systemConnection, { schemaName, pureName }, field, search)
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function ensureExecuteCustomScript(driver) {
|
|
226
|
+
if (driver.readOnlySessions) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (storedConnection.isReadOnly) {
|
|
230
|
+
throw new Error('Connection is read only');
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
186
234
|
async function handleUpdateCollection({ msgid, changeSet }) {
|
|
187
235
|
await waitConnected();
|
|
188
236
|
const driver = requireEngineDriver(storedConnection);
|
|
189
237
|
try {
|
|
238
|
+
ensureExecuteCustomScript(driver);
|
|
190
239
|
const result = await driver.updateCollection(systemConnection, changeSet);
|
|
191
240
|
process.send({ msgtype: 'response', msgid, result });
|
|
192
241
|
} catch (err) {
|
|
@@ -248,10 +297,16 @@ const messageHandlers = {
|
|
|
248
297
|
runScript: handleRunScript,
|
|
249
298
|
updateCollection: handleUpdateCollection,
|
|
250
299
|
collectionData: handleCollectionData,
|
|
300
|
+
loadKeys: handleLoadKeys,
|
|
301
|
+
loadKeyInfo: handleLoadKeyInfo,
|
|
302
|
+
callMethod: handleCallMethod,
|
|
303
|
+
loadKeyTableRange: handleLoadKeyTableRange,
|
|
251
304
|
sqlPreview: handleSqlPreview,
|
|
252
305
|
ping: handlePing,
|
|
253
306
|
syncModel: handleSyncModel,
|
|
254
307
|
generateDeploySql: handleGenerateDeploySql,
|
|
308
|
+
loadFieldValues: handleLoadFieldValues,
|
|
309
|
+
sqlSelect: handleSqlSelect,
|
|
255
310
|
// runCommand: handleRunCommand,
|
|
256
311
|
};
|
|
257
312
|
|
|
@@ -58,11 +58,14 @@ async function handleConnect(connection) {
|
|
|
58
58
|
|
|
59
59
|
const driver = requireEngineDriver(storedConnection);
|
|
60
60
|
try {
|
|
61
|
-
systemConnection = await connectUtility(driver, storedConnection);
|
|
61
|
+
systemConnection = await connectUtility(driver, storedConnection, 'app');
|
|
62
62
|
readVersion();
|
|
63
63
|
handleRefresh();
|
|
64
64
|
if (extractBoolSettingsValue(globalSettings, 'connection.autoRefresh', false)) {
|
|
65
|
-
setInterval(
|
|
65
|
+
setInterval(
|
|
66
|
+
handleRefresh,
|
|
67
|
+
extractIntSettingsValue(globalSettings, 'connection.autoRefreshInterval', 30, 5, 3600) * 1000
|
|
68
|
+
);
|
|
66
69
|
}
|
|
67
70
|
} catch (err) {
|
|
68
71
|
setStatus({
|
|
@@ -80,7 +83,7 @@ function handlePing() {
|
|
|
80
83
|
|
|
81
84
|
async function handleCreateDatabase({ name }) {
|
|
82
85
|
const driver = requireEngineDriver(storedConnection);
|
|
83
|
-
systemConnection = await connectUtility(driver, storedConnection);
|
|
86
|
+
systemConnection = await connectUtility(driver, storedConnection, 'app');
|
|
84
87
|
console.log(`RUNNING SCRIPT: CREATE DATABASE ${driver.dialect.quoteIdentifier(name)}`);
|
|
85
88
|
if (driver.createDatabase) {
|
|
86
89
|
await driver.createDatabase(systemConnection, name);
|
|
@@ -17,11 +17,15 @@ let afterConnectCallbacks = [];
|
|
|
17
17
|
// let currentHandlers = [];
|
|
18
18
|
|
|
19
19
|
class TableWriter {
|
|
20
|
-
constructor(
|
|
21
|
-
this.jslid = uuidv1();
|
|
22
|
-
this.currentFile = path.join(jsldir(), `${this.jslid}.jsonl`);
|
|
20
|
+
constructor() {
|
|
23
21
|
this.currentRowCount = 0;
|
|
24
22
|
this.currentChangeIndex = 1;
|
|
23
|
+
this.initializedFile = false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
initializeFromQuery(structure, resultIndex) {
|
|
27
|
+
this.jslid = uuidv1();
|
|
28
|
+
this.currentFile = path.join(jsldir(), `${this.jslid}.jsonl`);
|
|
25
29
|
fs.writeFileSync(
|
|
26
30
|
this.currentFile,
|
|
27
31
|
JSON.stringify({
|
|
@@ -32,13 +36,21 @@ class TableWriter {
|
|
|
32
36
|
this.currentStream = fs.createWriteStream(this.currentFile, { flags: 'a' });
|
|
33
37
|
this.writeCurrentStats(false, false);
|
|
34
38
|
this.resultIndex = resultIndex;
|
|
39
|
+
this.initializedFile = true;
|
|
35
40
|
process.send({ msgtype: 'recordset', jslid: this.jslid, resultIndex });
|
|
36
41
|
}
|
|
37
42
|
|
|
43
|
+
initializeFromReader(jslid) {
|
|
44
|
+
this.jslid = jslid;
|
|
45
|
+
this.currentFile = path.join(jsldir(), `${this.jslid}.jsonl`);
|
|
46
|
+
this.writeCurrentStats(false, false);
|
|
47
|
+
}
|
|
48
|
+
|
|
38
49
|
row(row) {
|
|
39
50
|
// console.log('ACCEPT ROW', row);
|
|
40
51
|
this.currentStream.write(JSON.stringify(row) + '\n');
|
|
41
52
|
this.currentRowCount += 1;
|
|
53
|
+
|
|
42
54
|
if (!this.plannedStats) {
|
|
43
55
|
this.plannedStats = true;
|
|
44
56
|
process.nextTick(() => {
|
|
@@ -49,6 +61,21 @@ class TableWriter {
|
|
|
49
61
|
}
|
|
50
62
|
}
|
|
51
63
|
|
|
64
|
+
rowFromReader(row) {
|
|
65
|
+
if (!this.initializedFile) {
|
|
66
|
+
process.send({ msgtype: 'initializeFile', jslid: this.jslid });
|
|
67
|
+
this.initializedFile = true;
|
|
68
|
+
|
|
69
|
+
fs.writeFileSync(this.currentFile, JSON.stringify(row) + '\n');
|
|
70
|
+
this.currentStream = fs.createWriteStream(this.currentFile, { flags: 'a' });
|
|
71
|
+
this.writeCurrentStats(false, false);
|
|
72
|
+
this.initializedFile = true;
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
this.row(row);
|
|
77
|
+
}
|
|
78
|
+
|
|
52
79
|
writeCurrentStats(isFinished = false, emitEvent = false) {
|
|
53
80
|
const stats = {
|
|
54
81
|
rowCount: this.currentRowCount,
|
|
@@ -63,10 +90,11 @@ class TableWriter {
|
|
|
63
90
|
}
|
|
64
91
|
}
|
|
65
92
|
|
|
66
|
-
close() {
|
|
93
|
+
close(afterClose) {
|
|
67
94
|
if (this.currentStream) {
|
|
68
95
|
this.currentStream.end(() => {
|
|
69
96
|
this.writeCurrentStats(true, true);
|
|
97
|
+
if (afterClose) afterClose();
|
|
70
98
|
});
|
|
71
99
|
}
|
|
72
100
|
}
|
|
@@ -98,7 +126,11 @@ class StreamHandler {
|
|
|
98
126
|
|
|
99
127
|
recordset(columns) {
|
|
100
128
|
this.closeCurrentWriter();
|
|
101
|
-
this.currentWriter = new TableWriter(
|
|
129
|
+
this.currentWriter = new TableWriter();
|
|
130
|
+
this.currentWriter.initializeFromQuery(
|
|
131
|
+
Array.isArray(columns) ? { columns } : columns,
|
|
132
|
+
this.resultIndexHolder.value
|
|
133
|
+
);
|
|
102
134
|
this.resultIndexHolder.value += 1;
|
|
103
135
|
|
|
104
136
|
// this.writeCurrentStats();
|
|
@@ -110,7 +142,6 @@ class StreamHandler {
|
|
|
110
142
|
// }, 500);
|
|
111
143
|
}
|
|
112
144
|
row(row) {
|
|
113
|
-
// console.log('ACCEPT ROW', row);
|
|
114
145
|
if (this.currentWriter) this.currentWriter.row(row);
|
|
115
146
|
else if (row.message) process.send({ msgtype: 'info', info: { message: row.message } });
|
|
116
147
|
// this.onRow(this.jslid);
|
|
@@ -135,11 +166,22 @@ function handleStream(driver, resultIndexHolder, sql) {
|
|
|
135
166
|
});
|
|
136
167
|
}
|
|
137
168
|
|
|
169
|
+
function allowExecuteCustomScript(driver) {
|
|
170
|
+
if (driver.readOnlySessions) {
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
if (storedConnection.isReadOnly) {
|
|
174
|
+
return false;
|
|
175
|
+
// throw new Error('Connection is read only');
|
|
176
|
+
}
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
|
|
138
180
|
async function handleConnect(connection) {
|
|
139
181
|
storedConnection = connection;
|
|
140
182
|
|
|
141
183
|
const driver = requireEngineDriver(storedConnection);
|
|
142
|
-
systemConnection = await connectUtility(driver, storedConnection);
|
|
184
|
+
systemConnection = await connectUtility(driver, storedConnection, 'app');
|
|
143
185
|
for (const [resolve] of afterConnectCallbacks) {
|
|
144
186
|
resolve();
|
|
145
187
|
}
|
|
@@ -163,6 +205,19 @@ async function handleExecuteQuery({ sql }) {
|
|
|
163
205
|
await waitConnected();
|
|
164
206
|
const driver = requireEngineDriver(storedConnection);
|
|
165
207
|
|
|
208
|
+
if (!allowExecuteCustomScript(driver)) {
|
|
209
|
+
process.send({
|
|
210
|
+
msgtype: 'info',
|
|
211
|
+
info: {
|
|
212
|
+
message: 'Connection without read-only sessions is read only',
|
|
213
|
+
severity: 'error',
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
process.send({ msgtype: 'done', skipFinishedMessage: true });
|
|
217
|
+
return;
|
|
218
|
+
//process.send({ msgtype: 'error', error: e.message });
|
|
219
|
+
}
|
|
220
|
+
|
|
166
221
|
const resultIndexHolder = {
|
|
167
222
|
value: 0,
|
|
168
223
|
};
|
|
@@ -176,9 +231,39 @@ async function handleExecuteQuery({ sql }) {
|
|
|
176
231
|
process.send({ msgtype: 'done' });
|
|
177
232
|
}
|
|
178
233
|
|
|
234
|
+
async function handleExecuteReader({ jslid, sql, fileName }) {
|
|
235
|
+
await waitConnected();
|
|
236
|
+
|
|
237
|
+
const driver = requireEngineDriver(storedConnection);
|
|
238
|
+
|
|
239
|
+
if (fileName) {
|
|
240
|
+
sql = fs.readFileSync(fileName, 'utf-8');
|
|
241
|
+
} else {
|
|
242
|
+
if (!allowExecuteCustomScript(driver)) {
|
|
243
|
+
process.send({ msgtype: 'done' });
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const writer = new TableWriter();
|
|
249
|
+
writer.initializeFromReader(jslid);
|
|
250
|
+
|
|
251
|
+
const reader = await driver.readQuery(systemConnection, sql);
|
|
252
|
+
|
|
253
|
+
reader.on('data', data => {
|
|
254
|
+
writer.rowFromReader(data);
|
|
255
|
+
});
|
|
256
|
+
reader.on('end', () => {
|
|
257
|
+
writer.close(() => {
|
|
258
|
+
process.send({ msgtype: 'done' });
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
179
263
|
const messageHandlers = {
|
|
180
264
|
connect: handleConnect,
|
|
181
265
|
executeQuery: handleExecuteQuery,
|
|
266
|
+
executeReader: handleExecuteReader,
|
|
182
267
|
// cancel: handleCancel,
|
|
183
268
|
};
|
|
184
269
|
|
|
@@ -5,7 +5,7 @@ async function executeQuery({ connection = undefined, systemConnection = undefin
|
|
|
5
5
|
console.log(`Execute query ${sql}`);
|
|
6
6
|
|
|
7
7
|
if (!driver) driver = requireEngineDriver(connection);
|
|
8
|
-
const pool = systemConnection || (await connectUtility(driver, connection));
|
|
8
|
+
const pool = systemConnection || (await connectUtility(driver, connection, 'script'));
|
|
9
9
|
console.log(`Connected.`);
|
|
10
10
|
|
|
11
11
|
await driver.script(pool, sql);
|
|
@@ -21,7 +21,7 @@ async function generateDeploySql({
|
|
|
21
21
|
}) {
|
|
22
22
|
if (!driver) driver = requireEngineDriver(connection);
|
|
23
23
|
|
|
24
|
-
const pool = systemConnection || (await connectUtility(driver, connection));
|
|
24
|
+
const pool = systemConnection || (await connectUtility(driver, connection, 'read'));
|
|
25
25
|
if (!analysedStructure) {
|
|
26
26
|
analysedStructure = await driver.analyseFull(pool);
|
|
27
27
|
}
|
package/src/shell/queryReader.js
CHANGED
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
const requireEngineDriver = require('../utility/requireEngineDriver');
|
|
2
|
-
const { decryptConnection } = require('../utility/crypting');
|
|
3
2
|
const connectUtility = require('../utility/connectUtility');
|
|
4
3
|
|
|
5
|
-
async function queryReader({
|
|
6
|
-
|
|
4
|
+
async function queryReader({
|
|
5
|
+
connection,
|
|
6
|
+
query,
|
|
7
|
+
queryType,
|
|
8
|
+
// obsolete; use query instead
|
|
9
|
+
sql,
|
|
10
|
+
}) {
|
|
11
|
+
// if (sql && json) {
|
|
12
|
+
// throw new Error('Only one of sql or json could be set');
|
|
13
|
+
// }
|
|
14
|
+
// if (!sql && !json) {
|
|
15
|
+
// throw new Error('One of sql or json must be set');
|
|
16
|
+
// }
|
|
17
|
+
console.log(`Reading query ${query || sql}`);
|
|
18
|
+
// else console.log(`Reading query ${JSON.stringify(json)}`);
|
|
7
19
|
|
|
8
20
|
const driver = requireEngineDriver(connection);
|
|
9
|
-
const pool = await connectUtility(driver, connection);
|
|
21
|
+
const pool = await connectUtility(driver, connection, queryType == 'json' ? 'read' : 'script');
|
|
10
22
|
console.log(`Connected.`);
|
|
11
|
-
return await driver.readQuery(pool, sql);
|
|
23
|
+
return queryType == 'json' ? await driver.readJsonQuery(pool, query) : await driver.readQuery(pool, query || sql);
|
|
12
24
|
}
|
|
13
25
|
|
|
14
26
|
module.exports = queryReader;
|
package/src/shell/tableReader.js
CHANGED
|
@@ -4,12 +4,12 @@ const connectUtility = require('../utility/connectUtility');
|
|
|
4
4
|
|
|
5
5
|
async function tableReader({ connection, pureName, schemaName }) {
|
|
6
6
|
const driver = requireEngineDriver(connection);
|
|
7
|
-
const pool = await connectUtility(driver, connection);
|
|
7
|
+
const pool = await connectUtility(driver, connection, 'read');
|
|
8
8
|
console.log(`Connected.`);
|
|
9
9
|
|
|
10
10
|
const fullName = { pureName, schemaName };
|
|
11
11
|
|
|
12
|
-
if (driver.
|
|
12
|
+
if (driver.databaseEngineTypes.includes('document')) {
|
|
13
13
|
// @ts-ignore
|
|
14
14
|
console.log(`Reading collection ${fullNameToString(fullName)}`);
|
|
15
15
|
// @ts-ignore
|
package/src/shell/tableWriter.js
CHANGED
|
@@ -8,7 +8,7 @@ async function tableWriter({ connection, schemaName, pureName, driver, systemCon
|
|
|
8
8
|
if (!driver) {
|
|
9
9
|
driver = requireEngineDriver(connection);
|
|
10
10
|
}
|
|
11
|
-
const pool = systemConnection || (await connectUtility(driver, connection));
|
|
11
|
+
const pool = systemConnection || (await connectUtility(driver, connection, 'write'));
|
|
12
12
|
|
|
13
13
|
console.log(`Connected.`);
|
|
14
14
|
return await driver.writeTable(pool, { schemaName, pureName }, options);
|
|
@@ -4,11 +4,47 @@ const fs = require('fs-extra');
|
|
|
4
4
|
const { decryptConnection } = require('./crypting');
|
|
5
5
|
const { getSshTunnel } = require('./sshTunnel');
|
|
6
6
|
const { getSshTunnelProxy } = require('./sshTunnelProxy');
|
|
7
|
+
const platformInfo = require('../utility/platformInfo');
|
|
8
|
+
const connections = require('../controllers/connections');
|
|
9
|
+
|
|
10
|
+
async function loadConnection(driver, storedConnection, connectionMode) {
|
|
11
|
+
const { allowShellConnection } = platformInfo;
|
|
12
|
+
|
|
13
|
+
if (connectionMode == 'app') {
|
|
14
|
+
return storedConnection;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (storedConnection._id || !allowShellConnection) {
|
|
18
|
+
if (!storedConnection._id) {
|
|
19
|
+
throw new Error('Missing connection _id');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
await connections._init();
|
|
23
|
+
const loaded = await connections.getCore({ conid: storedConnection._id });
|
|
24
|
+
const loadedWithDb = {
|
|
25
|
+
...loaded,
|
|
26
|
+
database: storedConnection.database,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
if (loaded.isReadOnly) {
|
|
30
|
+
if (connectionMode == 'read') return loadedWithDb;
|
|
31
|
+
if (connectionMode == 'write') throw new Error('Cannot write readonly connection');
|
|
32
|
+
if (connectionMode == 'script') {
|
|
33
|
+
if (driver.readOnlySessions) return loadedWithDb;
|
|
34
|
+
throw new Error('Cannot write readonly connection');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return loadedWithDb;
|
|
38
|
+
}
|
|
39
|
+
return storedConnection;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function connectUtility(driver, storedConnection, connectionMode) {
|
|
43
|
+
const connectionLoaded = await loadConnection(driver, storedConnection, connectionMode);
|
|
7
44
|
|
|
8
|
-
async function connectUtility(driver, storedConnection) {
|
|
9
45
|
const connection = {
|
|
10
|
-
database:
|
|
11
|
-
...decryptConnection(
|
|
46
|
+
database: connectionLoaded.defaultDatabase,
|
|
47
|
+
...decryptConnection(connectionLoaded),
|
|
12
48
|
};
|
|
13
49
|
|
|
14
50
|
if (!connection.port && driver.defaultPort) connection.port = driver.defaultPort.toString();
|
package/src/utility/crypting.js
CHANGED
|
@@ -55,7 +55,7 @@ function encryptPasswordField(connection, field) {
|
|
|
55
55
|
[field]: 'crypt:' + getEncryptor().encrypt(connection[field]),
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
|
-
return connection;
|
|
58
|
+
return connection;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
function decryptPasswordField(connection, field) {
|
|
@@ -75,6 +75,11 @@ function encryptConnection(connection) {
|
|
|
75
75
|
return connection;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
function maskConnection(connection) {
|
|
79
|
+
if (!connection) return connection;
|
|
80
|
+
return _.omit(connection, ['password', 'sshPassword', 'sshKeyfilePassword']);
|
|
81
|
+
}
|
|
82
|
+
|
|
78
83
|
function decryptConnection(connection) {
|
|
79
84
|
connection = decryptPasswordField(connection, 'password');
|
|
80
85
|
connection = decryptPasswordField(connection, 'sshPassword');
|
|
@@ -95,5 +100,6 @@ module.exports = {
|
|
|
95
100
|
loadEncryptionKey,
|
|
96
101
|
encryptConnection,
|
|
97
102
|
decryptConnection,
|
|
103
|
+
maskConnection,
|
|
98
104
|
pickSafeConnectionInfo,
|
|
99
105
|
};
|
|
@@ -54,7 +54,7 @@ function packagedPluginsDir() {
|
|
|
54
54
|
}
|
|
55
55
|
if (platformInfo.isNpmDist) {
|
|
56
56
|
// node_modules
|
|
57
|
-
return global['
|
|
57
|
+
return global['PLUGINS_DIR'];
|
|
58
58
|
}
|
|
59
59
|
if (platformInfo.isElectronBundle) {
|
|
60
60
|
return path.resolve(__dirname, '../../plugins');
|
|
@@ -1,12 +1,56 @@
|
|
|
1
1
|
const { compilePermissions, testPermission } = require('dbgate-tools');
|
|
2
|
+
const _ = require('lodash');
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
const userPermissions = {};
|
|
4
5
|
|
|
5
|
-
function hasPermission(tested) {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
function hasPermission(tested, req) {
|
|
7
|
+
const { user } = (req && req.auth) || {};
|
|
8
|
+
const key = user || '';
|
|
9
|
+
const logins = getLogins();
|
|
10
|
+
if (!userPermissions[key] && logins) {
|
|
11
|
+
const login = logins.find(x => x.login == user);
|
|
12
|
+
userPermissions[key] = compilePermissions(login ? login.permissions : null);
|
|
8
13
|
}
|
|
9
|
-
return testPermission(tested,
|
|
14
|
+
return testPermission(tested, userPermissions[key]);
|
|
10
15
|
}
|
|
11
16
|
|
|
12
|
-
|
|
17
|
+
let loginsCache = null;
|
|
18
|
+
let loginsLoaded = false;
|
|
19
|
+
|
|
20
|
+
function getLogins() {
|
|
21
|
+
if (loginsLoaded) {
|
|
22
|
+
return loginsCache;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const res = [];
|
|
26
|
+
if (process.env.LOGIN && process.env.PASSWORD) {
|
|
27
|
+
res.push({
|
|
28
|
+
login: process.env.LOGIN,
|
|
29
|
+
password: process.env.PASSWORD,
|
|
30
|
+
permissions: process.env.PERMISSIONS,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
if (process.env.LOGINS) {
|
|
34
|
+
const logins = _.compact(process.env.LOGINS.split(',').map(x => x.trim()));
|
|
35
|
+
for (const login of logins) {
|
|
36
|
+
const password = process.env[`LOGIN_PASSWORD_${login}`];
|
|
37
|
+
const permissions = process.env[`LOGIN_PERMISSIONS_${login}`];
|
|
38
|
+
if (password) {
|
|
39
|
+
res.push({
|
|
40
|
+
login,
|
|
41
|
+
password,
|
|
42
|
+
permissions,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
loginsCache = res.length > 0 ? res : null;
|
|
49
|
+
loginsLoaded = true;
|
|
50
|
+
return loginsCache;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = {
|
|
54
|
+
hasPermission,
|
|
55
|
+
getLogins,
|
|
56
|
+
};
|
|
@@ -10,7 +10,7 @@ const isMac = platform === 'darwin';
|
|
|
10
10
|
const isLinux = platform === 'linux';
|
|
11
11
|
const isDocker = fs.existsSync('/home/dbgate-docker/public');
|
|
12
12
|
const isDevMode = process.env.DEVMODE == '1';
|
|
13
|
-
const isNpmDist = !!global['
|
|
13
|
+
const isNpmDist = !!global['IS_NPM_DIST'];
|
|
14
14
|
const isForkedApi = processArgs.isForkedApi;
|
|
15
15
|
|
|
16
16
|
// function moduleAvailable(name) {
|
|
@@ -39,6 +39,8 @@ const platformInfo = {
|
|
|
39
39
|
environment: process.env.NODE_ENV,
|
|
40
40
|
platform,
|
|
41
41
|
runningInWebpack: !!process.env.WEBPACK_DEV_SERVER_URL,
|
|
42
|
+
allowShellConnection: !!process.env.SHELL_CONNECTION || !!isElectron(),
|
|
43
|
+
allowShellScripting: !!process.env.SHELL_SCRIPTING || !!isElectron(),
|
|
42
44
|
defaultKeyfile: path.join(os.homedir(), '.ssh/id_rsa'),
|
|
43
45
|
};
|
|
44
46
|
|
|
@@ -62,7 +62,7 @@ module.exports = function useController(app, electron, route, controller) {
|
|
|
62
62
|
// controller._init_called = true;
|
|
63
63
|
// }
|
|
64
64
|
try {
|
|
65
|
-
let params = [{ ...req.body, ...req.query }];
|
|
65
|
+
let params = [{ ...req.body, ...req.query }, req];
|
|
66
66
|
if (rawParams) params = [req, res];
|
|
67
67
|
const data = await controller[key](...params);
|
|
68
68
|
res.json(data);
|