diff --git a/packages/api/src/controllers/auth.js b/packages/api/src/controllers/auth.js index 509d4c9c1..0d474b8ef 100644 --- a/packages/api/src/controllers/auth.js +++ b/packages/api/src/controllers/auth.js @@ -43,6 +43,7 @@ function authMiddleware(req, res, next) { '/connections/dblogin-app', '/connections/dblogin-auth', '/connections/dblogin-auth-token', + '/health', ]; // console.log('********************* getAuthProvider()', getAuthProvider()); diff --git a/packages/api/src/controllers/runners.js b/packages/api/src/controllers/runners.js index eabb2f1c2..39e205e82 100644 --- a/packages/api/src/controllers/runners.js +++ b/packages/api/src/controllers/runners.js @@ -171,6 +171,7 @@ module.exports = { this.rejectRequest(runid, { message: 'No data returned, maybe input data source is too big' }); logger.info({ code, pid: subprocess.pid }, 'Exited process'); socket.emit(`runner-done-${runid}`, code); + this.opened = this.opened.filter(x => x.runid != runid); }); subprocess.on('error', error => { // console.log('... ERROR subprocess', error); @@ -180,6 +181,7 @@ module.exports = { severity: 'error', message: error.toString(), }); + this.opened = this.opened.filter(x => x.runid != runid); }); const newOpened = { runid, @@ -224,6 +226,7 @@ module.exports = { if (onFinished) { onFinished(); } + this.opened = this.opened.filter(x => x.runid != runid); }); subprocess.on('spawn', () => { this.dispatchMessage(runid, `Started external process ${command}`); @@ -241,6 +244,7 @@ module.exports = { }); } socket.emit(`runner-done-${runid}`); + this.opened = this.opened.filter(x => x.runid != runid); }); if (stdinFilePath) { diff --git a/packages/api/src/controllers/sessions.js b/packages/api/src/controllers/sessions.js index 5091fa6a8..08e3e11e2 100644 --- a/packages/api/src/controllers/sessions.js +++ b/packages/api/src/controllers/sessions.js @@ -127,6 +127,9 @@ module.exports = { this.dispatchMessage(sesid, 'Query session closed'); socket.emit(`session-closed-${sesid}`); }); + subprocess.on('error', () => { + this.opened = this.opened.filter(x => x.sesid != sesid); + }); subprocess.send({ msgtype: 'connect', diff --git a/packages/api/src/main.js b/packages/api/src/main.js index b3b51671c..22001ad26 100644 --- a/packages/api/src/main.js +++ b/packages/api/src/main.js @@ -38,6 +38,7 @@ const { getLogger } = require('dbgate-tools'); const { getDefaultAuthProvider } = require('./auth/authProvider'); const startCloudUpgradeTimer = require('./utility/cloudUpgrade'); const { isProApp } = require('./utility/checkLicense'); +const getHealthStatus = require('./utility/healthStatus'); const logger = getLogger('main'); @@ -117,6 +118,12 @@ function start() { }); }); + app.get(getExpressPath('/health'), async function (req, res) { + res.setHeader('Content-Type', 'application/json'); + const health = await getHealthStatus(); + res.end(JSON.stringify(health, null, 2)); + }); + app.use(bodyParser.json({ limit: '50mb' })); app.use( diff --git a/packages/api/src/utility/healthStatus.js b/packages/api/src/utility/healthStatus.js new file mode 100644 index 000000000..0b3ec80ba --- /dev/null +++ b/packages/api/src/utility/healthStatus.js @@ -0,0 +1,25 @@ +const os = require('os'); + +const databaseConnections = require('../controllers/databaseConnections'); +const serverConnections = require('../controllers/serverConnections'); +const sessions = require('../controllers/sessions'); +const runners = require('../controllers/runners'); + +async function getHealthStatus() { + const memory = process.memoryUsage(); + + return { + status: 'ok', + databaseConnectionCount: databaseConnections.opened.length, + serverConnectionCount: serverConnections.opened.length, + sessionCount: sessions.opened.length, + memory, + systemMemory: { + total: os.totalmem(), + free: os.freemem(), + }, + runProcessCount: runners.opened.length, + }; +} + +module.exports = getHealthStatus;