dbgate-api 6.3.2 → 6.3.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": "6.3.2",
4
+ "version": "6.3.3",
5
5
  "homepage": "https://dbgate.org/",
6
6
  "repository": {
7
7
  "type": "git",
@@ -29,10 +29,10 @@
29
29
  "compare-versions": "^3.6.0",
30
30
  "cors": "^2.8.5",
31
31
  "cross-env": "^6.0.3",
32
- "dbgate-datalib": "^6.3.2",
32
+ "dbgate-datalib": "^6.3.3",
33
33
  "dbgate-query-splitter": "^4.11.3",
34
- "dbgate-sqltree": "^6.3.2",
35
- "dbgate-tools": "^6.3.2",
34
+ "dbgate-sqltree": "^6.3.3",
35
+ "dbgate-tools": "^6.3.3",
36
36
  "debug": "^4.3.4",
37
37
  "diff": "^5.0.0",
38
38
  "diff2html": "^3.4.13",
@@ -83,7 +83,7 @@
83
83
  "devDependencies": {
84
84
  "@types/fs-extra": "^9.0.11",
85
85
  "@types/lodash": "^4.14.149",
86
- "dbgate-types": "^6.3.2",
86
+ "dbgate-types": "^6.3.3",
87
87
  "env-cmd": "^10.1.0",
88
88
  "jsdoc-to-markdown": "^9.0.5",
89
89
  "node-loader": "^1.0.2",
@@ -12,6 +12,7 @@ const {
12
12
  getAuthProviderById,
13
13
  } = require('../auth/authProvider');
14
14
  const storage = require('./storage');
15
+ const { decryptPasswordString } = require('../utility/crypting');
15
16
 
16
17
  const logger = getLogger('auth');
17
18
 
@@ -44,6 +45,7 @@ function authMiddleware(req, res, next) {
44
45
  '/connections/dblogin-auth',
45
46
  '/connections/dblogin-auth-token',
46
47
  '/health',
48
+ '/__health',
47
49
  ];
48
50
 
49
51
  // console.log('********************* getAuthProvider()', getAuthProvider());
@@ -95,7 +97,7 @@ module.exports = {
95
97
  let adminPassword = process.env.ADMIN_PASSWORD;
96
98
  if (!adminPassword) {
97
99
  const adminConfig = await storage.readConfig({ group: 'admin' });
98
- adminPassword = adminConfig?.adminPassword;
100
+ adminPassword = decryptPasswordString(adminConfig?.adminPassword);
99
101
  }
100
102
  if (adminPassword && adminPassword == password) {
101
103
  return {
@@ -37,6 +37,8 @@ const loadModelTransform = require('../utility/loadModelTransform');
37
37
  const exportDbModelSql = require('../utility/exportDbModelSql');
38
38
  const axios = require('axios');
39
39
  const { callTextToSqlApi, callCompleteOnCursorApi, callRefactorSqlQueryApi } = require('../utility/authProxy');
40
+ const { decryptConnection } = require('../utility/crypting');
41
+ const { getSshTunnel } = require('../utility/sshTunnel');
40
42
 
41
43
  const logger = getLogger('databaseConnections');
42
44
 
@@ -140,6 +142,11 @@ module.exports = {
140
142
  if (newOpened.disconnected) return;
141
143
  this.close(conid, database, false);
142
144
  });
145
+ subprocess.on('error', err => {
146
+ logger.error(extractErrorLogData(err), 'Error in database connection subprocess');
147
+ if (newOpened.disconnected) return;
148
+ this.close(conid, database, false);
149
+ });
143
150
 
144
151
  subprocess.send({
145
152
  msgtype: 'connect',
@@ -619,9 +626,26 @@ module.exports = {
619
626
  command,
620
627
  { conid, database, outputFile, inputFile, options, selectedTables, skippedTables, argsFormat }
621
628
  ) {
622
- const connection = await connections.getCore({ conid });
629
+ const sourceConnection = await connections.getCore({ conid });
630
+ const connection = {
631
+ ...decryptConnection(sourceConnection),
632
+ };
623
633
  const driver = requireEngineDriver(connection);
624
634
 
635
+ if (!connection.port && driver.defaultPort) {
636
+ connection.port = driver.defaultPort.toString();
637
+ }
638
+
639
+ if (connection.useSshTunnel) {
640
+ const tunnel = await getSshTunnel(connection);
641
+ if (tunnel.state == 'error') {
642
+ throw new Error(tunnel.message);
643
+ }
644
+
645
+ connection.server = tunnel.localHost;
646
+ connection.port = tunnel.localPort;
647
+ }
648
+
625
649
  const settingsValue = await config.getSettings();
626
650
 
627
651
  const externalTools = {};
@@ -98,6 +98,11 @@ module.exports = {
98
98
  if (newOpened.disconnected) return;
99
99
  this.close(conid, false);
100
100
  });
101
+ subprocess.on('error', err => {
102
+ logger.error(extractErrorLogData(err), 'Error in server connection subprocess');
103
+ if (newOpened.disconnected) return;
104
+ this.close(conid, false);
105
+ });
101
106
  subprocess.send({ msgtype: 'connect', ...connection, globalSettings: await config.getSettings() });
102
107
  return newOpened;
103
108
  });
@@ -1,5 +1,5 @@
1
1
 
2
2
  module.exports = {
3
- version: '6.3.2',
4
- buildTime: '2025-04-02T12:09:59.169Z'
3
+ version: '6.3.3',
4
+ buildTime: '2025-04-09T10:29:36.096Z'
5
5
  };
package/src/main.js CHANGED
@@ -38,7 +38,7 @@ const { getLogger } = require('dbgate-tools');
38
38
  const { getDefaultAuthProvider } = require('./auth/authProvider');
39
39
  const startCloudUpgradeTimer = require('./utility/cloudUpgrade');
40
40
  const { isProApp } = require('./utility/checkLicense');
41
- const getHealthStatus = require('./utility/healthStatus');
41
+ const { getHealthStatus, getHealthStatusSprinx } = require('./utility/healthStatus');
42
42
 
43
43
  const logger = getLogger('main');
44
44
 
@@ -124,6 +124,12 @@ function start() {
124
124
  res.end(JSON.stringify(health, null, 2));
125
125
  });
126
126
 
127
+ app.get(getExpressPath('/__health'), async function (req, res) {
128
+ res.setHeader('Content-Type', 'application/json');
129
+ const health = await getHealthStatusSprinx();
130
+ res.end(JSON.stringify(health, null, 2));
131
+ });
132
+
127
133
  app.use(bodyParser.json({ limit: '50mb' }));
128
134
 
129
135
  app.use(
@@ -60,6 +60,10 @@ class DatastoreProxy {
60
60
  // if (this.disconnected) return;
61
61
  this.subprocess = null;
62
62
  });
63
+ this.subprocess.on('error', err => {
64
+ logger.error(extractErrorLogData(err), 'Error in data store subprocess');
65
+ this.subprocess = null;
66
+ });
63
67
  this.subprocess.send({ msgtype: 'open', file: this.file });
64
68
  }
65
69
  return this.subprocess;
@@ -96,7 +96,9 @@ async function connectUtility(driver, storedConnection, connectionMode, addition
96
96
  ...decryptConnection(connectionLoaded),
97
97
  };
98
98
 
99
- if (!connection.port && driver.defaultPort) connection.port = driver.defaultPort.toString();
99
+ if (!connection.port && driver.defaultPort) {
100
+ connection.port = driver.defaultPort.toString();
101
+ }
100
102
 
101
103
  if (connection.useSshTunnel) {
102
104
  const tunnel = await getSshTunnelProxy(connection);
@@ -5,12 +5,16 @@ const path = require('path');
5
5
  const _ = require('lodash');
6
6
 
7
7
  const { datadir } = require('./directories');
8
+ const { encryptionKeyArg } = require('./processArgs');
8
9
 
9
10
  const defaultEncryptionKey = 'mQAUaXhavRGJDxDTXSCg7Ej0xMmGCrx6OKA07DIMBiDcYYkvkaXjTAzPUEHEHEf9';
10
11
 
11
12
  let _encryptionKey = null;
12
13
 
13
14
  function loadEncryptionKey() {
15
+ if (encryptionKeyArg) {
16
+ return encryptionKeyArg;
17
+ }
14
18
  if (_encryptionKey) {
15
19
  return _encryptionKey;
16
20
  }
@@ -33,6 +37,26 @@ function loadEncryptionKey() {
33
37
  return _encryptionKey;
34
38
  }
35
39
 
40
+ async function loadEncryptionKeyFromExternal(storedValue, setStoredValue) {
41
+ const encryptor = simpleEncryptor.createEncryptor(defaultEncryptionKey);
42
+
43
+ if (!storedValue) {
44
+ const generatedKey = crypto.randomBytes(32);
45
+ const newKey = generatedKey.toString('hex');
46
+ const result = {
47
+ encryptionKey: newKey,
48
+ };
49
+ await setStoredValue(encryptor.encrypt(result));
50
+
51
+ setEncryptionKey(newKey);
52
+
53
+ return;
54
+ }
55
+
56
+ const data = encryptor.decrypt(storedValue);
57
+ setEncryptionKey(data['encryptionKey']);
58
+ }
59
+
36
60
  let _encryptor = null;
37
61
 
38
62
  function getEncryptor() {
@@ -43,35 +67,46 @@ function getEncryptor() {
43
67
  return _encryptor;
44
68
  }
45
69
 
46
- function encryptPasswordField(connection, field) {
47
- if (
48
- connection &&
49
- connection[field] &&
50
- !connection[field].startsWith('crypt:') &&
51
- connection.passwordMode != 'saveRaw'
52
- ) {
70
+ function encryptPasswordString(password) {
71
+ if (password && !password.startsWith('crypt:')) {
72
+ return 'crypt:' + getEncryptor().encrypt(password);
73
+ }
74
+ return password;
75
+ }
76
+
77
+ function decryptPasswordString(password) {
78
+ if (password && password.startsWith('crypt:')) {
79
+ return getEncryptor().decrypt(password.substring('crypt:'.length));
80
+ }
81
+ return password;
82
+ }
83
+
84
+ function encryptObjectPasswordField(obj, field) {
85
+ if (obj && obj[field] && !obj[field].startsWith('crypt:')) {
53
86
  return {
54
- ...connection,
55
- [field]: 'crypt:' + getEncryptor().encrypt(connection[field]),
87
+ ...obj,
88
+ [field]: 'crypt:' + getEncryptor().encrypt(obj[field]),
56
89
  };
57
90
  }
58
- return connection;
91
+ return obj;
59
92
  }
60
93
 
61
- function decryptPasswordField(connection, field) {
62
- if (connection && connection[field] && connection[field].startsWith('crypt:')) {
94
+ function decryptObjectPasswordField(obj, field) {
95
+ if (obj && obj[field] && obj[field].startsWith('crypt:')) {
63
96
  return {
64
- ...connection,
65
- [field]: getEncryptor().decrypt(connection[field].substring('crypt:'.length)),
97
+ ...obj,
98
+ [field]: getEncryptor().decrypt(obj[field].substring('crypt:'.length)),
66
99
  };
67
100
  }
68
- return connection;
101
+ return obj;
69
102
  }
70
103
 
71
104
  function encryptConnection(connection) {
72
- connection = encryptPasswordField(connection, 'password');
73
- connection = encryptPasswordField(connection, 'sshPassword');
74
- connection = encryptPasswordField(connection, 'sshKeyfilePassword');
105
+ if (connection.passwordMode != 'saveRaw') {
106
+ connection = encryptObjectPasswordField(connection, 'password');
107
+ connection = encryptObjectPasswordField(connection, 'sshPassword');
108
+ connection = encryptObjectPasswordField(connection, 'sshKeyfilePassword');
109
+ }
75
110
  return connection;
76
111
  }
77
112
 
@@ -81,12 +116,24 @@ function maskConnection(connection) {
81
116
  }
82
117
 
83
118
  function decryptConnection(connection) {
84
- connection = decryptPasswordField(connection, 'password');
85
- connection = decryptPasswordField(connection, 'sshPassword');
86
- connection = decryptPasswordField(connection, 'sshKeyfilePassword');
119
+ connection = decryptObjectPasswordField(connection, 'password');
120
+ connection = decryptObjectPasswordField(connection, 'sshPassword');
121
+ connection = decryptObjectPasswordField(connection, 'sshKeyfilePassword');
87
122
  return connection;
88
123
  }
89
124
 
125
+ function encryptUser(user) {
126
+ if (user.encryptPassword) {
127
+ user = encryptObjectPasswordField(user, 'password');
128
+ }
129
+ return user;
130
+ }
131
+
132
+ function decryptUser(user) {
133
+ user = decryptObjectPasswordField(user, 'password');
134
+ return user;
135
+ }
136
+
90
137
  function pickSafeConnectionInfo(connection) {
91
138
  if (process.env.LOG_CONNECTION_SENSITIVE_VALUES) {
92
139
  return connection;
@@ -99,10 +146,27 @@ function pickSafeConnectionInfo(connection) {
99
146
  });
100
147
  }
101
148
 
149
+ function setEncryptionKey(encryptionKey) {
150
+ _encryptionKey = encryptionKey;
151
+ _encryptor = null;
152
+ global.ENCRYPTION_KEY = encryptionKey;
153
+ }
154
+
155
+ function getEncryptionKey() {
156
+ return _encryptionKey;
157
+ }
158
+
102
159
  module.exports = {
103
160
  loadEncryptionKey,
104
161
  encryptConnection,
162
+ encryptUser,
163
+ decryptUser,
105
164
  decryptConnection,
106
165
  maskConnection,
107
166
  pickSafeConnectionInfo,
167
+ loadEncryptionKeyFromExternal,
168
+ getEncryptionKey,
169
+ setEncryptionKey,
170
+ encryptPasswordString,
171
+ decryptPasswordString,
108
172
  };
@@ -22,6 +22,8 @@ const getMapExport = (geoJson) => {
22
22
  })
23
23
  .addTo(map);
24
24
 
25
+ leaflet.control.scale().addTo(map);
26
+
25
27
  const geoJsonObj = leaflet
26
28
  .geoJSON(${JSON.stringify(geoJson)}, {
27
29
  style: function () {
@@ -24,4 +24,15 @@ async function getHealthStatus() {
24
24
  };
25
25
  }
26
26
 
27
- module.exports = getHealthStatus;
27
+ async function getHealthStatusSprinx() {
28
+ return {
29
+ overallStatus: 'OK',
30
+ timeStamp: new Date().toISOString(),
31
+ timeStampUnix: Math.floor(Date.now() / 1000),
32
+ };
33
+ }
34
+
35
+ module.exports = {
36
+ getHealthStatus,
37
+ getHealthStatusSprinx,
38
+ };
@@ -17,6 +17,7 @@ const processDisplayName = getNamedArg('--process-display-name');
17
17
  const listenApi = process.argv.includes('--listen-api');
18
18
  const listenApiChild = process.argv.includes('--listen-api-child') || listenApi;
19
19
  const runE2eTests = process.argv.includes('--run-e2e-tests');
20
+ const encryptionKeyArg = getNamedArg('--encryption-key');
20
21
 
21
22
  function getPassArgs() {
22
23
  const res = [];
@@ -31,6 +32,9 @@ function getPassArgs() {
31
32
  if (runE2eTests) {
32
33
  res.push('--run-e2e-tests');
33
34
  }
35
+ if (global['ENCRYPTION_KEY']) {
36
+ res.push('--encryption-key', global['ENCRYPTION_KEY']);
37
+ }
34
38
  return res;
35
39
  }
36
40
 
@@ -45,4 +49,5 @@ module.exports = {
45
49
  listenApiChild,
46
50
  processDisplayName,
47
51
  runE2eTests,
52
+ encryptionKeyArg,
48
53
  };
@@ -57,10 +57,21 @@ function callForwardProcess(connection, tunnelConfig, tunnelCacheKey) {
57
57
  }
58
58
  });
59
59
  subprocess.on('exit', code => {
60
- logger.info('SSH forward process exited');
60
+ logger.info(`SSH forward process exited with code ${code}`);
61
61
  delete sshTunnelCache[tunnelCacheKey];
62
62
  if (!promiseHandled) {
63
- reject(new Error('SSH forward process exited, try to change "Local host address for SSH connections" in Settings/Connections'));
63
+ reject(
64
+ new Error(
65
+ 'SSH forward process exited, try to change "Local host address for SSH connections" in Settings/Connections'
66
+ )
67
+ );
68
+ }
69
+ });
70
+ subprocess.on('error', error => {
71
+ logger.error(extractErrorLogData(error), 'SSH forward process error');
72
+ delete sshTunnelCache[tunnelCacheKey];
73
+ if (!promiseHandled) {
74
+ reject(error);
64
75
  }
65
76
  });
66
77
  });