dbgate-api 6.5.5 → 6.6.0

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.5.5",
4
+ "version": "6.6.0",
5
5
  "homepage": "https://dbgate.org/",
6
6
  "repository": {
7
7
  "type": "git",
@@ -30,10 +30,10 @@
30
30
  "compare-versions": "^3.6.0",
31
31
  "cors": "^2.8.5",
32
32
  "cross-env": "^6.0.3",
33
- "dbgate-datalib": "^6.5.5",
33
+ "dbgate-datalib": "^6.6.0",
34
34
  "dbgate-query-splitter": "^4.11.5",
35
- "dbgate-sqltree": "^6.5.5",
36
- "dbgate-tools": "^6.5.5",
35
+ "dbgate-sqltree": "^6.6.0",
36
+ "dbgate-tools": "^6.6.0",
37
37
  "debug": "^4.3.4",
38
38
  "diff": "^5.0.0",
39
39
  "diff2html": "^3.4.13",
@@ -68,6 +68,7 @@
68
68
  },
69
69
  "scripts": {
70
70
  "start": "env-cmd -f .env node src/index.js --listen-api",
71
+ "start:debug": "env-cmd -f .env node --inspect src/index.js --listen-api",
71
72
  "start:portal": "env-cmd -f env/portal/.env node src/index.js --listen-api",
72
73
  "start:singledb": "env-cmd -f env/singledb/.env node src/index.js --listen-api",
73
74
  "start:auth": "env-cmd -f env/auth/.env node src/index.js --listen-api",
@@ -85,7 +86,7 @@
85
86
  "devDependencies": {
86
87
  "@types/fs-extra": "^9.0.11",
87
88
  "@types/lodash": "^4.14.149",
88
- "dbgate-types": "^6.5.5",
89
+ "dbgate-types": "^6.6.0",
89
90
  "env-cmd": "^10.1.0",
90
91
  "jsdoc-to-markdown": "^9.0.5",
91
92
  "node-loader": "^1.0.2",
@@ -21,7 +21,13 @@ const {
21
21
  } = require('../utility/cloudIntf');
22
22
  const socket = require('../utility/socket');
23
23
  const { sendToAuditLog } = require('../utility/auditlog');
24
- const { isLoginLicensed, LOGIN_LIMIT_ERROR } = require('../utility/loginchecker');
24
+ const {
25
+ isLoginLicensed,
26
+ LOGIN_LIMIT_ERROR,
27
+ markTokenAsLoggedIn,
28
+ markUserAsActive,
29
+ markLoginAsLoggedOut,
30
+ } = require('../utility/loginchecker');
25
31
 
26
32
  const logger = getLogger('auth');
27
33
 
@@ -61,6 +67,11 @@ function authMiddleware(req, res, next) {
61
67
 
62
68
  // const isAdminPage = req.headers['x-is-admin-page'] == 'true';
63
69
 
70
+ if (process.env.SKIP_ALL_AUTH) {
71
+ // API is not authorized for basic auth
72
+ return next();
73
+ }
74
+
64
75
  if (process.env.BASIC_AUTH) {
65
76
  // API is not authorized for basic auth
66
77
  return next();
@@ -79,7 +90,7 @@ function authMiddleware(req, res, next) {
79
90
  try {
80
91
  const decoded = jwt.verify(token, getTokenSecret());
81
92
  req.user = decoded;
82
- storage.markUserAsActive(decoded.licenseUid);
93
+ markUserAsActive(decoded.licenseUid, token);
83
94
 
84
95
  return next();
85
96
  } catch (err) {
@@ -124,19 +135,23 @@ module.exports = {
124
135
  message: 'Administration login successful',
125
136
  });
126
137
 
138
+ const licenseUid = `superadmin`;
139
+ const accessToken = jwt.sign(
140
+ {
141
+ login: 'superadmin',
142
+ permissions: await storage.loadSuperadminPermissions(),
143
+ roleId: -3,
144
+ licenseUid,
145
+ },
146
+ getTokenSecret(),
147
+ {
148
+ expiresIn: getTokenLifetime(),
149
+ }
150
+ );
151
+ markTokenAsLoggedIn(licenseUid, accessToken);
152
+
127
153
  return {
128
- accessToken: jwt.sign(
129
- {
130
- login: 'superadmin',
131
- permissions: await storage.loadSuperadminPermissions(),
132
- roleId: -3,
133
- licenseUid: `superadmin`,
134
- },
135
- getTokenSecret(),
136
- {
137
- expiresIn: getTokenLifetime(),
138
- }
139
- ),
154
+ accessToken,
140
155
  };
141
156
  }
142
157
 
@@ -192,5 +207,17 @@ module.exports = {
192
207
  return tokenHolder;
193
208
  },
194
209
 
210
+ logoutAdmin_meta: true,
211
+ async logoutAdmin() {
212
+ await markLoginAsLoggedOut('superadmin');
213
+ return true;
214
+ },
215
+
216
+ logoutUser_meta: true,
217
+ async logoutUser({}, req) {
218
+ await markLoginAsLoggedOut(req?.user?.licenseUid);
219
+ return true;
220
+ },
221
+
195
222
  authMiddleware,
196
223
  };
@@ -16,6 +16,7 @@ const { getConnectionLabel, getLogger, extractErrorLogData } = require('dbgate-t
16
16
  const logger = getLogger('cloud');
17
17
  const _ = require('lodash');
18
18
  const fs = require('fs-extra');
19
+ const { getAiGatewayServer } = require('../utility/authProxy');
19
20
 
20
21
  module.exports = {
21
22
  publicFiles_meta: true,
@@ -276,4 +277,17 @@ module.exports = {
276
277
  const resp = await callCloudApiPost(`content-folders/remove-user/${folid}`, { email });
277
278
  return resp;
278
279
  },
280
+
281
+ getAiGateway_meta: true,
282
+ async getAiGateway() {
283
+ return getAiGatewayServer();
284
+ },
285
+
286
+ // chatStream_meta: {
287
+ // raw: true,
288
+ // method: 'post',
289
+ // },
290
+ // chatStream(req, res) {
291
+ // callChatStream(req.body, res);
292
+ // },
279
293
  };
@@ -16,7 +16,7 @@ const connections = require('../controllers/connections');
16
16
  const { getAuthProviderFromReq } = require('../auth/authProvider');
17
17
  const { checkLicense, checkLicenseKey } = require('../utility/checkLicense');
18
18
  const storage = require('./storage');
19
- const { getAuthProxyUrl } = require('../utility/authProxy');
19
+ const { getAuthProxyUrl, tryToGetRefreshedLicense } = require('../utility/authProxy');
20
20
  const { getPublicHardwareFingerprint } = require('../utility/hardwareFingerprint');
21
21
  const { extractErrorMessage } = require('dbgate-tools');
22
22
  const {
@@ -109,6 +109,7 @@ module.exports = {
109
109
  ),
110
110
  isAdminPasswordMissing,
111
111
  isInvalidToken: req?.isInvalidToken,
112
+ skipAllAuth: !!process.env.SKIP_ALL_AUTH,
112
113
  adminPasswordState: adminConfig?.adminPasswordState,
113
114
  storageDatabase: process.env.STORAGE_DATABASE,
114
115
  logsFilePath: getLogsFilePath(),
@@ -191,6 +192,7 @@ module.exports = {
191
192
  return {
192
193
  ...this.fillMissingSettings(JSON.parse(settingsText)),
193
194
  'other.licenseKey': platformInfo.isElectron ? await this.loadLicenseKey() : undefined,
195
+ // 'other.licenseKey': await this.loadLicenseKey(),
194
196
  };
195
197
  }
196
198
  } catch (err) {
@@ -208,21 +210,34 @@ module.exports = {
208
210
  },
209
211
 
210
212
  saveLicenseKey_meta: true,
211
- async saveLicenseKey({ licenseKey }) {
212
- const decoded = jwt.decode(licenseKey?.trim());
213
- if (!decoded) {
214
- return {
215
- status: 'error',
216
- errorMessage: 'Invalid license key',
217
- };
218
- }
213
+ async saveLicenseKey({ licenseKey, forceSave = false, tryToRenew = false }) {
214
+ if (!forceSave) {
215
+ const decoded = jwt.decode(licenseKey?.trim());
216
+ if (!decoded) {
217
+ return {
218
+ status: 'error',
219
+ errorMessage: 'Invalid license key',
220
+ };
221
+ }
219
222
 
220
- const { exp } = decoded;
221
- if (exp * 1000 < Date.now()) {
222
- return {
223
- status: 'error',
224
- errorMessage: 'License key is expired',
225
- };
223
+ const { exp } = decoded;
224
+ if (exp * 1000 < Date.now()) {
225
+ let renewed = false;
226
+ if (tryToRenew) {
227
+ const newLicenseKey = await tryToGetRefreshedLicense(licenseKey);
228
+ if (newLicenseKey.status == 'ok') {
229
+ licenseKey = newLicenseKey.token;
230
+ renewed = true;
231
+ }
232
+ }
233
+
234
+ if (!renewed) {
235
+ return {
236
+ status: 'error',
237
+ errorMessage: 'License key is expired',
238
+ };
239
+ }
240
+ }
226
241
  }
227
242
 
228
243
  try {
@@ -297,7 +312,7 @@ module.exports = {
297
312
  // this.settingsValue = updated;
298
313
 
299
314
  if (currentValue['other.licenseKey'] != values['other.licenseKey']) {
300
- await this.saveLicenseKey({ licenseKey: values['other.licenseKey'] });
315
+ await this.saveLicenseKey({ licenseKey: values['other.licenseKey'], forceSave: true });
301
316
  socket.emitChanged(`config-changed`);
302
317
  }
303
318
  }
@@ -327,6 +342,16 @@ module.exports = {
327
342
  return resp;
328
343
  },
329
344
 
345
+ getNewLicense_meta: true,
346
+ async getNewLicense({ oldLicenseKey }) {
347
+ const newLicenseKey = await tryToGetRefreshedLicense(oldLicenseKey);
348
+ const res = await checkLicenseKey(newLicenseKey.token);
349
+ if (res.status == 'ok') {
350
+ res.licenseKey = newLicenseKey.token;
351
+ }
352
+ return res;
353
+ },
354
+
330
355
  recryptDatabaseForExport(db) {
331
356
  const encryptionKey = generateTransportEncryptionKey();
332
357
  const transportEncryptor = createTransportEncryptor(encryptionKey);
@@ -41,6 +41,4 @@ module.exports = {
41
41
  async getUsedEngines() {
42
42
  return null;
43
43
  },
44
-
45
- markUserAsActive(licenseUid) {},
46
44
  };
@@ -1,5 +1,5 @@
1
1
 
2
2
  module.exports = {
3
- version: '6.5.5',
4
- buildTime: '2025-07-04T07:10:46.574Z'
3
+ version: '6.6.0',
4
+ buildTime: '2025-07-25T07:23:15.741Z'
5
5
  };
@@ -519,6 +519,12 @@ module.exports = {
519
519
  "dataType": "int",
520
520
  "notNull": false
521
521
  },
522
+ {
523
+ "pureName": "connections",
524
+ "columnName": "useSeparateSchemas",
525
+ "dataType": "int",
526
+ "notNull": false
527
+ },
522
528
  {
523
529
  "pureName": "connections",
524
530
  "columnName": "defaultDatabase",
@@ -668,6 +674,12 @@ module.exports = {
668
674
  "columnName": "awsRegion",
669
675
  "dataType": "varchar(250)",
670
676
  "notNull": false
677
+ },
678
+ {
679
+ "pureName": "connections",
680
+ "columnName": "connectionDefinition",
681
+ "dataType": "text",
682
+ "notNull": false
671
683
  }
672
684
  ],
673
685
  "foreignKeys": [],
@@ -682,6 +694,49 @@ module.exports = {
682
694
  ]
683
695
  }
684
696
  },
697
+ {
698
+ "pureName": "roles",
699
+ "columns": [
700
+ {
701
+ "pureName": "roles",
702
+ "columnName": "id",
703
+ "dataType": "int",
704
+ "autoIncrement": true,
705
+ "notNull": true
706
+ },
707
+ {
708
+ "pureName": "roles",
709
+ "columnName": "name",
710
+ "dataType": "varchar(250)",
711
+ "notNull": false
712
+ }
713
+ ],
714
+ "foreignKeys": [],
715
+ "primaryKey": {
716
+ "pureName": "roles",
717
+ "constraintType": "primaryKey",
718
+ "constraintName": "PK_roles",
719
+ "columns": [
720
+ {
721
+ "columnName": "id"
722
+ }
723
+ ]
724
+ },
725
+ "preloadedRows": [
726
+ {
727
+ "id": -1,
728
+ "name": "anonymous-user"
729
+ },
730
+ {
731
+ "id": -2,
732
+ "name": "logged-user"
733
+ },
734
+ {
735
+ "id": -3,
736
+ "name": "superadmin"
737
+ }
738
+ ]
739
+ },
685
740
  {
686
741
  "pureName": "role_connections",
687
742
  "columns": [
@@ -794,47 +849,45 @@ module.exports = {
794
849
  }
795
850
  },
796
851
  {
797
- "pureName": "roles",
852
+ "pureName": "users",
798
853
  "columns": [
799
854
  {
800
- "pureName": "roles",
855
+ "pureName": "users",
801
856
  "columnName": "id",
802
857
  "dataType": "int",
803
858
  "autoIncrement": true,
804
859
  "notNull": true
805
860
  },
806
861
  {
807
- "pureName": "roles",
808
- "columnName": "name",
862
+ "pureName": "users",
863
+ "columnName": "login",
864
+ "dataType": "varchar(250)",
865
+ "notNull": false
866
+ },
867
+ {
868
+ "pureName": "users",
869
+ "columnName": "password",
870
+ "dataType": "varchar(250)",
871
+ "notNull": false
872
+ },
873
+ {
874
+ "pureName": "users",
875
+ "columnName": "email",
809
876
  "dataType": "varchar(250)",
810
877
  "notNull": false
811
878
  }
812
879
  ],
813
880
  "foreignKeys": [],
814
881
  "primaryKey": {
815
- "pureName": "roles",
882
+ "pureName": "users",
816
883
  "constraintType": "primaryKey",
817
- "constraintName": "PK_roles",
884
+ "constraintName": "PK_users",
818
885
  "columns": [
819
886
  {
820
887
  "columnName": "id"
821
888
  }
822
889
  ]
823
- },
824
- "preloadedRows": [
825
- {
826
- "id": -1,
827
- "name": "anonymous-user"
828
- },
829
- {
830
- "id": -2,
831
- "name": "logged-user"
832
- },
833
- {
834
- "id": -3,
835
- "name": "superadmin"
836
- }
837
- ]
890
+ }
838
891
  },
839
892
  {
840
893
  "pureName": "user_connections",
@@ -1008,47 +1061,6 @@ module.exports = {
1008
1061
  }
1009
1062
  ]
1010
1063
  }
1011
- },
1012
- {
1013
- "pureName": "users",
1014
- "columns": [
1015
- {
1016
- "pureName": "users",
1017
- "columnName": "id",
1018
- "dataType": "int",
1019
- "autoIncrement": true,
1020
- "notNull": true
1021
- },
1022
- {
1023
- "pureName": "users",
1024
- "columnName": "login",
1025
- "dataType": "varchar(250)",
1026
- "notNull": false
1027
- },
1028
- {
1029
- "pureName": "users",
1030
- "columnName": "password",
1031
- "dataType": "varchar(250)",
1032
- "notNull": false
1033
- },
1034
- {
1035
- "pureName": "users",
1036
- "columnName": "email",
1037
- "dataType": "varchar(250)",
1038
- "notNull": false
1039
- }
1040
- ],
1041
- "foreignKeys": [],
1042
- "primaryKey": {
1043
- "pureName": "users",
1044
- "constraintType": "primaryKey",
1045
- "constraintName": "PK_users",
1046
- "columns": [
1047
- {
1048
- "columnName": "id"
1049
- }
1050
- ]
1051
- }
1052
1064
  }
1053
1065
  ],
1054
1066
  "collections": [],
@@ -40,6 +40,16 @@ function getLicenseHttpHeaders() {
40
40
  return {};
41
41
  }
42
42
 
43
+ async function tryToGetRefreshedLicense(oldLicenseKey) {
44
+ return {
45
+ status: 'error',
46
+ };
47
+ }
48
+
49
+ function getAiGatewayServer() {
50
+ return {};
51
+ }
52
+
43
53
  module.exports = {
44
54
  isAuthProxySupported,
45
55
  authProxyGetRedirectUrl,
@@ -52,4 +62,6 @@ module.exports = {
52
62
  callCompleteOnCursorApi,
53
63
  callRefactorSqlQueryApi,
54
64
  getLicenseHttpHeaders,
65
+ tryToGetRefreshedLicense,
66
+ getAiGatewayServer,
55
67
  };
@@ -1,4 +1,5 @@
1
1
  const axios = require('axios');
2
+ const crypto = require('crypto');
2
3
  const fs = require('fs-extra');
3
4
  const _ = require('lodash');
4
5
  const path = require('path');
@@ -216,7 +217,7 @@ async function updateCloudFiles(isRefresh) {
216
217
  {
217
218
  headers: {
218
219
  ...getLicenseHttpHeaders(),
219
- ...(await getCloudSigninHeaders()),
220
+ ...(await getCloudInstanceHeaders()),
220
221
  'x-app-version': currentVersion.version,
221
222
  },
222
223
  }
@@ -300,6 +301,17 @@ async function callCloudApiGet(endpoint, signinHolder = null, additionalHeaders
300
301
  return resp.data;
301
302
  }
302
303
 
304
+ async function getCloudInstanceHeaders() {
305
+ if (!(await fs.exists(path.join(datadir(), 'cloud-instance.txt')))) {
306
+ const newInstanceId = crypto.randomUUID();
307
+ await fs.writeFile(path.join(datadir(), 'cloud-instance.txt'), newInstanceId);
308
+ }
309
+ const instanceId = await fs.readFile(path.join(datadir(), 'cloud-instance.txt'), 'utf-8');
310
+ return {
311
+ 'x-cloud-instance': instanceId,
312
+ };
313
+ }
314
+
303
315
  async function callCloudApiPost(endpoint, body, signinHolder = null) {
304
316
  if (!signinHolder) {
305
317
  signinHolder = await getCloudSigninHolder();
@@ -101,24 +101,26 @@ function decryptObjectPasswordField(obj, field, encryptor = null) {
101
101
  return obj;
102
102
  }
103
103
 
104
+ const fieldsToEncrypt = ['password', 'sshPassword', 'sshKeyfilePassword', 'connectionDefinition'];
105
+
104
106
  function encryptConnection(connection, encryptor = null) {
105
107
  if (connection.passwordMode != 'saveRaw') {
106
- connection = encryptObjectPasswordField(connection, 'password', encryptor);
107
- connection = encryptObjectPasswordField(connection, 'sshPassword', encryptor);
108
- connection = encryptObjectPasswordField(connection, 'sshKeyfilePassword', encryptor);
108
+ for (const field of fieldsToEncrypt) {
109
+ connection = encryptObjectPasswordField(connection, field, encryptor);
110
+ }
109
111
  }
110
112
  return connection;
111
113
  }
112
114
 
113
115
  function maskConnection(connection) {
114
116
  if (!connection) return connection;
115
- return _.omit(connection, ['password', 'sshPassword', 'sshKeyfilePassword']);
117
+ return _.omit(connection, fieldsToEncrypt);
116
118
  }
117
119
 
118
- function decryptConnection(connection, encryptor = null) {
119
- connection = decryptObjectPasswordField(connection, 'password', encryptor);
120
- connection = decryptObjectPasswordField(connection, 'sshPassword', encryptor);
121
- connection = decryptObjectPasswordField(connection, 'sshKeyfilePassword', encryptor);
120
+ function decryptConnection(connection) {
121
+ for (const field of fieldsToEncrypt) {
122
+ connection = decryptObjectPasswordField(connection, field);
123
+ }
122
124
  return connection;
123
125
  }
124
126
 
@@ -188,9 +190,9 @@ function recryptObjectPasswordFieldInPlace(obj, field, decryptEncryptor, encrypt
188
190
  }
189
191
 
190
192
  function recryptConnection(connection, decryptEncryptor, encryptEncryptor) {
191
- connection = recryptObjectPasswordField(connection, 'password', decryptEncryptor, encryptEncryptor);
192
- connection = recryptObjectPasswordField(connection, 'sshPassword', decryptEncryptor, encryptEncryptor);
193
- connection = recryptObjectPasswordField(connection, 'sshKeyfilePassword', decryptEncryptor, encryptEncryptor);
193
+ for (const field of fieldsToEncrypt) {
194
+ connection = recryptObjectPasswordField(connection, field, decryptEncryptor, encryptEncryptor);
195
+ }
194
196
  return connection;
195
197
  }
196
198
 
@@ -1,15 +1,18 @@
1
1
  // only in DbGate Premium
2
2
 
3
- function markUserAsActive(licenseUid) {}
3
+ function markUserAsActive(licenseUid, token) {}
4
4
 
5
5
  async function isLoginLicensed(req, licenseUid) {
6
6
  return true;
7
7
  }
8
8
 
9
+ function markLoginAsLoggedOut(licenseUid) {}
10
+
9
11
  const LOGIN_LIMIT_ERROR = '';
10
12
 
11
13
  module.exports = {
12
14
  markUserAsActive,
13
15
  isLoginLicensed,
16
+ markLoginAsLoggedOut,
14
17
  LOGIN_LIMIT_ERROR,
15
18
  };