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 +2 -0
- package/package.json +6 -5
- package/src/controllers/auth.js +12 -4
- package/src/controllers/config.js +26 -8
- package/src/controllers/storage.js +9 -0
- package/src/currentVersion.js +2 -2
- package/src/index.js +11 -2
- package/src/main.js +11 -0
- package/src/proc/connectProcess.js +3 -2
- package/src/proc/databaseConnectionProcess.js +5 -2
- package/src/proc/serverConnectionProcess.js +17 -14
- package/src/proc/sessionProcess.js +12 -8
- package/src/proc/sshForwardProcess.js +2 -2
- package/src/shell/autoIndexForeignKeysTransform.js +19 -0
- package/src/shell/dataTypeMapperTransform.js +21 -0
- package/src/shell/deployDb.js +13 -2
- package/src/shell/executeQuery.js +12 -4
- package/src/shell/generateDeploySql.js +14 -4
- package/src/shell/index.js +8 -0
- package/src/shell/sqlTextReplacementTransform.js +32 -0
- package/src/utility/cloudUpgrade.js +61 -0
- package/src/utility/directories.js +3 -0
- package/src/utility/hardwareFingerprint.js +1 -0
- package/src/utility/platformInfo.js +2 -0
- package/src/utility/sshTunnel.js +2 -1
package/.env
CHANGED
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.
|
|
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.
|
|
30
|
+
"dbgate-datalib": "^5.5.7-alpha.16",
|
|
31
31
|
"dbgate-query-splitter": "^4.11.2",
|
|
32
|
-
"dbgate-sqltree": "^5.5.
|
|
33
|
-
"dbgate-tools": "^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.
|
|
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",
|
package/src/controllers/auth.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
'
|
|
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
|
-
|
|
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 =
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
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
|
|
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'),
|
package/src/currentVersion.js
CHANGED
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
|
|
24
|
-
const res = await driver.getVersion(
|
|
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
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
111
|
+
dbhan = await connectUtility(driver, storedConnection, 'app');
|
|
112
112
|
if (driver[op]) {
|
|
113
|
-
await driver[op](
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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
|
-
|
|
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 (
|
|
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(
|
|
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(
|
|
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(
|
|
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;
|
package/src/shell/deployDb.js
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
const generateDeploySql = require('./generateDeploySql');
|
|
2
2
|
const executeQuery = require('./executeQuery');
|
|
3
3
|
|
|
4
|
-
async function deployDb({
|
|
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({
|
|
8
|
-
|
|
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
|
-
|
|
32
|
-
|
|
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) {
|
package/src/shell/index.js
CHANGED
|
@@ -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'];
|
|
@@ -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;
|
package/src/utility/sshTunnel.js
CHANGED
|
@@ -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',
|