dbgate-api 5.2.1 → 5.2.2-alpha.12

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.
Files changed (43) hide show
  1. package/package.json +7 -6
  2. package/src/controllers/archive.js +4 -1
  3. package/src/controllers/auth.js +6 -3
  4. package/src/controllers/config.js +3 -1
  5. package/src/controllers/connections.js +23 -13
  6. package/src/controllers/databaseConnections.js +35 -12
  7. package/src/controllers/runners.js +21 -13
  8. package/src/controllers/scheduler.js +4 -1
  9. package/src/controllers/serverConnections.js +37 -10
  10. package/src/controllers/sessions.js +27 -10
  11. package/src/controllers/uploads.js +3 -1
  12. package/src/currentVersion.js +2 -2
  13. package/src/index.js +96 -2
  14. package/src/main.js +12 -9
  15. package/src/proc/databaseConnectionProcess.js +8 -6
  16. package/src/proc/serverConnectionProcess.js +4 -3
  17. package/src/proc/sessionProcess.js +26 -1
  18. package/src/proc/sshForwardProcess.js +5 -2
  19. package/src/shell/archiveWriter.js +4 -1
  20. package/src/shell/dumpDatabase.js +5 -2
  21. package/src/shell/executeQuery.js +5 -2
  22. package/src/shell/generateModelSql.js +30 -0
  23. package/src/shell/importDatabase.js +5 -2
  24. package/src/shell/index.js +4 -0
  25. package/src/shell/jsonArrayWriter.js +4 -1
  26. package/src/shell/jsonLinesReader.js +3 -1
  27. package/src/shell/jsonLinesWriter.js +3 -1
  28. package/src/shell/loadDatabase.js +21 -0
  29. package/src/shell/queryReader.js +4 -2
  30. package/src/shell/requirePlugin.js +3 -1
  31. package/src/shell/runScript.js +3 -1
  32. package/src/shell/sqlDataWriter.js +3 -2
  33. package/src/shell/tableReader.js +6 -5
  34. package/src/shell/tableWriter.js +4 -3
  35. package/src/utility/DatastoreProxy.js +29 -9
  36. package/src/utility/childProcessChecker.js +6 -2
  37. package/src/utility/cleanDirectory.js +2 -2
  38. package/src/utility/directories.js +22 -6
  39. package/src/utility/pipeForkLogs.js +19 -0
  40. package/src/utility/processArgs.js +2 -0
  41. package/src/utility/sshTunnel.js +23 -14
  42. package/src/utility/sshTunnelProxy.js +7 -1
  43. package/src/utility/useController.js +9 -7
@@ -1,17 +1,18 @@
1
- const { quoteFullName, fullNameToString } = require('dbgate-tools');
1
+ const { quoteFullName, fullNameToString, getLogger } = require('dbgate-tools');
2
2
  const requireEngineDriver = require('../utility/requireEngineDriver');
3
3
  const connectUtility = require('../utility/connectUtility');
4
+ const logger = getLogger('tableReader');
4
5
 
5
6
  async function tableReader({ connection, pureName, schemaName }) {
6
7
  const driver = requireEngineDriver(connection);
7
8
  const pool = await connectUtility(driver, connection, 'read');
8
- console.log(`Connected.`);
9
+ logger.info(`Connected.`);
9
10
 
10
11
  const fullName = { pureName, schemaName };
11
12
 
12
13
  if (driver.databaseEngineTypes.includes('document')) {
13
14
  // @ts-ignore
14
- console.log(`Reading collection ${fullNameToString(fullName)}`);
15
+ logger.info(`Reading collection ${fullNameToString(fullName)}`);
15
16
  // @ts-ignore
16
17
  return await driver.readQuery(pool, JSON.stringify(fullName));
17
18
  }
@@ -20,14 +21,14 @@ async function tableReader({ connection, pureName, schemaName }) {
20
21
  const query = `select * from ${quoteFullName(driver.dialect, fullName)}`;
21
22
  if (table) {
22
23
  // @ts-ignore
23
- console.log(`Reading table ${fullNameToString(table)}`);
24
+ logger.info(`Reading table ${fullNameToString(table)}`);
24
25
  // @ts-ignore
25
26
  return await driver.readQuery(pool, query, table);
26
27
  }
27
28
  const view = await driver.analyseSingleObject(pool, fullName, 'views');
28
29
  if (view) {
29
30
  // @ts-ignore
30
- console.log(`Reading view ${fullNameToString(view)}`);
31
+ logger.info(`Reading view ${fullNameToString(view)}`);
31
32
  // @ts-ignore
32
33
  return await driver.readQuery(pool, query, view);
33
34
  }
@@ -1,16 +1,17 @@
1
- const { fullNameToString } = require('dbgate-tools');
1
+ const { fullNameToString, getLogger } = require('dbgate-tools');
2
2
  const requireEngineDriver = require('../utility/requireEngineDriver');
3
3
  const connectUtility = require('../utility/connectUtility');
4
+ const logger = getLogger('tableWriter');
4
5
 
5
6
  async function tableWriter({ connection, schemaName, pureName, driver, systemConnection, ...options }) {
6
- console.log(`Writing table ${fullNameToString({ schemaName, pureName })}`);
7
+ logger.info(`Writing table ${fullNameToString({ schemaName, pureName })}`);
7
8
 
8
9
  if (!driver) {
9
10
  driver = requireEngineDriver(connection);
10
11
  }
11
12
  const pool = systemConnection || (await connectUtility(driver, connection, 'write'));
12
13
 
13
- console.log(`Connected.`);
14
+ logger.info(`Connected.`);
14
15
  return await driver.writeTable(pool, { schemaName, pureName }, options);
15
16
  }
16
17
 
@@ -2,6 +2,9 @@ const { fork } = require('child_process');
2
2
  const uuidv1 = require('uuid/v1');
3
3
  const { handleProcessCommunication } = require('./processComm');
4
4
  const processArgs = require('../utility/processArgs');
5
+ const pipeForkLogs = require('./pipeForkLogs');
6
+ const { getLogger } = require('dbgate-tools');
7
+ const logger = getLogger('DatastoreProxy');
5
8
 
6
9
  class DatastoreProxy {
7
10
  constructor(file) {
@@ -30,13 +33,20 @@ class DatastoreProxy {
30
33
 
31
34
  async ensureSubprocess() {
32
35
  if (!this.subprocess) {
33
- this.subprocess = fork(global['API_PACKAGE'] || process.argv[1], [
34
- '--is-forked-api',
35
- '--start-process',
36
- 'jslDatastoreProcess',
37
- ...processArgs.getPassArgs(),
38
- // ...process.argv.slice(3),
39
- ]);
36
+ this.subprocess = fork(
37
+ global['API_PACKAGE'] || process.argv[1],
38
+ [
39
+ '--is-forked-api',
40
+ '--start-process',
41
+ 'jslDatastoreProcess',
42
+ ...processArgs.getPassArgs(),
43
+ // ...process.argv.slice(3),
44
+ ],
45
+ {
46
+ stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
47
+ }
48
+ );
49
+ pipeForkLogs(this.subprocess);
40
50
 
41
51
  this.subprocess.on('message', message => {
42
52
  // @ts-ignore
@@ -60,7 +70,12 @@ class DatastoreProxy {
60
70
  const msgid = uuidv1();
61
71
  const promise = new Promise((resolve, reject) => {
62
72
  this.requests[msgid] = [resolve, reject];
63
- this.subprocess.send({ msgtype: 'read', msgid, offset, limit });
73
+ try {
74
+ this.subprocess.send({ msgtype: 'read', msgid, offset, limit });
75
+ } catch (err) {
76
+ logger.error({ err }, 'Error getting rows');
77
+ this.subprocess = null;
78
+ }
64
79
  });
65
80
  return promise;
66
81
  }
@@ -69,7 +84,12 @@ class DatastoreProxy {
69
84
  const msgid = uuidv1();
70
85
  const promise = new Promise((resolve, reject) => {
71
86
  this.requests[msgid] = [resolve, reject];
72
- this.subprocess.send({ msgtype: 'notify', msgid });
87
+ try {
88
+ this.subprocess.send({ msgtype: 'notify', msgid });
89
+ } catch (err) {
90
+ logger.error({ err }, 'Error notifying subprocess');
91
+ this.subprocess = null;
92
+ }
73
93
  });
74
94
  return promise;
75
95
  }
@@ -1,14 +1,18 @@
1
+ const { getLogger } = require('dbgate-tools');
2
+
3
+ const logger = getLogger('childProcessChecked');
4
+
1
5
  let counter = 0;
2
6
 
3
7
  function childProcessChecker() {
4
8
  setInterval(() => {
5
9
  try {
6
10
  process.send({ msgtype: 'ping', counter: counter++ });
7
- } catch (ex) {
11
+ } catch (err) {
8
12
  // This will come once parent dies.
9
13
  // One way can be to check for error code ERR_IPC_CHANNEL_CLOSED
10
14
  // and call process.exit()
11
- console.log('parent died', ex.toString());
15
+ logger.error({ err }, 'parent died');
12
16
  process.exit(1);
13
17
  }
14
18
  }, 1000);
@@ -2,7 +2,7 @@ const fs = require('fs-extra');
2
2
  const path = require('path');
3
3
  const ageSeconds = 3600;
4
4
 
5
- async function cleanDirectory(directory) {
5
+ async function cleanDirectory(directory, age = undefined) {
6
6
  const files = await fs.readdir(directory);
7
7
  const now = new Date().getTime();
8
8
 
@@ -10,7 +10,7 @@ async function cleanDirectory(directory) {
10
10
  const full = path.join(directory, file);
11
11
  const stat = await fs.stat(full);
12
12
  const mtime = stat.mtime.getTime();
13
- const expirationTime = mtime + ageSeconds * 1000;
13
+ const expirationTime = mtime + (age || ageSeconds) * 1000;
14
14
  if (now > expirationTime) {
15
15
  if (stat.isDirectory()) {
16
16
  await fs.rmdir(full, { recursive: true });
@@ -1,20 +1,24 @@
1
1
  const os = require('os');
2
2
  const path = require('path');
3
3
  const fs = require('fs');
4
+ const _ = require('lodash');
4
5
  const cleanDirectory = require('./cleanDirectory');
5
6
  const platformInfo = require('./platformInfo');
6
7
  const processArgs = require('./processArgs');
7
8
  const consoleObjectWriter = require('../shell/consoleObjectWriter');
9
+ const { getLogger } = require('dbgate-tools');
10
+
11
+ let logsFilePath;
8
12
 
9
13
  const createDirectories = {};
10
14
  const ensureDirectory = (dir, clean) => {
11
15
  if (!createDirectories[dir]) {
12
16
  if (clean && fs.existsSync(dir) && !platformInfo.isForkedApi) {
13
- console.log(`Cleaning directory ${dir}`);
14
- cleanDirectory(dir);
17
+ getLogger('directories').info(`Cleaning directory ${dir}`);
18
+ cleanDirectory(dir, _.isNumber(clean) ? clean : null);
15
19
  }
16
20
  if (!fs.existsSync(dir)) {
17
- console.log(`Creating directory ${dir}`);
21
+ getLogger('directories').info(`Creating directory ${dir}`);
18
22
  fs.mkdirSync(dir);
19
23
  }
20
24
  createDirectories[dir] = true;
@@ -38,7 +42,7 @@ function datadir() {
38
42
  return dir;
39
43
  }
40
44
 
41
- const dirFunc = (dirname, clean = false) => () => {
45
+ const dirFunc = (dirname, clean) => () => {
42
46
  const dir = path.join(datadir(), dirname);
43
47
  ensureDirectory(dir, clean);
44
48
 
@@ -52,6 +56,7 @@ const pluginsdir = dirFunc('plugins');
52
56
  const archivedir = dirFunc('archive');
53
57
  const appdir = dirFunc('apps');
54
58
  const filesdir = dirFunc('files');
59
+ const logsdir = dirFunc('logs', 3600 * 24 * 7);
55
60
 
56
61
  function packagedPluginsDir() {
57
62
  // console.log('CALL DIR FROM', new Error('xxx').stack);
@@ -127,11 +132,19 @@ function migrateDataDir() {
127
132
  if (fs.existsSync(oldDir) && !fs.existsSync(newDir)) {
128
133
  fs.renameSync(oldDir, newDir);
129
134
  }
130
- } catch (e) {
131
- console.log('Error migrating data dir:', e.message);
135
+ } catch (err) {
136
+ getLogger('directories').error({ err }, 'Error migrating data dir');
132
137
  }
133
138
  }
134
139
 
140
+ function setLogsFilePath(value) {
141
+ logsFilePath = value;
142
+ }
143
+
144
+ function getLogsFilePath() {
145
+ return logsFilePath;
146
+ }
147
+
135
148
  migrateDataDir();
136
149
 
137
150
  module.exports = {
@@ -144,9 +157,12 @@ module.exports = {
144
157
  ensureDirectory,
145
158
  pluginsdir,
146
159
  filesdir,
160
+ logsdir,
147
161
  packagedPluginsDir,
148
162
  packagedPluginList,
149
163
  getPluginBackendPath,
150
164
  resolveArchiveFolder,
151
165
  clearArchiveLinksCache,
166
+ getLogsFilePath,
167
+ setLogsFilePath,
152
168
  };
@@ -0,0 +1,19 @@
1
+ const byline = require('byline');
2
+ const { safeJsonParse, getLogger } = require('dbgate-tools');
3
+ const logger = getLogger();
4
+
5
+ const logDispatcher = method => data => {
6
+ const json = safeJsonParse(data.toString());
7
+ if (json && json.level) {
8
+ logger.log(json);
9
+ } else {
10
+ logger[method](json || data.toString());
11
+ }
12
+ };
13
+
14
+ function pipeForkLogs(subprocess) {
15
+ byline(subprocess.stdout).on('data', logDispatcher('info'));
16
+ byline(subprocess.stderr).on('data', logDispatcher('error'));
17
+ }
18
+
19
+ module.exports = pipeForkLogs;
@@ -11,6 +11,7 @@ const startProcess = getNamedArg('--start-process');
11
11
  const isForkedApi = process.argv.includes('--is-forked-api');
12
12
  const pluginsDir = getNamedArg('--plugins-dir');
13
13
  const workspaceDir = getNamedArg('--workspace-dir');
14
+ const processDisplayName = getNamedArg('--process-display-name');
14
15
  const listenApi = process.argv.includes('--listen-api');
15
16
  const listenApiChild = process.argv.includes('--listen-api-child') || listenApi;
16
17
 
@@ -37,4 +38,5 @@ module.exports = {
37
38
  workspaceDir,
38
39
  listenApi,
39
40
  listenApiChild,
41
+ processDisplayName,
40
42
  };
@@ -5,6 +5,9 @@ const AsyncLock = require('async-lock');
5
5
  const lock = new AsyncLock();
6
6
  const { fork } = require('child_process');
7
7
  const processArgs = require('../utility/processArgs');
8
+ const { getLogger } = require('dbgate-tools');
9
+ const pipeForkLogs = require('./pipeForkLogs');
10
+ const logger = getLogger('sshTunnel');
8
11
 
9
12
  const sshTunnelCache = {};
10
13
 
@@ -21,18 +24,24 @@ const CONNECTION_FIELDS = [
21
24
  const TUNNEL_FIELDS = [...CONNECTION_FIELDS, 'server', 'port'];
22
25
 
23
26
  function callForwardProcess(connection, tunnelConfig, tunnelCacheKey) {
24
- let subprocess = fork(global['API_PACKAGE'] || process.argv[1], [
25
- '--is-forked-api',
26
- '--start-process',
27
- 'sshForwardProcess',
28
- ...processArgs.getPassArgs(),
29
- ]);
27
+ let subprocess = fork(
28
+ global['API_PACKAGE'] || process.argv[1],
29
+ ['--is-forked-api', '--start-process', 'sshForwardProcess', ...processArgs.getPassArgs()],
30
+ {
31
+ stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
32
+ }
33
+ );
34
+ pipeForkLogs(subprocess);
30
35
 
31
- subprocess.send({
32
- msgtype: 'connect',
33
- connection,
34
- tunnelConfig,
35
- });
36
+ try {
37
+ subprocess.send({
38
+ msgtype: 'connect',
39
+ connection,
40
+ tunnelConfig,
41
+ });
42
+ } catch (err) {
43
+ logger.error({ err }, 'Error connecting SSH');
44
+ }
36
45
  return new Promise((resolve, reject) => {
37
46
  subprocess.on('message', resp => {
38
47
  // @ts-ignore
@@ -45,7 +54,7 @@ function callForwardProcess(connection, tunnelConfig, tunnelCacheKey) {
45
54
  }
46
55
  });
47
56
  subprocess.on('exit', code => {
48
- console.log('SSH forward process exited');
57
+ logger.info('SSH forward process exited');
49
58
  delete sshTunnelCache[tunnelCacheKey];
50
59
  });
51
60
  });
@@ -65,13 +74,13 @@ async function getSshTunnel(connection) {
65
74
  toHost: connection.server,
66
75
  };
67
76
  try {
68
- console.log(
77
+ logger.info(
69
78
  `Creating SSH tunnel to ${connection.sshHost}-${connection.server}:${connection.port}, using local port ${localPort}`
70
79
  );
71
80
 
72
81
  const subprocess = await callForwardProcess(connection, tunnelConfig, tunnelCacheKey);
73
82
 
74
- console.log(
83
+ logger.info(
75
84
  `Created SSH tunnel to ${connection.sshHost}-${connection.server}:${connection.port}, using local port ${localPort}`
76
85
  );
77
86
 
@@ -1,11 +1,17 @@
1
+ const { getLogger } = require('dbgate-tools');
1
2
  const uuidv1 = require('uuid/v1');
2
3
  const { getSshTunnel } = require('./sshTunnel');
4
+ const logger = getLogger('sshTunnelProxy');
3
5
 
4
6
  const dispatchedMessages = {};
5
7
 
6
8
  async function handleGetSshTunnelRequest({ msgid, connection }, subprocess) {
7
9
  const response = await getSshTunnel(connection);
8
- subprocess.send({ msgtype: 'getsshtunnel-response', msgid, response });
10
+ try {
11
+ subprocess.send({ msgtype: 'getsshtunnel-response', msgid, response });
12
+ } catch (err) {
13
+ logger.error({ err }, 'Error sending to SSH tunnel');
14
+ }
9
15
  }
10
16
 
11
17
  function handleGetSshTunnelResponse({ msgid, response }, subprocess) {
@@ -2,7 +2,9 @@ const _ = require('lodash');
2
2
  const express = require('express');
3
3
  const getExpressPath = require('./getExpressPath');
4
4
  const { MissingCredentialsError } = require('./exceptions');
5
+ const { getLogger } = require('dbgate-tools');
5
6
 
7
+ const logger = getLogger('useController');
6
8
  /**
7
9
  * @param {string} route
8
10
  */
@@ -10,11 +12,11 @@ module.exports = function useController(app, electron, route, controller) {
10
12
  const router = express.Router();
11
13
 
12
14
  if (controller._init) {
13
- console.log(`Calling init controller for controller ${route}`);
15
+ logger.info(`Calling init controller for controller ${route}`);
14
16
  try {
15
17
  controller._init();
16
18
  } catch (err) {
17
- console.log(`Error initializing controller, exiting application`, err);
19
+ logger.error({ err }, `Error initializing controller, exiting application`);
18
20
  process.exit(1);
19
21
  }
20
22
  }
@@ -75,16 +77,16 @@ module.exports = function useController(app, electron, route, controller) {
75
77
  try {
76
78
  const data = await controller[key]({ ...req.body, ...req.query }, req);
77
79
  res.json(data);
78
- } catch (e) {
79
- console.log(e);
80
- if (e instanceof MissingCredentialsError) {
80
+ } catch (err) {
81
+ logger.error({ err }, `Error when processing route ${route}/${key}`);
82
+ if (err instanceof MissingCredentialsError) {
81
83
  res.json({
82
84
  missingCredentials: true,
83
85
  apiErrorMessage: 'Missing credentials',
84
- detail: e.detail,
86
+ detail: err.detail,
85
87
  });
86
88
  } else {
87
- res.status(500).json({ apiErrorMessage: e.message });
89
+ res.status(500).json({ apiErrorMessage: err.message });
88
90
  }
89
91
  }
90
92
  });