dbgate-api 5.5.6 → 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.6",
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.6",
30
+ "dbgate-datalib": "^5.5.7-alpha.16",
31
31
  "dbgate-query-splitter": "^4.11.2",
32
- "dbgate-sqltree": "^5.5.6",
33
- "dbgate-tools": "^5.5.6",
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.6",
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",
@@ -36,8 +36,9 @@ function authMiddleware(req, res, next) {
36
36
  '/auth/login',
37
37
  '/auth/redirect',
38
38
  '/stream',
39
- 'storage/get-connections-for-login-page',
40
- 'auth/get-providers',
39
+ '/storage/get-connections-for-login-page',
40
+ '/storage/set-admin-password',
41
+ '/auth/get-providers',
41
42
  '/connections/dblogin-web',
42
43
  '/connections/dblogin-app',
43
44
  '/connections/dblogin-auth',
@@ -69,6 +70,7 @@ function authMiddleware(req, res, next) {
69
70
  return next();
70
71
  } catch (err) {
71
72
  if (skipAuth) {
73
+ req.isInvalidToken = true;
72
74
  return next();
73
75
  }
74
76
 
@@ -89,7 +91,12 @@ module.exports = {
89
91
  const { amoid, login, password, isAdminPage } = params;
90
92
 
91
93
  if (isAdminPage) {
92
- 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) {
93
100
  return {
94
101
  accessToken: jwt.sign(
95
102
  {
@@ -60,6 +60,14 @@ module.exports = {
60
60
  const checkedLicense = storageConnectionError ? null : await checkLicense();
61
61
  const isLicenseValid = checkedLicense?.status == 'ok';
62
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
+ );
63
71
 
64
72
  return {
65
73
  runAsPortal: !!connections.portalConnections,
@@ -83,10 +91,12 @@ module.exports = {
83
91
  isBasicAuth: !!process.env.BASIC_AUTH,
84
92
  isAdminLoginForm: !!(
85
93
  process.env.STORAGE_DATABASE &&
86
- process.env.ADMIN_PASSWORD &&
87
- !process.env.BASIC_AUTH &&
88
- checkedLicense?.type == 'premium'
94
+ (process.env.ADMIN_PASSWORD || adminConfig?.adminPasswordState == 'set') &&
95
+ !process.env.BASIC_AUTH
89
96
  ),
97
+ isAdminPasswordMissing,
98
+ isInvalidToken: req.isInvalidToken,
99
+ adminPasswordState: adminConfig?.adminPasswordState,
90
100
  storageDatabase: process.env.STORAGE_DATABASE,
91
101
  logsFilePath: getLogsFilePath(),
92
102
  connectionsFilePath: path.join(datadir(), 'connections.jsonl'),
@@ -20,5 +20,10 @@ module.exports = {
20
20
 
21
21
  getStorageConnectionError() {
22
22
  return null;
23
- }
23
+ },
24
+
25
+ readConfig_meta: true,
26
+ async readConfig({ group }) {
27
+ return {};
28
+ },
24
29
  };
@@ -1,5 +1,5 @@
1
1
 
2
2
  module.exports = {
3
- version: '5.5.6',
4
- buildTime: '2024-10-17T11:26:28.099Z'
3
+ version: '5.5.7-alpha.16',
4
+ buildTime: '2024-11-01T11:41:22.229Z'
5
5
  };
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) {
@@ -1,5 +1,5 @@
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');
@@ -39,7 +39,7 @@ 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
  }
@@ -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
 
@@ -164,7 +164,6 @@ function start() {
164
164
  setInterval(async () => {
165
165
  const time = new Date().getTime();
166
166
  if (time - lastPing > 40 * 1000) {
167
-
168
167
  logger.info('Server connection not alive, exiting');
169
168
  const driver = requireEngineDriver(storedConnection);
170
169
  await driver.close(dbhan);
@@ -181,6 +180,7 @@ function start() {
181
180
  name: 'error',
182
181
  message: err.message,
183
182
  });
183
+ logger.error(extractErrorLogData(err), `Error processing message ${message?.['msgtype']}`);
184
184
  }
185
185
  });
186
186
  }
@@ -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',