dbgate-api-premium 6.1.6 → 6.2.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-premium",
3
3
  "main": "src/index.js",
4
- "version": "6.1.6",
4
+ "version": "6.2.0",
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.1.6",
32
+ "dbgate-datalib": "^6.2.0",
33
33
  "dbgate-query-splitter": "^4.11.3",
34
- "dbgate-sqltree": "^6.1.6",
35
- "dbgate-tools": "^6.1.6",
34
+ "dbgate-sqltree": "^6.2.0",
35
+ "dbgate-tools": "^6.2.0",
36
36
  "debug": "^4.3.4",
37
37
  "diff": "^5.0.0",
38
38
  "diff2html": "^3.4.13",
@@ -74,6 +74,8 @@
74
74
  "start:storage": "env-cmd -f env/storage/.env node src/index.js --listen-api",
75
75
  "start:storage:built": "env-cmd -f env/storage/.env cross-env DEVMODE= BUILTWEBMODE=1 node dist/bundle.js --listen-api",
76
76
  "start:singleconn": "env-cmd node src/index.js --server localhost --user root --port 3307 --engine mysql@dbgate-plugin-mysql --password test --listen-api",
77
+ "start:azure": "env-cmd -f env/azure/.env node src/index.js --listen-api",
78
+ "start:e2e:team": "cross-env DEVWEB=1 DEVMODE=1 env-cmd -f ../../e2e-tests/env/team/.env node src/index.js --listen-api",
77
79
  "ts": "tsc",
78
80
  "build": "webpack",
79
81
  "build:doc": "jsdoc2md --template doctpl.hbs ./src/shell/* > ../../../dbgate.github.io/_docs/apidoc.md"
@@ -81,7 +83,7 @@
81
83
  "devDependencies": {
82
84
  "@types/fs-extra": "^9.0.11",
83
85
  "@types/lodash": "^4.14.149",
84
- "dbgate-types": "^6.1.6",
86
+ "dbgate-types": "^6.2.0",
85
87
  "env-cmd": "^10.1.0",
86
88
  "jsdoc-to-markdown": "^9.0.5",
87
89
  "node-loader": "^1.0.2",
@@ -3,12 +3,14 @@ const {
3
3
  getPredefinedPermissions,
4
4
  getLogger,
5
5
  getConnectionLabel,
6
+ runQueryFmt,
6
7
  } = require('dbgate-tools');
7
8
  const {
8
9
  storageSelectFmt,
9
10
  storageReadUserRolePermissions,
10
11
  storageReadUserPermissions,
11
12
  storageReadRolePermissions,
13
+ storageSqlCommandFmt,
12
14
  } = require('../controllers/storageDb');
13
15
  const { getTokenSecret, getTokenLifetime } = require('./authCommon');
14
16
  const { AuthProviderBase } = require('./authProvider');
@@ -129,9 +131,13 @@ class OauthProvider extends StorageProviderBase {
129
131
  )}&client_id=${this.config.oauthClient}&client_secret=${this.config.oauthClientSecret}${scopeParam}`
130
132
  );
131
133
 
132
- const { access_token, refresh_token } = resp.data;
134
+ const { access_token, id_token, refresh_token } = resp.data;
133
135
 
134
- const payload = jwt.decode(access_token);
136
+ if (!id_token && !access_token) {
137
+ return { error: 'Token not found' };
138
+ }
139
+
140
+ const payload = jwt.decode(id_token ?? access_token);
135
141
 
136
142
  logger.info({ payload }, 'User payload returned from OAUTH');
137
143
 
@@ -143,10 +149,13 @@ class OauthProvider extends StorageProviderBase {
143
149
  const loginRows = await storageSelectFmt('select * from ~users where ~login = %v', login);
144
150
  const permissions = await loadPermissionsForUserId(loginRows[0]?.id ?? -1);
145
151
 
146
- if (this.config.oauthOnlyDefinedLogins == 1) {
147
- if (loginRows.length == 0) {
148
- return { error: `Username ${login} not allowed to log in` };
149
- }
152
+ if (this.config.oauthOnlyDefinedLogins == 1 && loginRows.length == 0) {
153
+ return { error: `Username ${login} not allowed to log in` };
154
+ }
155
+
156
+ if (this.config.oauthSaveNotDefinedLogins == 1 && loginRows.length == 0) {
157
+ const email = payload[this.config.oauthEmailField || 'email'] ?? null;
158
+ await storageSqlCommandFmt('insert into ~users (~login, ~email) values (%v, %v)', login, email);
150
159
  }
151
160
 
152
161
  let groups =
@@ -165,22 +174,18 @@ class OauthProvider extends StorageProviderBase {
165
174
  }
166
175
  }
167
176
 
168
- if (access_token) {
169
- return {
170
- accessToken: jwt.sign(
171
- {
172
- amoid: this.amoid,
173
- login,
174
- permissions,
175
- userId: loginRows[0]?.id,
176
- },
177
- getTokenSecret(),
178
- { expiresIn: getTokenLifetime() }
179
- ),
180
- };
181
- }
182
-
183
- return { error: 'Token not found' };
177
+ return {
178
+ accessToken: jwt.sign(
179
+ {
180
+ amoid: this.amoid,
181
+ login,
182
+ permissions,
183
+ userId: loginRows[0]?.id,
184
+ },
185
+ getTokenSecret(),
186
+ { expiresIn: getTokenLifetime() }
187
+ ),
188
+ };
184
189
  }
185
190
 
186
191
  redirect({ state, redirectUri }) {
@@ -136,7 +136,7 @@ module.exports = {
136
136
 
137
137
  deleteSettings_meta: true,
138
138
  async deleteSettings() {
139
- await fs.unlink(path.join(datadir(), 'settings.json'));
139
+ await fs.unlink(path.join(datadir(), processArgs.runE2eTests ? 'settings-e2etests.json' : 'settings.json'));
140
140
  return true;
141
141
  },
142
142
 
@@ -161,7 +161,10 @@ module.exports = {
161
161
 
162
162
  async loadSettings() {
163
163
  try {
164
- const settingsText = await fs.readFile(path.join(datadir(), 'settings.json'), { encoding: 'utf-8' });
164
+ const settingsText = await fs.readFile(
165
+ path.join(datadir(), processArgs.runE2eTests ? 'settings-e2etests.json' : 'settings.json'),
166
+ { encoding: 'utf-8' }
167
+ );
165
168
  return {
166
169
  ...this.fillMissingSettings(JSON.parse(settingsText)),
167
170
  'other.licenseKey': platformInfo.isElectron ? await this.loadLicenseKey() : undefined,
@@ -247,7 +250,10 @@ module.exports = {
247
250
  ...currentValue,
248
251
  ..._.omit(values, ['other.licenseKey']),
249
252
  };
250
- await fs.writeFile(path.join(datadir(), 'settings.json'), JSON.stringify(updated, undefined, 2));
253
+ await fs.writeFile(
254
+ path.join(datadir(), processArgs.runE2eTests ? 'settings-e2etests.json' : 'settings.json'),
255
+ JSON.stringify(updated, undefined, 2)
256
+ );
251
257
  // this.settingsValue = updated;
252
258
 
253
259
  if (currentValue['other.licenseKey'] != values['other.licenseKey']) {
@@ -34,6 +34,8 @@ const pipeForkLogs = require('../utility/pipeForkLogs');
34
34
  const crypto = require('crypto');
35
35
  const loadModelTransform = require('../utility/loadModelTransform');
36
36
  const exportDbModelSql = require('../utility/exportDbModelSql');
37
+ const axios = require('axios');
38
+ const { callTextToSqlApi, callCompleteOnCursorApi, callRefactorSqlQueryApi } = require('../utility/authProxy');
37
39
 
38
40
  const logger = getLogger('databaseConnections');
39
41
 
@@ -69,6 +71,11 @@ module.exports = {
69
71
  handle_error(conid, database, props) {
70
72
  const { error } = props;
71
73
  logger.error(`Error in database connection ${conid}, database ${database}: ${error}`);
74
+ if (props?.msgid) {
75
+ const [resolve, reject] = this.requests[props?.msgid];
76
+ reject(error);
77
+ delete this.requests[props?.msgid];
78
+ }
72
79
  },
73
80
  handle_response(conid, database, { msgid, ...response }) {
74
81
  const [resolve, reject] = this.requests[msgid];
@@ -253,9 +260,9 @@ module.exports = {
253
260
  },
254
261
 
255
262
  loadFieldValues_meta: true,
256
- async loadFieldValues({ conid, database, schemaName, pureName, field, search }, req) {
263
+ async loadFieldValues({ conid, database, schemaName, pureName, field, search, dataType }, req) {
257
264
  testConnectionPermission(conid, req);
258
- return this.loadDataCore('loadFieldValues', { conid, database, schemaName, pureName, field, search });
265
+ return this.loadDataCore('loadFieldValues', { conid, database, schemaName, pureName, field, search, dataType });
259
266
  },
260
267
 
261
268
  callMethod_meta: true,
@@ -401,6 +408,10 @@ module.exports = {
401
408
 
402
409
  structure_meta: true,
403
410
  async structure({ conid, database, modelTransFile = null }, req) {
411
+ if (!conid || !database) {
412
+ return {};
413
+ }
414
+
404
415
  testConnectionPermission(conid, req);
405
416
  if (conid == '__model') {
406
417
  const model = await importDbModel(database);
@@ -558,4 +569,47 @@ module.exports = {
558
569
 
559
570
  return true;
560
571
  },
572
+
573
+ textToSql_meta: true,
574
+ async textToSql({ conid, database, text, dialect }) {
575
+ const existing = this.opened.find(x => x.conid == conid && x.database == database);
576
+ const { structure } = existing || {};
577
+ if (!structure) return { errorMessage: 'No database structure' };
578
+
579
+ const res = await callTextToSqlApi(text, structure, dialect);
580
+
581
+ if (!res?.sql) {
582
+ return { errorMessage: 'No SQL generated' };
583
+ }
584
+
585
+ return res;
586
+ },
587
+
588
+ completeOnCursor_meta: true,
589
+ async completeOnCursor({ conid, database, text, dialect, line }) {
590
+ const existing = this.opened.find(x => x.conid == conid && x.database == database);
591
+ const { structure } = existing || {};
592
+ if (!structure) return { errorMessage: 'No database structure' };
593
+ const res = await callCompleteOnCursorApi(text, structure, dialect, line);
594
+
595
+ if (!res?.variants) {
596
+ return { errorMessage: 'No SQL generated' };
597
+ }
598
+
599
+ return res;
600
+ },
601
+
602
+ refactorSqlQuery_meta: true,
603
+ async refactorSqlQuery({ conid, database, query, task, dialect }) {
604
+ const existing = this.opened.find(x => x.conid == conid && x.database == database);
605
+ const { structure } = existing || {};
606
+ if (!structure) return { errorMessage: 'No database structure' };
607
+ const res = await callRefactorSqlQueryApi(query, task, structure, dialect);
608
+
609
+ if (!res?.sql) {
610
+ return { errorMessage: 'No SQL generated' };
611
+ }
612
+
613
+ return res;
614
+ },
561
615
  };
@@ -17,7 +17,7 @@ const { hasPermission } = require('../utility/hasPermission');
17
17
  const { changeSetToSql, removeSchemaFromChangeSet } = require('dbgate-datalib');
18
18
  const storageModel = require('../storageModel');
19
19
  const { dumpSqlCommand } = require('dbgate-sqltree');
20
- const { runCommandOnDriver, getLogger } = require('dbgate-tools');
20
+ const { runCommandOnDriver, getLogger, runQueryFmt } = require('dbgate-tools');
21
21
  const socket = require('../utility/socket');
22
22
  const { obtainRefreshedLicense } = require('../utility/authProxy');
23
23
  const { datadir } = require('../utility/directories');
@@ -31,11 +31,6 @@ function mapConnection(connnection) {
31
31
  };
32
32
  }
33
33
 
34
- async function runQueryFmt(driver, conn, query, ...args) {
35
- const dmp = driver.createDumper();
36
- dmp.put(query, ...args);
37
- await driver.query(conn, dmp.s);
38
- }
39
34
 
40
35
  let refreshLicenseStarted = false;
41
36
 
@@ -140,7 +135,7 @@ module.exports = {
140
135
  displayName: 'Internal storage',
141
136
  defaultDatabase: process.env.STORAGE_DATABASE,
142
137
  singleDatabase: true,
143
- ...getDbConnectionParams(),
138
+ ...await getDbConnectionParams(),
144
139
  };
145
140
  }
146
141
 
@@ -197,7 +192,8 @@ module.exports = {
197
192
  await runQueryFmt(
198
193
  driver,
199
194
  conn,
200
- 'update auth_methods set is_disabled=%v, is_default = %v, is_collapsed = %v where id = %v',
195
+ 'update auth_methods set name=%v, is_disabled=%v, is_default = %v, is_collapsed = %v where id = %v',
196
+ method.name,
201
197
  method.isDisabled,
202
198
  method.isDefault,
203
199
  method.isCollapsed,
@@ -4,8 +4,9 @@ const dbgateApi = require('../shell');
4
4
  const { getPredefinedPermissions, getLogger, extractErrorLogData } = require('dbgate-tools');
5
5
  const _ = require('lodash');
6
6
  const logger = getLogger('storageDb');
7
+ const { extractConnectionSslParams } = require('../utility/connectUtility');
7
8
 
8
- function getDbConnectionParams() {
9
+ async function getDbConnectionParams() {
9
10
  const server = process.env.STORAGE_SERVER;
10
11
  const port = process.env.STORAGE_PORT;
11
12
  const user = process.env.STORAGE_USER;
@@ -13,21 +14,33 @@ function getDbConnectionParams() {
13
14
  const database = process.env.STORAGE_DATABASE;
14
15
  const engine = process.env.STORAGE_ENGINE;
15
16
  const serviceName = process.env.STORAGE_SERVICE_NAME;
17
+ const socketPath = process.env.STORAGE_SOCKET_PATH;
18
+ const databaseUrl = process.env.STORAGE_DATABASE_URL;
19
+ const useDatabaseUrl = !!process.env.STORAGE_DATABASE_URL;
20
+ const authType = process.env.STORAGE_AUTH_TYPE;
16
21
 
17
- if (!server || !user || !password || !database || !engine) {
22
+ const useSsl = process.env.STORAGE_USE_SSL;
23
+ const sslCaFile = process.env.STORAGE_SSL_CA_FILE;
24
+ const sslCertFile = process.env.STORAGE_SSL_CERT_FILE;
25
+ const sslCertFilePassword = process.env.STORAGE_SSL_CERT_FILE_PASSWORD;
26
+ const sslKeyFile = process.env.STORAGE_SSL_KEY_FILE;
27
+ const sslRejectUnauthorized = process.env.STORAGE_SSL_REJECT_UNAUTHORIZED;
28
+ const trustServerCertificate = process.env.STORAGE_SSL_TRUST_CERTIFICATE;
29
+
30
+ if (!server || !engine) {
18
31
  if (database) {
19
32
  if (!server) {
20
33
  throw new Error('Incorrect storage configuration, missing env variable STORAGE_SERVER');
21
34
  }
22
- if (!port) {
23
- throw new Error('Incorrect storage configuration, missing env variable STORAGE_PORT');
24
- }
25
- if (!user) {
26
- throw new Error('Incorrect storage configuration, missing env variable STORAGE_USER');
27
- }
28
- if (!password) {
29
- throw new Error('Incorrect storage configuration, missing env variable STORAGE_PASSWORD');
30
- }
35
+ // if (!port) {
36
+ // throw new Error('Incorrect storage configuration, missing env variable STORAGE_PORT');
37
+ // }
38
+ // if (!user) {
39
+ // throw new Error('Incorrect storage configuration, missing env variable STORAGE_USER');
40
+ // }
41
+ // if (!password) {
42
+ // throw new Error('Incorrect storage configuration, missing env variable STORAGE_PASSWORD');
43
+ // }
31
44
  if (!engine) {
32
45
  throw new Error('Incorrect storage configuration, missing env variable STORAGE_ENGINE');
33
46
  }
@@ -35,7 +48,30 @@ function getDbConnectionParams() {
35
48
  return null;
36
49
  }
37
50
 
38
- return { server, port, user, password, database, engine, serviceName };
51
+ const res = {
52
+ server,
53
+ port,
54
+ user,
55
+ password,
56
+ database,
57
+ engine,
58
+ serviceName,
59
+ socketPath,
60
+ databaseUrl,
61
+ useDatabaseUrl,
62
+ authType,
63
+ useSsl,
64
+ sslCaFile,
65
+ sslCertFile,
66
+ sslCertFilePassword,
67
+ sslKeyFile,
68
+ sslRejectUnauthorized,
69
+ trustServerCertificate,
70
+ };
71
+ return {
72
+ ...res,
73
+ ssl: await extractConnectionSslParams(res),
74
+ };
39
75
  }
40
76
 
41
77
  let storageConnection = null;
@@ -47,7 +83,7 @@ async function getStorageConnectionCore() {
47
83
  return [storageConnection, storageDriver];
48
84
  }
49
85
 
50
- const dbConnectionParams = getDbConnectionParams();
86
+ const dbConnectionParams = await getDbConnectionParams();
51
87
 
52
88
  if (!dbConnectionParams) {
53
89
  return [null, null];
@@ -125,6 +161,10 @@ async function storageSelectFmt(sql, ...params) {
125
161
  return resp.rows;
126
162
  }
127
163
 
164
+ async function storageSqlCommandFmt(sql, ...params) {
165
+ await storageSelectFmt(sql, ...params);
166
+ }
167
+
128
168
  async function storageReadUserRolePermissions(userId) {
129
169
  const resp = await storageSelectFmt(
130
170
  `
@@ -213,4 +253,5 @@ module.exports = {
213
253
  storageReadConfig,
214
254
  storageWriteConfig,
215
255
  getStorageConnectionError,
256
+ storageSqlCommandFmt,
216
257
  };
@@ -1,5 +1,5 @@
1
1
 
2
2
  module.exports = {
3
- version: '6.1.6',
4
- buildTime: '2025-02-04T16:06:06.228Z'
3
+ version: '6.2.0',
4
+ buildTime: '2025-02-14T13:58:57.650Z'
5
5
  };
package/src/main.js CHANGED
@@ -78,6 +78,8 @@ function start() {
78
78
  app.use(getExpressPath('/'), express.static('/home/dbgate-docker/public'));
79
79
  } else if (platformInfo.isAwsUbuntuLayout) {
80
80
  app.use(getExpressPath('/'), express.static('/home/ubuntu/build/public'));
81
+ } else if (platformInfo.isAzureUbuntuLayout) {
82
+ app.use(getExpressPath('/'), express.static('/home/azureuser/build/public'));
81
83
  } else if (processArgs.runE2eTests) {
82
84
  app.use(getExpressPath('/'), express.static(path.resolve('packer/build/public')));
83
85
  } else if (platformInfo.isNpmDist) {
@@ -140,6 +142,10 @@ function start() {
140
142
  const port = process.env.PORT || 3000;
141
143
  logger.info(`DbGate API listening on port ${port} (AWS AMI build)`);
142
144
  server.listen(port);
145
+ } else if (platformInfo.isAzureUbuntuLayout) {
146
+ const port = process.env.PORT || 3000;
147
+ logger.info(`DbGate API listening on port ${port} (Azure VM build)`);
148
+ server.listen(port);
143
149
  } else if (platformInfo.isNpmDist) {
144
150
  getPort({
145
151
  port: parseInt(
@@ -1,6 +1,6 @@
1
1
  const childProcessChecker = require('../utility/childProcessChecker');
2
2
  const requireEngineDriver = require('../utility/requireEngineDriver');
3
- const connectUtility = require('../utility/connectUtility');
3
+ const { connectUtility } = require('../utility/connectUtility');
4
4
  const { handleProcessCommunication } = require('../utility/processComm');
5
5
  const { pickSafeConnectionInfo } = require('../utility/crypting');
6
6
  const _ = require('lodash');
@@ -11,7 +11,7 @@ const {
11
11
  extractErrorLogData,
12
12
  } = require('dbgate-tools');
13
13
  const requireEngineDriver = require('../utility/requireEngineDriver');
14
- const connectUtility = require('../utility/connectUtility');
14
+ const { connectUtility } = require('../utility/connectUtility');
15
15
  const { handleProcessCommunication } = require('../utility/processComm');
16
16
  const { SqlGenerator } = require('dbgate-tools');
17
17
  const generateDeploySql = require('../shell/generateDeploySql');
@@ -213,13 +213,12 @@ async function handleRunOperation({ msgid, operation, useTransaction }, skipRead
213
213
  }
214
214
  }
215
215
 
216
- async function handleQueryData({ msgid, sql }, skipReadonlyCheck = false) {
216
+ async function handleQueryData({ msgid, sql, range }, skipReadonlyCheck = false) {
217
217
  await waitConnected();
218
218
  const driver = requireEngineDriver(storedConnection);
219
219
  try {
220
220
  if (!skipReadonlyCheck) ensureExecuteCustomScript(driver);
221
- // console.log(sql);
222
- const res = await driver.query(dbhan, sql);
221
+ const res = await driver.query(dbhan, sql, { range });
223
222
  process.send({ msgtype: 'response', msgid, ...res });
224
223
  } catch (err) {
225
224
  process.send({
@@ -234,7 +233,7 @@ async function handleSqlSelect({ msgid, select }) {
234
233
  const driver = requireEngineDriver(storedConnection);
235
234
  const dmp = driver.createDumper();
236
235
  dumpSqlSelect(dmp, select);
237
- return handleQueryData({ msgid, sql: dmp.s }, true);
236
+ return handleQueryData({ msgid, sql: dmp.s, range: select.range }, true);
238
237
  }
239
238
 
240
239
  async function handleDriverDataCore(msgid, callMethod, { logName }) {
@@ -291,10 +290,14 @@ async function handleLoadKeyTableRange({ msgid, key, cursor, count }) {
291
290
  });
292
291
  }
293
292
 
294
- async function handleLoadFieldValues({ msgid, schemaName, pureName, field, search }) {
295
- return handleDriverDataCore(msgid, driver => driver.loadFieldValues(dbhan, { schemaName, pureName }, field, search), {
296
- logName: 'loadFieldValues',
297
- });
293
+ async function handleLoadFieldValues({ msgid, schemaName, pureName, field, search, dataType }) {
294
+ return handleDriverDataCore(
295
+ msgid,
296
+ driver => driver.loadFieldValues(dbhan, { schemaName, pureName }, field, search, dataType),
297
+ {
298
+ logName: 'loadFieldValues',
299
+ }
300
+ );
298
301
  }
299
302
 
300
303
  function ensureExecuteCustomScript(driver) {
@@ -336,6 +339,7 @@ async function handleSqlPreview({ msgid, objects, options }) {
336
339
  }, 500);
337
340
  }
338
341
  } catch (err) {
342
+ console.error(err);
339
343
  process.send({
340
344
  msgtype: 'response',
341
345
  msgid,
@@ -423,7 +427,11 @@ function start() {
423
427
  await handleMessage(message);
424
428
  } catch (err) {
425
429
  logger.error(extractErrorLogData(err), 'Error in DB connection');
426
- process.send({ msgtype: 'error', error: extractErrorMessage(err, 'Error processing message') });
430
+ process.send({
431
+ msgtype: 'error',
432
+ error: extractErrorMessage(err, 'Error processing message'),
433
+ msgid: message?.msgid,
434
+ });
427
435
  }
428
436
  });
429
437
  }
@@ -2,7 +2,7 @@ const stableStringify = require('json-stable-stringify');
2
2
  const { extractBoolSettingsValue, extractIntSettingsValue, getLogger, extractErrorLogData } = require('dbgate-tools');
3
3
  const childProcessChecker = require('../utility/childProcessChecker');
4
4
  const requireEngineDriver = require('../utility/requireEngineDriver');
5
- const connectUtility = require('../utility/connectUtility');
5
+ const { connectUtility } = require('../utility/connectUtility');
6
6
  const { handleProcessCommunication } = require('../utility/processComm');
7
7
  const logger = getLogger('srvconnProcess');
8
8
 
@@ -8,7 +8,7 @@ const { splitQuery } = require('dbgate-query-splitter');
8
8
  const { jsldir } = require('../utility/directories');
9
9
  const requireEngineDriver = require('../utility/requireEngineDriver');
10
10
  const { decryptConnection } = require('../utility/crypting');
11
- const connectUtility = require('../utility/connectUtility');
11
+ const { connectUtility } = require('../utility/connectUtility');
12
12
  const { handleProcessCommunication } = require('../utility/processComm');
13
13
  const { getLogger, extractIntSettingsValue, extractBoolSettingsValue } = require('dbgate-tools');
14
14
 
@@ -2,7 +2,7 @@ const stream = require('stream');
2
2
  const path = require('path');
3
3
  const { quoteFullName, fullNameToString, getLogger } = require('dbgate-tools');
4
4
  const requireEngineDriver = require('../utility/requireEngineDriver');
5
- const connectUtility = require('../utility/connectUtility');
5
+ const { connectUtility } = require('../utility/connectUtility');
6
6
  const logger = getLogger('dataDuplicator');
7
7
  const { DataDuplicator } = require('dbgate-datalib');
8
8
  const copyStream = require('./copyStream');
@@ -1,7 +1,7 @@
1
1
  const generateDeploySql = require('./generateDeploySql');
2
2
  const executeQuery = require('./executeQuery');
3
3
  const { ScriptDrivedDeployer } = require('dbgate-datalib');
4
- const connectUtility = require('../utility/connectUtility');
4
+ const { connectUtility } = require('../utility/connectUtility');
5
5
  const requireEngineDriver = require('../utility/requireEngineDriver');
6
6
  const loadModelFolder = require('../utility/loadModelFolder');
7
7
  const crypto = require('crypto');
@@ -1,6 +1,6 @@
1
1
  const executeQuery = require('./executeQuery');
2
2
  const requireEngineDriver = require('../utility/requireEngineDriver');
3
- const connectUtility = require('../utility/connectUtility');
3
+ const { connectUtility } = require('../utility/connectUtility');
4
4
  const { getLogger, extendDatabaseInfo } = require('dbgate-tools');
5
5
 
6
6
  const logger = getLogger('dropAllDbObjects');
@@ -1,5 +1,5 @@
1
1
  const requireEngineDriver = require('../utility/requireEngineDriver');
2
- const connectUtility = require('../utility/connectUtility');
2
+ const { connectUtility } = require('../utility/connectUtility');
3
3
  const { getLogger } = require('dbgate-tools');
4
4
 
5
5
  const logger = getLogger('dumpDb');
@@ -1,6 +1,6 @@
1
1
  const fs = require('fs-extra');
2
2
  const requireEngineDriver = require('../utility/requireEngineDriver');
3
- const connectUtility = require('../utility/connectUtility');
3
+ const { connectUtility } = require('../utility/connectUtility');
4
4
  const { getLogger, getLimitedQuery } = require('dbgate-tools');
5
5
 
6
6
  const logger = getLogger('execQuery');
@@ -13,7 +13,7 @@ const {
13
13
  } = require('dbgate-tools');
14
14
  const importDbModel = require('../utility/importDbModel');
15
15
  const requireEngineDriver = require('../utility/requireEngineDriver');
16
- const connectUtility = require('../utility/connectUtility');
16
+ const { connectUtility } = require('../utility/connectUtility');
17
17
 
18
18
  /**
19
19
  * Generates query for deploying model into database
@@ -1,6 +1,6 @@
1
1
  const fs = require('fs');
2
2
  const requireEngineDriver = require('../utility/requireEngineDriver');
3
- const connectUtility = require('../utility/connectUtility');
3
+ const { connectUtility } = require('../utility/connectUtility');
4
4
  const { splitQueryStream } = require('dbgate-query-splitter/lib/splitQueryStream');
5
5
  const download = require('./download');
6
6
  const stream = require('stream');
@@ -1,5 +1,5 @@
1
1
  const requireEngineDriver = require('../utility/requireEngineDriver');
2
- const connectUtility = require('../utility/connectUtility');
2
+ const { connectUtility } = require('../utility/connectUtility');
3
3
  const { getLogger } = require('dbgate-tools');
4
4
  const exportDbModel = require('../utility/exportDbModel');
5
5
 
@@ -1,5 +1,5 @@
1
1
  const requireEngineDriver = require('../utility/requireEngineDriver');
2
- const connectUtility = require('../utility/connectUtility');
2
+ const { connectUtility } = require('../utility/connectUtility');
3
3
  const { getLogger } = require('dbgate-tools');
4
4
  const logger = getLogger('queryReader');
5
5
 
@@ -1,6 +1,6 @@
1
1
  const { quoteFullName, fullNameToString, getLogger } = require('dbgate-tools');
2
2
  const requireEngineDriver = require('../utility/requireEngineDriver');
3
- const connectUtility = require('../utility/connectUtility');
3
+ const { connectUtility } = require('../utility/connectUtility');
4
4
  const logger = getLogger('tableReader');
5
5
 
6
6
  /**
@@ -1,6 +1,6 @@
1
1
  const { fullNameToString, getLogger } = require('dbgate-tools');
2
2
  const requireEngineDriver = require('../utility/requireEngineDriver');
3
- const connectUtility = require('../utility/connectUtility');
3
+ const { connectUtility } = require('../utility/connectUtility');
4
4
  const logger = getLogger('tableWriter');
5
5
 
6
6
  /**
@@ -2,11 +2,24 @@ const axios = require('axios');
2
2
  const { Signer } = require('@aws-sdk/rds-signer');
3
3
  const jwt = require('jsonwebtoken');
4
4
  const { getLogger, extractErrorLogData } = require('dbgate-tools');
5
+ const stableStringify = require('json-stable-stringify');
6
+ const crypto = require('crypto');
7
+ const _ = require('lodash');
8
+ const processArgs = require('./processArgs');
5
9
 
6
10
  const logger = getLogger('authProxy');
7
11
 
8
- const AUTH_PROXY_URL = process.env.DEVWEB || process.env.DEVMODE ? 'https://auth-proxy.dbgate.udolni.net' : 'https://auth.dbgate.eu';
9
- // const AUTH_PROXY_URL = 'https://auth-proxy.dbgate.udolni.net';
12
+ const AUTH_PROXY_URL = process.env.LOCAL_AUTH_PROXY
13
+ ? 'http://localhost:3109'
14
+ : process.env.DEVWEB || process.env.DEVMODE
15
+ ? 'https://auth-proxy.dbgate.udolni.net'
16
+ : 'https://auth.dbgate.eu';
17
+
18
+ const AI_GATEWAY_URL = process.env.LOCAL_AI_GATEWAY
19
+ ? 'http://localhost:3110'
20
+ : process.env.DEVWEB || process.env.DEVMODE
21
+ ? 'https://aigw.dbgate.udolni.net'
22
+ : 'https://aigw.dbgate.io';
10
23
 
11
24
  let licenseKey = null;
12
25
 
@@ -18,6 +31,16 @@ function isAuthProxySupported() {
18
31
  return true;
19
32
  }
20
33
 
34
+ function getAxiosParamsWithLicense() {
35
+ return {
36
+ headers: {
37
+ 'Content-Type': 'application/json',
38
+ Authorization: `Bearer ${licenseKey ?? process.env.DBGATE_LICENSE}`,
39
+ 'x-api-key': processArgs.runE2eTests ? 'bcf6e1a0-5763-4060-9391-18fda005722d' : null,
40
+ },
41
+ };
42
+ }
43
+
21
44
  async function authProxyGetRedirectUrl({ client, type, state, redirectUri }) {
22
45
  const respSession = await axios.default.post(
23
46
  `${AUTH_PROXY_URL}/create-session`,
@@ -25,12 +48,7 @@ async function authProxyGetRedirectUrl({ client, type, state, redirectUri }) {
25
48
  client,
26
49
  type,
27
50
  },
28
- {
29
- headers: {
30
- 'Content-Type': 'application/json',
31
- Authorization: `Bearer ${licenseKey ?? process.env.DBGATE_LICENSE}`,
32
- },
33
- }
51
+ getAxiosParamsWithLicense()
34
52
  );
35
53
 
36
54
  const { sid } = respSession.data;
@@ -55,12 +73,7 @@ async function authProxyGetTokenFromCode({ sid, code }) {
55
73
  sid,
56
74
  code,
57
75
  },
58
- {
59
- headers: {
60
- 'Content-Type': 'application/json',
61
- Authorization: `Bearer ${licenseKey ?? process.env.DBGATE_LICENSE}`,
62
- },
63
- }
76
+ getAxiosParamsWithLicense()
64
77
  );
65
78
  return respToken.data.token;
66
79
  } catch (err) {
@@ -82,12 +95,7 @@ function startTokenChecking(sid, callback) {
82
95
  {
83
96
  sid,
84
97
  },
85
- {
86
- headers: {
87
- 'Content-Type': 'application/json',
88
- Authorization: `Bearer ${licenseKey ?? process.env.DBGATE_LICENSE}`,
89
- },
90
- }
98
+ getAxiosParamsWithLicense()
91
99
  );
92
100
 
93
101
  if (resp.data.status == 'ok') {
@@ -156,6 +164,110 @@ async function obtainRefreshedLicense() {
156
164
  }
157
165
  }
158
166
 
167
+ /**
168
+ * @param {import('dbgate-types').DatabaseInfo} structure
169
+ * @returns {import('dbgate-types').DatabaseInfoTiny}
170
+ */
171
+
172
+ function extractTinyStructure(structure) {
173
+ return {
174
+ t: _.sortBy(structure.tables, x => x.pureName).map(table => ({
175
+ n: table.pureName,
176
+ o: table.objectComment,
177
+ c: table.columns.map(column => ({ n: column.columnName, t: column.dataType })),
178
+ p: table.primaryKey
179
+ ? {
180
+ c: table.primaryKey?.columns?.map(column => ({ n: column.columnName })),
181
+ }
182
+ : undefined,
183
+ f: _.sortBy(table.foreignKeys, x => x.constraintName).map(fk => ({
184
+ r: fk.refTableName,
185
+ c: fk.columns.map(column => ({ n: column.columnName, r: column.refColumnName })),
186
+ })),
187
+ })),
188
+ };
189
+ }
190
+
191
+ function getSha256Hash(data) {
192
+ return crypto
193
+ .createHash('sha256') // volba hashovací funkce
194
+ .update(data, 'utf8') // aktualizace hashe o data (ve formátu UTF-8)
195
+ .digest('hex'); // získáme výsledek v hexadecimální reprezentaci
196
+ }
197
+
198
+ async function findModelOnProxy(hash) {
199
+ const resp = await axios.default.post(
200
+ `${AI_GATEWAY_URL}/find-db-model`,
201
+ {
202
+ hash,
203
+ },
204
+ getAxiosParamsWithLicense()
205
+ );
206
+ return resp.data.status == 'ok';
207
+ }
208
+
209
+ async function callTextToSqlApi(text, structure, dialect) {
210
+ const tinyStructure = extractTinyStructure(structure);
211
+ const json = stableStringify(tinyStructure);
212
+ const modelHash = getSha256Hash(json);
213
+ const isModelOnProxy = await findModelOnProxy(modelHash);
214
+
215
+ const resp = await axios.default.post(
216
+ `${AI_GATEWAY_URL}/text-to-sql`,
217
+ {
218
+ text,
219
+ modelHash,
220
+ model: isModelOnProxy ? null : tinyStructure,
221
+ dialect,
222
+ },
223
+ getAxiosParamsWithLicense()
224
+ );
225
+
226
+ return resp.data;
227
+ }
228
+
229
+ async function callCompleteOnCursorApi(text, structure, dialect, line) {
230
+ const tinyStructure = extractTinyStructure(structure);
231
+ const json = stableStringify(tinyStructure);
232
+ const modelHash = getSha256Hash(json);
233
+ const isModelOnProxy = await findModelOnProxy(modelHash);
234
+
235
+ const resp = await axios.default.post(
236
+ `${AI_GATEWAY_URL}/complete-on-cursor`,
237
+ {
238
+ text,
239
+ modelHash,
240
+ model: isModelOnProxy ? null : tinyStructure,
241
+ dialect,
242
+ line,
243
+ },
244
+ getAxiosParamsWithLicense()
245
+ );
246
+
247
+ return resp.data;
248
+ }
249
+
250
+ async function callRefactorSqlQueryApi(query, task, structure, dialect) {
251
+ const tinyStructure = extractTinyStructure(structure);
252
+ const json = stableStringify(tinyStructure);
253
+ const modelHash = getSha256Hash(json);
254
+ const isModelOnProxy = await findModelOnProxy(modelHash);
255
+
256
+ const resp = await axios.default.post(
257
+ `${AI_GATEWAY_URL}/refactor-sql-query`,
258
+ {
259
+ query,
260
+ task,
261
+ modelHash,
262
+ model: isModelOnProxy ? null : tinyStructure,
263
+ dialect,
264
+ },
265
+ getAxiosParamsWithLicense()
266
+ );
267
+
268
+ return resp.data;
269
+ }
270
+
159
271
  module.exports = {
160
272
  isAuthProxySupported,
161
273
  authProxyGetRedirectUrl,
@@ -166,4 +278,7 @@ module.exports = {
166
278
  supportsAwsIam,
167
279
  getAwsIamToken,
168
280
  obtainRefreshedLicense,
281
+ callTextToSqlApi,
282
+ callCompleteOnCursorApi,
283
+ callRefactorSqlQueryApi,
169
284
  };
@@ -9,6 +9,7 @@ const { setAuthProxyLicense } = require('./authProxy');
9
9
  const axios = require('axios');
10
10
  const crypto = require('crypto');
11
11
  const platformInfo = require('./platformInfo');
12
+ const processArgs = require('./processArgs');
12
13
 
13
14
  const logger = getLogger('checkLicense');
14
15
 
@@ -107,6 +108,13 @@ async function getAwsMetadata() {
107
108
  }
108
109
 
109
110
  function checkLicenseKey(licenseKey) {
111
+ if (processArgs.runE2eTests) {
112
+ return {
113
+ status: 'ok',
114
+ type: 'premium',
115
+ };
116
+ }
117
+
110
118
  try {
111
119
  const decoded = jwt.verify(licenseKey, publicKey, {
112
120
  algorithms: ['RS256'],
@@ -157,11 +165,11 @@ function checkLicenseKey(licenseKey) {
157
165
  }
158
166
  }
159
167
 
160
- let awsTokenLoaded = false;
161
- let awsTokenHash = null;
162
- function getAwsToken() {
163
- if (awsTokenLoaded) {
164
- return awsTokenHash;
168
+ let cloudTokenLoaded = false;
169
+ let cloudTokenHash = null;
170
+ function getCloudToken() {
171
+ if (cloudTokenLoaded) {
172
+ return cloudTokenHash;
165
173
  }
166
174
  try {
167
175
  const token = fs
@@ -169,18 +177,25 @@ function getAwsToken() {
169
177
  encoding: 'utf-8',
170
178
  })
171
179
  .trim();
172
- awsTokenHash = crypto.createHash('md5').update(token).digest('hex');
180
+ cloudTokenHash = crypto.createHash('md5').update(token).digest('hex');
173
181
  } catch (err) {}
174
- awsTokenLoaded = true;
175
- return awsTokenHash;
182
+ cloudTokenLoaded = true;
183
+ return cloudTokenHash;
176
184
  }
177
185
 
178
186
  async function checkLicense() {
187
+ if (processArgs.runE2eTests) {
188
+ return {
189
+ status: 'ok',
190
+ type: 'premium',
191
+ };
192
+ }
193
+
179
194
  if (process.env.DBGATE_LICENSE) {
180
195
  return checkLicenseKey(process.env.DBGATE_LICENSE);
181
196
  }
182
197
 
183
- if (platformInfo.isAwsUbuntuLayout && getAwsToken() == 'b93c7491890460063003a02de06ec84a') {
198
+ if (platformInfo.isAwsUbuntuLayout && getCloudToken() == 'b93c7491890460063003a02de06ec84a') {
184
199
  const metadata = await getAwsMetadata();
185
200
  if (metadata?.amiId) {
186
201
  return {
@@ -190,6 +205,13 @@ async function checkLicense() {
190
205
  }
191
206
  }
192
207
 
208
+ if (platformInfo.isAzureUbuntuLayout && getCloudToken() == 'b93c7491890460063003a02de06ec84a') {
209
+ return {
210
+ status: 'ok',
211
+ type: 'premium',
212
+ };
213
+ }
214
+
193
215
  if (process.env.STORAGE_DATABASE) {
194
216
  const licenseConfig = await storageReadConfig('license');
195
217
  const key = licenseConfig?.licenseKey;
@@ -47,50 +47,32 @@ async function loadConnection(driver, storedConnection, connectionMode) {
47
47
  return storedConnection;
48
48
  }
49
49
 
50
- async function connectUtility(driver, storedConnection, connectionMode, additionalOptions = null) {
51
- const connectionLoaded = await loadConnection(driver, storedConnection, connectionMode);
52
-
53
- const connection = {
54
- database: connectionLoaded.defaultDatabase,
55
- ...decryptConnection(connectionLoaded),
56
- };
57
-
58
- if (!connection.port && driver.defaultPort) connection.port = driver.defaultPort.toString();
59
-
60
- if (connection.useSshTunnel) {
61
- const tunnel = await getSshTunnelProxy(connection);
62
- if (tunnel.state == 'error') {
63
- throw new Error(tunnel.message);
64
- }
65
-
66
- connection.server = tunnel.localHost;
67
- connection.port = tunnel.localPort;
68
- }
69
-
70
- // SSL functionality - copied from https://github.com/beekeeper-studio/beekeeper-studio
50
+ async function extractConnectionSslParams(connection) {
51
+ /** @type {any} */
52
+ let ssl = undefined;
71
53
  if (connection.useSsl) {
72
- connection.ssl = {};
54
+ ssl = {};
73
55
 
74
56
  if (connection.sslCaFile) {
75
- connection.ssl.ca = await fs.readFile(connection.sslCaFile);
76
- connection.ssl.sslCaFile = connection.sslCaFile;
57
+ ssl.ca = await fs.readFile(connection.sslCaFile);
58
+ ssl.sslCaFile = connection.sslCaFile;
77
59
  }
78
60
 
79
61
  if (connection.sslCertFile) {
80
- connection.ssl.cert = await fs.readFile(connection.sslCertFile);
81
- connection.ssl.sslCertFile = connection.sslCertFile;
62
+ ssl.cert = await fs.readFile(connection.sslCertFile);
63
+ ssl.sslCertFile = connection.sslCertFile;
82
64
  }
83
65
 
84
66
  if (connection.sslKeyFile) {
85
- connection.ssl.key = await fs.readFile(connection.sslKeyFile);
86
- connection.ssl.sslKeyFile = connection.sslKeyFile;
67
+ ssl.key = await fs.readFile(connection.sslKeyFile);
68
+ ssl.sslKeyFile = connection.sslKeyFile;
87
69
  }
88
70
 
89
71
  if (connection.sslCertFilePassword) {
90
- connection.ssl.password = connection.sslCertFilePassword;
72
+ ssl.password = connection.sslCertFilePassword;
91
73
  }
92
74
 
93
- if (!connection.ssl.key && !connection.ssl.ca && !connection.ssl.cert) {
75
+ if (!ssl.key && !ssl.ca && !ssl.cert) {
94
76
  // TODO: provide this as an option in settings
95
77
  // or per-connection as 'reject self-signed certs'
96
78
  // How it works:
@@ -98,14 +80,41 @@ async function connectUtility(driver, storedConnection, connectionMode, addition
98
80
  // if true, has to be from a public CA
99
81
  // Heroku certs are self-signed.
100
82
  // if you provide ca/cert/key files, it overrides this
101
- connection.ssl.rejectUnauthorized = false;
83
+ ssl.rejectUnauthorized = false;
102
84
  } else {
103
- connection.ssl.rejectUnauthorized = connection.sslRejectUnauthorized;
85
+ ssl.rejectUnauthorized = connection.sslRejectUnauthorized;
86
+ }
87
+ }
88
+ return ssl;
89
+ }
90
+
91
+ async function connectUtility(driver, storedConnection, connectionMode, additionalOptions = null) {
92
+ const connectionLoaded = await loadConnection(driver, storedConnection, connectionMode);
93
+
94
+ const connection = {
95
+ database: connectionLoaded.defaultDatabase,
96
+ ...decryptConnection(connectionLoaded),
97
+ };
98
+
99
+ if (!connection.port && driver.defaultPort) connection.port = driver.defaultPort.toString();
100
+
101
+ if (connection.useSshTunnel) {
102
+ const tunnel = await getSshTunnelProxy(connection);
103
+ if (tunnel.state == 'error') {
104
+ throw new Error(tunnel.message);
104
105
  }
106
+
107
+ connection.server = tunnel.localHost;
108
+ connection.port = tunnel.localPort;
105
109
  }
106
110
 
111
+ connection.ssl = await extractConnectionSslParams(connection);
112
+
107
113
  const conn = await driver.connect({ ...connection, ...additionalOptions });
108
114
  return conn;
109
115
  }
110
116
 
111
- module.exports = connectUtility;
117
+ module.exports = {
118
+ extractConnectionSslParams,
119
+ connectUtility,
120
+ };
@@ -58,9 +58,11 @@ const jsldir = dirFunc('jsl', true);
58
58
  const rundir = dirFunc('run', true);
59
59
  const uploadsdir = dirFunc('uploads', true);
60
60
  const pluginsdir = dirFunc('plugins');
61
- const archivedir = dirFunc('archive', false, ['default']);
61
+ const archivedir = processArgs.runE2eTests
62
+ ? dirFunc('archive-e2etests', false, ['default'])
63
+ : dirFunc('archive', false, ['default']);
62
64
  const appdir = dirFunc('apps');
63
- const filesdir = dirFunc('files');
65
+ const filesdir = processArgs.runE2eTests ? dirFunc('files-e2etests') : dirFunc('files');
64
66
  const logsdir = dirFunc('logs', 3600 * 24 * 7);
65
67
 
66
68
  function packagedPluginsDir() {
@@ -80,6 +82,9 @@ function packagedPluginsDir() {
80
82
  if (platformInfo.isAwsUbuntuLayout) {
81
83
  return '/home/ubuntu/build/plugins';
82
84
  }
85
+ if (platformInfo.isAzureUbuntuLayout) {
86
+ return '/home/azureuser/build/plugins';
87
+ }
83
88
  if (platformInfo.isNpmDist) {
84
89
  // node_modules
85
90
  return global['PLUGINS_DIR'];
@@ -73,6 +73,7 @@ async function getPublicHardwareFingerprint() {
73
73
  region: fingerprint.region,
74
74
  isDocker: platformInfo.isDocker,
75
75
  isAwsUbuntuLayout: platformInfo.isAwsUbuntuLayout,
76
+ isAzureUbuntuLayout: platformInfo.isAzureUbuntuLayout,
76
77
  isElectron: platformInfo.isElectron,
77
78
  },
78
79
  };
@@ -15,6 +15,7 @@ const isNpmDist = !!global['IS_NPM_DIST'];
15
15
  const isDbModel = !!global['IS_DB_MODEL'];
16
16
  const isForkedApi = processArgs.isForkedApi;
17
17
  const isAwsUbuntuLayout = fs.existsSync('/home/ubuntu/build/public');
18
+ const isAzureUbuntuLayout = fs.existsSync('/home/azureuser/build/public');
18
19
 
19
20
  // function moduleAvailable(name) {
20
21
  // try {
@@ -57,6 +58,7 @@ const platformInfo = {
57
58
  allowConnectionFromEnvVariables: !!isDbModel,
58
59
  defaultKeyfile: path.join(os.homedir(), '.ssh/id_rsa'),
59
60
  isAwsUbuntuLayout,
61
+ isAzureUbuntuLayout,
60
62
  };
61
63
 
62
64
  module.exports = platformInfo;
@@ -28,9 +28,9 @@ function getPassArgs() {
28
28
  if (listenApiChild) {
29
29
  res.push('listen-api-child');
30
30
  }
31
- // if (runE2eTests) {
32
- // res.push('--run-e2e-tests');
33
- // }
31
+ if (runE2eTests) {
32
+ res.push('--run-e2e-tests');
33
+ }
34
34
  return res;
35
35
  }
36
36
 
@@ -86,7 +86,7 @@ module.exports = function useController(app, electron, route, controller) {
86
86
  detail: err.detail,
87
87
  });
88
88
  } else {
89
- res.status(500).json({ apiErrorMessage: err.message });
89
+ res.status(500).json({ apiErrorMessage: (_.isString(err) ? err : err.message) ?? 'Unknown error' });
90
90
  }
91
91
  }
92
92
  });