dbgate-api 5.5.5 → 5.5.7-alpha.16

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 CHANGED
@@ -1,6 +1,8 @@
1
1
  DEVMODE=1
2
2
  SHELL_SCRIPTING=1
3
3
 
4
+ CLOUD_UPGRADE_FILE=c:\test\upg\upgrade.zip
5
+
4
6
  # PERMISSIONS=~widgets/app,~widgets/plugins
5
7
  # DISABLE_SHELL=1
6
8
  # HIDE_APP_EDITOR=1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dbgate-api",
3
3
  "main": "src/index.js",
4
- "version": "5.5.5",
4
+ "version": "5.5.7-alpha.16",
5
5
  "homepage": "https://dbgate.org/",
6
6
  "repository": {
7
7
  "type": "git",
@@ -27,10 +27,10 @@
27
27
  "compare-versions": "^3.6.0",
28
28
  "cors": "^2.8.5",
29
29
  "cross-env": "^6.0.3",
30
- "dbgate-datalib": "^5.5.5",
30
+ "dbgate-datalib": "^5.5.7-alpha.16",
31
31
  "dbgate-query-splitter": "^4.11.2",
32
- "dbgate-sqltree": "^5.5.5",
33
- "dbgate-tools": "^5.5.5",
32
+ "dbgate-sqltree": "^5.5.7-alpha.16",
33
+ "dbgate-tools": "^5.5.7-alpha.16",
34
34
  "debug": "^4.3.4",
35
35
  "diff": "^5.0.0",
36
36
  "diff2html": "^3.4.13",
@@ -56,6 +56,7 @@
56
56
  "pinomin": "^1.0.4",
57
57
  "portfinder": "^1.0.28",
58
58
  "rimraf": "^3.0.0",
59
+ "semver": "^7.6.3",
59
60
  "simple-encryptor": "^4.0.0",
60
61
  "ssh2": "^1.11.0",
61
62
  "stream-json": "^1.8.0",
@@ -77,7 +78,7 @@
77
78
  "devDependencies": {
78
79
  "@types/fs-extra": "^9.0.11",
79
80
  "@types/lodash": "^4.14.149",
80
- "dbgate-types": "^5.5.5",
81
+ "dbgate-types": "^5.5.7-alpha.16",
81
82
  "env-cmd": "^10.1.0",
82
83
  "node-loader": "^1.0.2",
83
84
  "nodemon": "^2.0.2",
@@ -22,7 +22,8 @@ function unauthorizedResponse(req, res, text) {
22
22
  // if (req.path == getExpressPath('/connections/list')) {
23
23
  // return res.json([]);
24
24
  // }
25
- return res.sendStatus(401).send(text);
25
+
26
+ return res.status(401).send(text);
26
27
  }
27
28
 
28
29
  function authMiddleware(req, res, next) {
@@ -35,8 +36,9 @@ function authMiddleware(req, res, next) {
35
36
  '/auth/login',
36
37
  '/auth/redirect',
37
38
  '/stream',
38
- 'storage/get-connections-for-login-page',
39
- 'auth/get-providers',
39
+ '/storage/get-connections-for-login-page',
40
+ '/storage/set-admin-password',
41
+ '/auth/get-providers',
40
42
  '/connections/dblogin-web',
41
43
  '/connections/dblogin-app',
42
44
  '/connections/dblogin-auth',
@@ -68,6 +70,7 @@ function authMiddleware(req, res, next) {
68
70
  return next();
69
71
  } catch (err) {
70
72
  if (skipAuth) {
73
+ req.isInvalidToken = true;
71
74
  return next();
72
75
  }
73
76
 
@@ -88,7 +91,12 @@ module.exports = {
88
91
  const { amoid, login, password, isAdminPage } = params;
89
92
 
90
93
  if (isAdminPage) {
91
- if (process.env.ADMIN_PASSWORD && process.env.ADMIN_PASSWORD == password) {
94
+ let adminPassword = process.env.ADMIN_PASSWORD;
95
+ if (!adminPassword) {
96
+ const adminConfig = await storage.readConfig({ group: 'admin' });
97
+ adminPassword = adminConfig?.adminPassword;
98
+ }
99
+ if (adminPassword && adminPassword == password) {
92
100
  return {
93
101
  accessToken: jwt.sign(
94
102
  {
@@ -17,6 +17,7 @@ const { checkLicense, checkLicenseKey } = require('../utility/checkLicense');
17
17
  const storage = require('./storage');
18
18
  const { getAuthProxyUrl } = require('../utility/authProxy');
19
19
  const { getPublicHardwareFingerprint } = require('../utility/hardwareFingerprint');
20
+ const { extractErrorMessage } = require('dbgate-tools');
20
21
 
21
22
  const lock = new AsyncLock();
22
23
 
@@ -39,10 +40,12 @@ module.exports = {
39
40
  const isUserLoggedIn = authProvider.isUserLoggedIn(req);
40
41
 
41
42
  const singleConid = authProvider.getSingleConnectionId(req);
43
+ const storageConnectionError = storage.getStorageConnectionError();
42
44
 
43
- const singleConnection = singleConid
44
- ? await connections.getCore({ conid: singleConid })
45
- : connections.singleConnection;
45
+ const singleConnection =
46
+ singleConid && !storageConnectionError
47
+ ? await connections.getCore({ conid: singleConid })
48
+ : connections.singleConnection;
46
49
 
47
50
  let configurationError = null;
48
51
  if (process.env.STORAGE_DATABASE && process.env.BASIC_AUTH) {
@@ -50,8 +53,21 @@ module.exports = {
50
53
  'Basic authentization is not allowed, when using storage. Cannot use both STORAGE_DATABASE and BASIC_AUTH';
51
54
  }
52
55
 
53
- const checkedLicense = await checkLicense();
56
+ if (storageConnectionError && !configurationError) {
57
+ configurationError = extractErrorMessage(storageConnectionError);
58
+ }
59
+
60
+ const checkedLicense = storageConnectionError ? null : await checkLicense();
54
61
  const isLicenseValid = checkedLicense?.status == 'ok';
62
+ const logoutUrl = storageConnectionError ? null : await authProvider.getLogoutUrl();
63
+ const adminConfig = storageConnectionError ? null : await storage.readConfig({ group: 'admin' });
64
+
65
+ const isAdminPasswordMissing = !!(
66
+ process.env.STORAGE_DATABASE &&
67
+ !process.env.ADMIN_PASSWORD &&
68
+ !process.env.BASIC_AUTH &&
69
+ !adminConfig?.adminPasswordState
70
+ );
55
71
 
56
72
  return {
57
73
  runAsPortal: !!connections.portalConnections,
@@ -68,17 +84,19 @@ module.exports = {
68
84
  trialDaysLeft: checkedLicense?.isGeneratedTrial && !checkedLicense?.isExpired ? checkedLicense?.daysLeft : null,
69
85
  checkedLicense,
70
86
  configurationError,
71
- logoutUrl: await authProvider.getLogoutUrl(),
87
+ logoutUrl,
72
88
  permissions,
73
89
  login,
74
90
  // ...additionalConfigProps,
75
91
  isBasicAuth: !!process.env.BASIC_AUTH,
76
92
  isAdminLoginForm: !!(
77
93
  process.env.STORAGE_DATABASE &&
78
- process.env.ADMIN_PASSWORD &&
79
- !process.env.BASIC_AUTH &&
80
- checkedLicense?.type == 'premium'
94
+ (process.env.ADMIN_PASSWORD || adminConfig?.adminPasswordState == 'set') &&
95
+ !process.env.BASIC_AUTH
81
96
  ),
97
+ isAdminPasswordMissing,
98
+ isInvalidToken: req.isInvalidToken,
99
+ adminPasswordState: adminConfig?.adminPasswordState,
82
100
  storageDatabase: process.env.STORAGE_DATABASE,
83
101
  logsFilePath: getLogsFilePath(),
84
102
  connectionsFilePath: path.join(datadir(), 'connections.jsonl'),
@@ -17,4 +17,13 @@ module.exports = {
17
17
  async getConnectionsForLoginPage() {
18
18
  return null;
19
19
  },
20
+
21
+ getStorageConnectionError() {
22
+ return null;
23
+ },
24
+
25
+ readConfig_meta: true,
26
+ async readConfig({ group }) {
27
+ return {};
28
+ },
20
29
  };
@@ -1,5 +1,5 @@
1
1
 
2
2
  module.exports = {
3
- version: '5.5.5',
4
- buildTime: '2024-10-11T08:23:34.200Z'
3
+ version: '5.5.7-alpha.16',
4
+ buildTime: '2024-11-01T11:41:22.229Z'
5
5
  };
package/src/index.js CHANGED
@@ -4,11 +4,13 @@ const fs = require('fs');
4
4
  const moment = require('moment');
5
5
  const path = require('path');
6
6
  const { logsdir, setLogsFilePath, getLogsFilePath } = require('./utility/directories');
7
+ const currentVersion = require('./currentVersion');
7
8
 
8
9
  const logger = getLogger('apiIndex');
9
10
 
10
11
  process.on('uncaughtException', err => {
11
- logger.fatal(extractErrorLogData(err), 'Uncaught exception');
12
+ logger.fatal(extractErrorLogData(err), 'Uncaught exception, exiting process');
13
+ process.exit(1);
12
14
  });
13
15
 
14
16
  if (processArgs.startProcess) {
@@ -99,10 +101,17 @@ function configureLogger() {
99
101
 
100
102
  if (processArgs.listenApi) {
101
103
  configureLogger();
104
+ logger.info(`Starting API process version ${currentVersion.version}`);
105
+
106
+ if (process.env.DEBUG_PRINT_ENV_VARIABLES) {
107
+ logger.info('Debug print environment variables:');
108
+ for (const key of Object.keys(process.env)) {
109
+ logger.info(` ${key}: ${JSON.stringify(process.env[key])}`);
110
+ }
111
+ }
102
112
  }
103
113
 
104
114
  const shell = require('./shell/index');
105
- const currentVersion = require('./currentVersion');
106
115
 
107
116
  global.DBGATE_PACKAGES = {
108
117
  'dbgate-tools': require('dbgate-tools'),
package/src/main.js CHANGED
@@ -35,6 +35,7 @@ const getExpressPath = require('./utility/getExpressPath');
35
35
  const _ = require('lodash');
36
36
  const { getLogger } = require('dbgate-tools');
37
37
  const { getDefaultAuthProvider } = require('./auth/authProvider');
38
+ const startCloudUpgradeTimer = require('./utility/cloudUpgrade');
38
39
 
39
40
  const logger = getLogger('main');
40
41
 
@@ -73,6 +74,8 @@ function start() {
73
74
  if (platformInfo.isDocker) {
74
75
  // server static files inside docker container
75
76
  app.use(getExpressPath('/'), express.static('/home/dbgate-docker/public'));
77
+ } else if (platformInfo.isAwsUbuntuLayout) {
78
+ app.use(getExpressPath('/'), express.static('/home/ubuntu/build/public'));
76
79
  } else if (platformInfo.isNpmDist) {
77
80
  app.use(getExpressPath('/'), express.static(path.join(__dirname, '../../dbgate-web/public')));
78
81
  } else if (process.env.DEVWEB) {
@@ -126,6 +129,10 @@ function start() {
126
129
  const port = process.env.PORT || 3000;
127
130
  logger.info(`DbGate API listening on port ${port} (docker build)`);
128
131
  server.listen(port);
132
+ } else if (platformInfo.isAwsUbuntuLayout) {
133
+ const port = process.env.PORT || 3000;
134
+ logger.info(`DbGate API listening on port ${port} (AWS AMI build)`);
135
+ server.listen(port);
129
136
  } else if (platformInfo.isNpmDist) {
130
137
  getPort({
131
138
  port: parseInt(
@@ -162,6 +169,10 @@ function start() {
162
169
  process.on('SIGINT', shutdown);
163
170
  process.on('SIGTERM', shutdown);
164
171
  process.on('SIGBREAK', shutdown);
172
+
173
+ if (process.env.CLOUD_UPGRADE_FILE) {
174
+ startCloudUpgradeTimer();
175
+ }
165
176
  }
166
177
 
167
178
  function useAllControllers(app, electron) {
@@ -20,9 +20,10 @@ function start() {
20
20
  if (handleProcessCommunication(connection)) return;
21
21
  try {
22
22
  const driver = requireEngineDriver(connection);
23
- const conn = await connectUtility(driver, connection, 'app');
24
- const res = await driver.getVersion(conn);
23
+ const dbhan = await connectUtility(driver, connection, 'app');
24
+ const res = await driver.getVersion(dbhan);
25
25
  process.send({ msgtype: 'connected', ...res });
26
+ await driver.close(dbhan);
26
27
  } catch (e) {
27
28
  console.error(e);
28
29
  process.send({
@@ -329,8 +329,9 @@ async function handleSqlPreview({ msgid, objects, options }) {
329
329
  await generator.dump();
330
330
  process.send({ msgtype: 'response', msgid, sql: dmp.s, isTruncated: generator.isTruncated });
331
331
  if (generator.isUnhandledException) {
332
- setTimeout(() => {
332
+ setTimeout(async () => {
333
333
  logger.error('Exiting because of unhandled exception');
334
+ await driver.close(dbhan);
334
335
  process.exit(0);
335
336
  }, 500);
336
337
  }
@@ -406,10 +407,12 @@ async function handleMessage({ msgtype, ...other }) {
406
407
  function start() {
407
408
  childProcessChecker();
408
409
 
409
- setInterval(() => {
410
+ setInterval(async () => {
410
411
  const time = new Date().getTime();
411
412
  if (time - lastPing > 40 * 1000) {
412
413
  logger.info('Database connection not alive, exiting');
414
+ const driver = requireEngineDriver(storedConnection);
415
+ await driver.close(dbhan);
413
416
  process.exit(0);
414
417
  }
415
418
  }, 10 * 1000);
@@ -1,12 +1,12 @@
1
1
  const stableStringify = require('json-stable-stringify');
2
- const { extractBoolSettingsValue, extractIntSettingsValue, getLogger } = require('dbgate-tools');
2
+ const { extractBoolSettingsValue, extractIntSettingsValue, getLogger, extractErrorLogData } = require('dbgate-tools');
3
3
  const childProcessChecker = require('../utility/childProcessChecker');
4
4
  const requireEngineDriver = require('../utility/requireEngineDriver');
5
5
  const connectUtility = require('../utility/connectUtility');
6
6
  const { handleProcessCommunication } = require('../utility/processComm');
7
7
  const logger = getLogger('srvconnProcess');
8
8
 
9
- let systemConnection;
9
+ let dbhan;
10
10
  let storedConnection;
11
11
  let lastDatabases = null;
12
12
  let lastStatus = null;
@@ -16,7 +16,7 @@ let afterConnectCallbacks = [];
16
16
  async function handleRefresh() {
17
17
  const driver = requireEngineDriver(storedConnection);
18
18
  try {
19
- let databases = await driver.listDatabases(systemConnection);
19
+ let databases = await driver.listDatabases(dbhan);
20
20
  if (storedConnection?.allowedDatabases?.trim()) {
21
21
  const allowedDatabaseList = storedConnection.allowedDatabases
22
22
  .split('\n')
@@ -39,14 +39,14 @@ async function handleRefresh() {
39
39
  name: 'error',
40
40
  message: err.message,
41
41
  });
42
- // console.error(err);
42
+ logger.error(extractErrorLogData(err), 'Error refreshing server databases');
43
43
  setTimeout(() => process.exit(1), 1000);
44
44
  }
45
45
  }
46
46
 
47
47
  async function readVersion() {
48
48
  const driver = requireEngineDriver(storedConnection);
49
- const version = await driver.getVersion(systemConnection);
49
+ const version = await driver.getVersion(dbhan);
50
50
  process.send({ msgtype: 'version', version });
51
51
  }
52
52
 
@@ -70,7 +70,7 @@ async function handleConnect(connection) {
70
70
 
71
71
  const driver = requireEngineDriver(storedConnection);
72
72
  try {
73
- systemConnection = await connectUtility(driver, storedConnection, 'app');
73
+ dbhan = await connectUtility(driver, storedConnection, 'app');
74
74
  readVersion();
75
75
  handleRefresh();
76
76
  if (extractBoolSettingsValue(globalSettings, 'connection.autoRefresh', false)) {
@@ -84,7 +84,7 @@ async function handleConnect(connection) {
84
84
  name: 'error',
85
85
  message: err.message,
86
86
  });
87
- // console.error(err);
87
+ logger.error(extractErrorLogData(err), 'Error connecting to server');
88
88
  setTimeout(() => process.exit(1), 1000);
89
89
  }
90
90
 
@@ -95,7 +95,7 @@ async function handleConnect(connection) {
95
95
  }
96
96
 
97
97
  function waitConnected() {
98
- if (systemConnection) return Promise.resolve();
98
+ if (dbhan) return Promise.resolve();
99
99
  return new Promise((resolve, reject) => {
100
100
  afterConnectCallbacks.push([resolve, reject]);
101
101
  });
@@ -108,14 +108,14 @@ function handlePing() {
108
108
  async function handleDatabaseOp(op, { msgid, name }) {
109
109
  try {
110
110
  const driver = requireEngineDriver(storedConnection);
111
- systemConnection = await connectUtility(driver, storedConnection, 'app');
111
+ dbhan = await connectUtility(driver, storedConnection, 'app');
112
112
  if (driver[op]) {
113
- await driver[op](systemConnection, name);
113
+ await driver[op](dbhan, name);
114
114
  } else {
115
115
  const dmp = driver.createDumper();
116
116
  dmp[op](name);
117
117
  logger.info({ sql: dmp.s }, 'Running script');
118
- await driver.query(systemConnection, dmp.s, { discardResult: true });
118
+ await driver.query(dbhan, dmp.s, { discardResult: true });
119
119
  }
120
120
  await handleRefresh();
121
121
 
@@ -137,11 +137,11 @@ async function handleDriverDataCore(msgid, callMethod) {
137
137
  }
138
138
 
139
139
  async function handleServerSummary({ msgid }) {
140
- return handleDriverDataCore(msgid, driver => driver.serverSummary(systemConnection));
140
+ return handleDriverDataCore(msgid, driver => driver.serverSummary(dbhan));
141
141
  }
142
142
 
143
143
  async function handleSummaryCommand({ msgid, command, row }) {
144
- return handleDriverDataCore(msgid, driver => driver.summaryCommand(systemConnection, command, row));
144
+ return handleDriverDataCore(msgid, driver => driver.summaryCommand(dbhan, command, row));
145
145
  }
146
146
 
147
147
  const messageHandlers = {
@@ -161,10 +161,12 @@ async function handleMessage({ msgtype, ...other }) {
161
161
  function start() {
162
162
  childProcessChecker();
163
163
 
164
- setInterval(() => {
164
+ setInterval(async () => {
165
165
  const time = new Date().getTime();
166
166
  if (time - lastPing > 40 * 1000) {
167
167
  logger.info('Server connection not alive, exiting');
168
+ const driver = requireEngineDriver(storedConnection);
169
+ await driver.close(dbhan);
168
170
  process.exit(0);
169
171
  }
170
172
  }, 10 * 1000);
@@ -178,6 +180,7 @@ function start() {
178
180
  name: 'error',
179
181
  message: err.message,
180
182
  });
183
+ logger.error(extractErrorLogData(err), `Error processing message ${message?.['msgtype']}`);
181
184
  }
182
185
  });
183
186
  }
@@ -14,7 +14,7 @@ const { getLogger, extractIntSettingsValue, extractBoolSettingsValue } = require
14
14
 
15
15
  const logger = getLogger('sessionProcess');
16
16
 
17
- let systemConnection;
17
+ let dbhan;
18
18
  let storedConnection;
19
19
  let afterConnectCallbacks = [];
20
20
  // let currentHandlers = [];
@@ -177,7 +177,7 @@ function handleStream(driver, resultIndexHolder, sqlItem) {
177
177
  return new Promise((resolve, reject) => {
178
178
  const start = sqlItem.trimStart || sqlItem.start;
179
179
  const handler = new StreamHandler(resultIndexHolder, resolve, start && start.line);
180
- driver.stream(systemConnection, sqlItem.text, handler);
180
+ driver.stream(dbhan, sqlItem.text, handler);
181
181
  });
182
182
  }
183
183
 
@@ -196,7 +196,7 @@ async function handleConnect(connection) {
196
196
  storedConnection = connection;
197
197
 
198
198
  const driver = requireEngineDriver(storedConnection);
199
- systemConnection = await connectUtility(driver, storedConnection, 'app');
199
+ dbhan = await connectUtility(driver, storedConnection, 'app');
200
200
  for (const [resolve] of afterConnectCallbacks) {
201
201
  resolve();
202
202
  }
@@ -210,7 +210,7 @@ async function handleConnect(connection) {
210
210
  // }
211
211
 
212
212
  function waitConnected() {
213
- if (systemConnection) return Promise.resolve();
213
+ if (dbhan) return Promise.resolve();
214
214
  return new Promise((resolve, reject) => {
215
215
  afterConnectCallbacks.push([resolve, reject]);
216
216
  });
@@ -230,7 +230,7 @@ async function handleStartProfiler({ jslid }) {
230
230
  const writer = new TableWriter();
231
231
  writer.initializeFromReader(jslid);
232
232
 
233
- currentProfiler = await driver.startProfiler(systemConnection, {
233
+ currentProfiler = await driver.startProfiler(dbhan, {
234
234
  row: data => writer.rowFromReader(data),
235
235
  });
236
236
  currentProfiler.writer = writer;
@@ -241,7 +241,7 @@ async function handleStopProfiler({ jslid }) {
241
241
 
242
242
  const driver = requireEngineDriver(storedConnection);
243
243
  currentProfiler.writer.close();
244
- driver.stopProfiler(systemConnection, currentProfiler);
244
+ driver.stopProfiler(dbhan, currentProfiler);
245
245
  currentProfiler = null;
246
246
  }
247
247
 
@@ -304,7 +304,7 @@ async function handleExecuteReader({ jslid, sql, fileName }) {
304
304
  const writer = new TableWriter();
305
305
  writer.initializeFromReader(jslid);
306
306
 
307
- const reader = await driver.readQuery(systemConnection, sql);
307
+ const reader = await driver.readQuery(dbhan, sql);
308
308
 
309
309
  reader.on('data', data => {
310
310
  writer.rowFromReader(data);
@@ -340,10 +340,12 @@ function start() {
340
340
 
341
341
  lastPing = new Date().getTime();
342
342
 
343
- setInterval(() => {
343
+ setInterval(async () => {
344
344
  const time = new Date().getTime();
345
345
  if (time - lastPing > 25 * 1000) {
346
346
  logger.info('Session not alive, exiting');
347
+ const driver = requireEngineDriver(storedConnection);
348
+ await driver.close(dbhan);
347
349
  process.exit(0);
348
350
  }
349
351
 
@@ -362,6 +364,8 @@ function start() {
362
364
  executingScripts == 0
363
365
  ) {
364
366
  logger.info('Session not active, exiting');
367
+ const driver = requireEngineDriver(storedConnection);
368
+ await driver.close(dbhan);
365
369
  process.exit(0);
366
370
  }
367
371
  }, 10 * 1000);
@@ -3,7 +3,7 @@ const platformInfo = require('../utility/platformInfo');
3
3
  const childProcessChecker = require('../utility/childProcessChecker');
4
4
  const { handleProcessCommunication } = require('../utility/processComm');
5
5
  const { SSHConnection } = require('../utility/SSHConnection');
6
- const { getLogger, extractErrorLogData } = require('dbgate-tools');
6
+ const { getLogger, extractErrorLogData, extractErrorMessage } = require('dbgate-tools');
7
7
 
8
8
  const logger = getLogger('sshProcess');
9
9
 
@@ -46,7 +46,7 @@ async function handleStart({ connection, tunnelConfig }) {
46
46
  msgtype: 'error',
47
47
  connection,
48
48
  tunnelConfig,
49
- errorMessage: err.message,
49
+ errorMessage: extractErrorMessage(err.message),
50
50
  });
51
51
  }
52
52
  }
@@ -0,0 +1,19 @@
1
+ const autoIndexForeignKeysTransform = () => database => {
2
+ return {
3
+ ...database,
4
+ tables: database.tables.map(table => {
5
+ return {
6
+ ...table,
7
+ indexes: [
8
+ ...(table.indexes || []),
9
+ ...table.foreignKeys.map(fk => ({
10
+ constraintName: `IX_${fk.constraintName}`,
11
+ columns: fk.columns,
12
+ })),
13
+ ],
14
+ };
15
+ }),
16
+ };
17
+ };
18
+
19
+ module.exports = autoIndexForeignKeysTransform;
@@ -0,0 +1,21 @@
1
+ const dataTypeMapperTransform = (oldType, newType) => database => {
2
+ return {
3
+ ...database,
4
+ tables: database.tables.map(table => {
5
+ return {
6
+ ...table,
7
+ columns: table.columns.map(column => {
8
+ if (column.dataType?.toLowerCase() === oldType?.toLowerCase()) {
9
+ return {
10
+ ...column,
11
+ dataType: newType,
12
+ };
13
+ }
14
+ return column;
15
+ }),
16
+ };
17
+ }),
18
+ };
19
+ };
20
+
21
+ module.exports = dataTypeMapperTransform;
@@ -1,7 +1,16 @@
1
1
  const generateDeploySql = require('./generateDeploySql');
2
2
  const executeQuery = require('./executeQuery');
3
3
 
4
- async function deployDb({ connection, systemConnection, driver, analysedStructure, modelFolder, loadedDbModel }) {
4
+ async function deployDb({
5
+ connection,
6
+ systemConnection,
7
+ driver,
8
+ analysedStructure,
9
+ modelFolder,
10
+ loadedDbModel,
11
+ modelTransforms,
12
+ dbdiffOptionsExtra,
13
+ }) {
5
14
  const { sql } = await generateDeploySql({
6
15
  connection,
7
16
  systemConnection,
@@ -9,9 +18,11 @@ async function deployDb({ connection, systemConnection, driver, analysedStructur
9
18
  analysedStructure,
10
19
  modelFolder,
11
20
  loadedDbModel,
21
+ modelTransforms,
22
+ dbdiffOptionsExtra,
12
23
  });
13
24
  // console.log('RUNNING DEPLOY SCRIPT:', sql);
14
- await executeQuery({ connection, systemConnection, driver, sql });
25
+ await executeQuery({ connection, systemConnection, driver, sql, logScriptItems: true });
15
26
  }
16
27
 
17
28
  module.exports = deployDb;
@@ -1,11 +1,19 @@
1
1
  const requireEngineDriver = require('../utility/requireEngineDriver');
2
2
  const connectUtility = require('../utility/connectUtility');
3
- const { getLogger } = require('dbgate-tools');
3
+ const { getLogger, getLimitedQuery } = require('dbgate-tools');
4
4
 
5
5
  const logger = getLogger('execQuery');
6
6
 
7
- async function executeQuery({ connection = undefined, systemConnection = undefined, driver = undefined, sql }) {
8
- logger.info({ sql }, `Execute query`);
7
+ async function executeQuery({
8
+ connection = undefined,
9
+ systemConnection = undefined,
10
+ driver = undefined,
11
+ sql,
12
+ logScriptItems = false,
13
+ }) {
14
+ if (!logScriptItems) {
15
+ logger.info({ sql: getLimitedQuery(sql) }, `Execute query`);
16
+ }
9
17
 
10
18
  if (!driver) driver = requireEngineDriver(connection);
11
19
  const dbhan = systemConnection || (await connectUtility(driver, connection, 'script'));
@@ -13,7 +21,7 @@ async function executeQuery({ connection = undefined, systemConnection = undefin
13
21
  try {
14
22
  logger.info(`Connected.`);
15
23
 
16
- await driver.script(dbhan, sql);
24
+ await driver.script(dbhan, sql, { logScriptItems });
17
25
  } finally {
18
26
  if (!systemConnection) {
19
27
  await driver.close(dbhan);
@@ -18,6 +18,8 @@ async function generateDeploySql({
18
18
  analysedStructure = undefined,
19
19
  modelFolder = undefined,
20
20
  loadedDbModel = undefined,
21
+ modelTransforms = undefined,
22
+ dbdiffOptionsExtra = {},
21
23
  }) {
22
24
  if (!driver) driver = requireEngineDriver(connection);
23
25
 
@@ -28,9 +30,15 @@ async function generateDeploySql({
28
30
  analysedStructure = await driver.analyseFull(dbhan);
29
31
  }
30
32
 
31
- const deployedModel = generateDbPairingId(
32
- extendDatabaseInfo(loadedDbModel ? databaseInfoFromYamlModel(loadedDbModel) : await importDbModel(modelFolder))
33
- );
33
+ let deployedModelSource = loadedDbModel
34
+ ? databaseInfoFromYamlModel(loadedDbModel)
35
+ : await importDbModel(modelFolder);
36
+
37
+ for (const transform of modelTransforms || []) {
38
+ deployedModelSource = transform(deployedModelSource);
39
+ }
40
+
41
+ const deployedModel = generateDbPairingId(extendDatabaseInfo(deployedModelSource));
34
42
  const currentModel = generateDbPairingId(extendDatabaseInfo(analysedStructure));
35
43
  const opts = {
36
44
  ...modelCompareDbDiffOptions,
@@ -41,6 +49,8 @@ async function generateDeploySql({
41
49
  noDropSqlObject: true,
42
50
  noRenameTable: true,
43
51
  noRenameColumn: true,
52
+
53
+ ...dbdiffOptionsExtra,
44
54
  };
45
55
  const currentModelPaired = matchPairedObjects(deployedModel, currentModel, opts);
46
56
  const currentModelPairedPreloaded = await enrichWithPreloadedRows(deployedModel, currentModelPaired, dbhan, driver);
@@ -57,7 +67,7 @@ async function generateDeploySql({
57
67
  deployedModel,
58
68
  driver
59
69
  );
60
-
70
+
61
71
  return res;
62
72
  } finally {
63
73
  if (!systemConnection) {
@@ -30,6 +30,10 @@ const dataDuplicator = require('./dataDuplicator');
30
30
  const dbModelToJson = require('./dbModelToJson');
31
31
  const jsonToDbModel = require('./jsonToDbModel');
32
32
  const jsonReader = require('./jsonReader');
33
+ const dataTypeMapperTransform = require('./dataTypeMapperTransform');
34
+ const sqlTextReplacementTransform = require('./sqlTextReplacementTransform');
35
+ const autoIndexForeignKeysTransform = require('./autoIndexForeignKeysTransform');
36
+ const generateDeploySql = require('./generateDeploySql');
33
37
 
34
38
  const dbgateApi = {
35
39
  queryReader,
@@ -63,6 +67,10 @@ const dbgateApi = {
63
67
  dataDuplicator,
64
68
  dbModelToJson,
65
69
  jsonToDbModel,
70
+ dataTypeMapperTransform,
71
+ sqlTextReplacementTransform,
72
+ autoIndexForeignKeysTransform,
73
+ generateDeploySql,
66
74
  };
67
75
 
68
76
  requirePlugin.initializeDbgateApi(dbgateApi);
@@ -0,0 +1,32 @@
1
+ function replaceInText(text, replacements) {
2
+ let result = text;
3
+ for (const key of Object.keys(replacements)) {
4
+ result = result.split(key).join(replacements[key]);
5
+ }
6
+ return result;
7
+ }
8
+
9
+ function replaceInCollection(collection, replacements) {
10
+ if (!collection) return collection;
11
+ return collection.map(item => {
12
+ if (item.createSql) {
13
+ return {
14
+ ...item,
15
+ createSql: replaceInText(item.createSql, replacements),
16
+ };
17
+ }
18
+ return item;
19
+ });
20
+ }
21
+
22
+ const sqlTextReplacementTransform = replacements => database => {
23
+ return {
24
+ ...database,
25
+ views: replaceInCollection(database.views, replacements),
26
+ matviews: replaceInCollection(database.matviews, replacements),
27
+ procedures: replaceInCollection(database.procedures, replacements),
28
+ functions: replaceInCollection(database.functions, replacements),
29
+ };
30
+ };
31
+
32
+ module.exports = sqlTextReplacementTransform;
@@ -0,0 +1,61 @@
1
+ const axios = require('axios');
2
+ const fs = require('fs');
3
+ const fsp = require('fs/promises');
4
+ const semver = require('semver');
5
+ const currentVersion = require('../currentVersion');
6
+ const { getLogger, extractErrorLogData } = require('dbgate-tools');
7
+
8
+ const logger = getLogger('cloudUpgrade');
9
+
10
+ async function checkCloudUpgrade() {
11
+ try {
12
+ const resp = await axios.default.get('https://api.github.com/repos/dbgate/dbgate/releases/latest');
13
+ const json = resp.data;
14
+ const version = json.name.substring(1);
15
+ let cloudDownloadedVersion = null;
16
+ try {
17
+ cloudDownloadedVersion = await fsp.readFile(process.env.CLOUD_UPGRADE_FILE + '.version', 'utf-8');
18
+ } catch (err) {
19
+ cloudDownloadedVersion = null;
20
+ }
21
+ if (
22
+ semver.gt(version, currentVersion.version) &&
23
+ (!cloudDownloadedVersion || semver.gt(version, cloudDownloadedVersion))
24
+ ) {
25
+ logger.info(`New version available: ${version}`);
26
+ const zipUrl = json.assets.find(x => x.name == 'cloud-build.zip').browser_download_url;
27
+
28
+ const writer = fs.createWriteStream(process.env.CLOUD_UPGRADE_FILE);
29
+
30
+ const response = await axios.default({
31
+ url: zipUrl,
32
+ method: 'GET',
33
+ responseType: 'stream',
34
+ });
35
+
36
+ response.data.pipe(writer);
37
+
38
+ await new Promise((resolve, reject) => {
39
+ writer.on('finish', resolve);
40
+ writer.on('error', reject);
41
+ });
42
+ await fsp.writeFile(process.env.CLOUD_UPGRADE_FILE + '.version', version);
43
+
44
+ logger.info(`Downloaded new version from ${zipUrl}`);
45
+ } else {
46
+ logger.info(`Checked version ${version} is not newer than ${cloudDownloadedVersion ?? currentVersion.version}, upgrade skippped`);
47
+ }
48
+ } catch (err) {
49
+ logger.error(extractErrorLogData(err), 'Error checking cloud upgrade');
50
+ }
51
+ }
52
+
53
+ function startCloudUpgradeTimer() {
54
+ // at first in 5 seconds
55
+ setTimeout(checkCloudUpgrade, 5000);
56
+
57
+ // hourly
58
+ setInterval(checkCloudUpgrade, 60 * 60 * 1000);
59
+ }
60
+
61
+ module.exports = startCloudUpgradeTimer;
@@ -77,6 +77,9 @@ function packagedPluginsDir() {
77
77
  if (platformInfo.isDocker) {
78
78
  return '/home/dbgate-docker/plugins';
79
79
  }
80
+ if (platformInfo.isAwsUbuntuLayout) {
81
+ return '/home/ubuntu/build/plugins';
82
+ }
80
83
  if (platformInfo.isNpmDist) {
81
84
  // node_modules
82
85
  return global['PLUGINS_DIR'];
@@ -72,6 +72,7 @@ async function getPublicHardwareFingerprint() {
72
72
  country: fingerprint.country,
73
73
  region: fingerprint.region,
74
74
  isDocker: platformInfo.isDocker,
75
+ isAwsUbuntuLayout: platformInfo.isAwsUbuntuLayout,
75
76
  isElectron: platformInfo.isElectron,
76
77
  },
77
78
  };
@@ -14,6 +14,7 @@ const isBuiltWebMode = process.env.BUILTWEBMODE == '1';
14
14
  const isNpmDist = !!global['IS_NPM_DIST'];
15
15
  const isDbModel = !!global['IS_DB_MODEL'];
16
16
  const isForkedApi = processArgs.isForkedApi;
17
+ const isAwsUbuntuLayout = fs.existsSync('/home/ubuntu/build/public');
17
18
 
18
19
  // function moduleAvailable(name) {
19
20
  // try {
@@ -47,6 +48,7 @@ const platformInfo = {
47
48
  (!processArgs.listenApiChild && !isNpmDist) || !!process.env.SHELL_SCRIPTING || !!isElectron() || !!isDbModel,
48
49
  allowConnectionFromEnvVariables: !!isDbModel,
49
50
  defaultKeyfile: path.join(os.homedir(), '.ssh/id_rsa'),
51
+ isAwsUbuntuLayout,
50
52
  };
51
53
 
52
54
  module.exports = platformInfo;
@@ -50,7 +50,7 @@ function callForwardProcess(connection, tunnelConfig, tunnelCacheKey) {
50
50
  resolve(subprocess);
51
51
  }
52
52
  if (msgtype == 'error') {
53
- reject(errorMessage);
53
+ reject(new Error(errorMessage));
54
54
  }
55
55
  });
56
56
  subprocess.on('exit', code => {
@@ -91,6 +91,7 @@ async function getSshTunnel(connection) {
91
91
  };
92
92
  return sshTunnelCache[tunnelCacheKey];
93
93
  } catch (err) {
94
+ logger.error(extractErrorLogData(err), 'Error creating SSH tunnel:');
94
95
  // error is not cached
95
96
  return {
96
97
  state: 'error',