mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-18 02:06:01 +00:00
293 lines
8.4 KiB
JavaScript
293 lines
8.4 KiB
JavaScript
const crypto = require('crypto');
|
|
const _ = require('lodash');
|
|
const connections = require('./connections');
|
|
const socket = require('../utility/socket');
|
|
const { fork } = require('child_process');
|
|
const jsldata = require('./jsldata');
|
|
const path = require('path');
|
|
const { handleProcessCommunication } = require('../utility/processComm');
|
|
const processArgs = require('../utility/processArgs');
|
|
const { appdir } = require('../utility/directories');
|
|
const { getLogger, extractErrorLogData } = require('dbgate-tools');
|
|
const pipeForkLogs = require('../utility/pipeForkLogs');
|
|
const config = require('./config');
|
|
const { sendToAuditLog } = require('../utility/auditlog');
|
|
|
|
const logger = getLogger('sessions');
|
|
|
|
module.exports = {
|
|
/** @type {import('dbgate-types').OpenedSession[]} */
|
|
opened: [],
|
|
|
|
// handle_error(sesid, props) {
|
|
// const { error } = props;
|
|
// console.log(`Error in database session ${sesid}`, error);
|
|
// this.dispatchMessage(sesid, {
|
|
// severity: 'error',
|
|
// message: error && error.toString(),
|
|
// });
|
|
// },
|
|
|
|
// handle_row(sesid, props) {
|
|
// const { row } = props;
|
|
// socket.emit('sessionRow', row);
|
|
// },
|
|
dispatchMessage(sesid, message) {
|
|
if (_.isString(message)) {
|
|
socket.emit(`session-info-${sesid}`, {
|
|
message,
|
|
time: new Date(),
|
|
severity: 'info',
|
|
});
|
|
}
|
|
if (_.isPlainObject(message)) {
|
|
socket.emit(`session-info-${sesid}`, {
|
|
time: new Date(),
|
|
severity: 'info',
|
|
...message,
|
|
});
|
|
}
|
|
},
|
|
|
|
handle_info(sesid, props) {
|
|
const { info } = props;
|
|
this.dispatchMessage(sesid, info);
|
|
},
|
|
|
|
handle_done(sesid, props) {
|
|
socket.emit(`session-done-${sesid}`);
|
|
if (!props.skipFinishedMessage) {
|
|
if (props.controlCommand) {
|
|
this.dispatchMessage(sesid, `${_.startCase(props.controlCommand)} finished`);
|
|
} else {
|
|
this.dispatchMessage(sesid, 'Query execution finished');
|
|
}
|
|
}
|
|
const session = this.opened.find(x => x.sesid == sesid);
|
|
if (session.loadingReader_jslid) {
|
|
socket.emit(`session-jslid-done-${session.loadingReader_jslid}`);
|
|
}
|
|
if (props.autoCommit) {
|
|
this.executeControlCommand({ sesid, command: 'commitTransaction' });
|
|
}
|
|
if (session.killOnDone) {
|
|
this.kill({ sesid });
|
|
}
|
|
},
|
|
|
|
handle_recordset(sesid, props) {
|
|
const { jslid, resultIndex } = props;
|
|
socket.emit(`session-recordset-${sesid}`, { jslid, resultIndex });
|
|
},
|
|
|
|
handle_stats(sesid, stats) {
|
|
jsldata.notifyChangedStats(stats);
|
|
},
|
|
|
|
handle_charts(sesid, props) {
|
|
const { jslid, charts, resultIndex } = props;
|
|
socket.emit(`session-charts-${sesid}`, { jslid, resultIndex, charts });
|
|
},
|
|
|
|
handle_initializeFile(sesid, props) {
|
|
const { jslid } = props;
|
|
socket.emit(`session-initialize-file-${jslid}`);
|
|
},
|
|
|
|
handle_ping() {},
|
|
|
|
create_meta: true,
|
|
async create({ conid, database }) {
|
|
const sesid = crypto.randomUUID();
|
|
const connection = await connections.getCore({ conid });
|
|
const subprocess = fork(
|
|
global['API_PACKAGE'] || process.argv[1],
|
|
[
|
|
'--is-forked-api',
|
|
'--start-process',
|
|
'sessionProcess',
|
|
...processArgs.getPassArgs(),
|
|
// ...process.argv.slice(3),
|
|
],
|
|
{
|
|
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
|
|
}
|
|
);
|
|
pipeForkLogs(subprocess);
|
|
const newOpened = {
|
|
conid,
|
|
database,
|
|
subprocess,
|
|
connection,
|
|
sesid,
|
|
};
|
|
this.opened.push(newOpened);
|
|
subprocess.on('message', message => {
|
|
// @ts-ignore
|
|
const { msgtype } = message;
|
|
if (handleProcessCommunication(message, subprocess)) return;
|
|
this[`handle_${msgtype}`](sesid, message);
|
|
});
|
|
subprocess.on('exit', () => {
|
|
this.opened = this.opened.filter(x => x.sesid != sesid);
|
|
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',
|
|
...connection,
|
|
database,
|
|
globalSettings: await config.getSettings(),
|
|
});
|
|
return _.pick(newOpened, ['conid', 'database', 'sesid']);
|
|
},
|
|
|
|
executeQuery_meta: true,
|
|
async executeQuery({ sesid, sql, autoCommit, autoDetectCharts, limitRows, frontMatter }, req) {
|
|
const session = this.opened.find(x => x.sesid == sesid);
|
|
if (!session) {
|
|
throw new Error('Invalid session');
|
|
}
|
|
|
|
sendToAuditLog(req, {
|
|
category: 'dbop',
|
|
component: 'SessionController',
|
|
action: 'executeQuery',
|
|
event: 'query.execute',
|
|
severity: 'info',
|
|
detail: sql,
|
|
conid: session.conid,
|
|
database: session.database,
|
|
message: 'Executing query',
|
|
});
|
|
|
|
logger.info({ sesid, sql }, 'DBGM-00019 Processing query');
|
|
this.dispatchMessage(sesid, 'Query execution started');
|
|
session.subprocess.send({
|
|
msgtype: 'executeQuery',
|
|
sql,
|
|
autoCommit,
|
|
autoDetectCharts: autoDetectCharts || !!frontMatter?.['selected-chart'],
|
|
limitRows,
|
|
frontMatter,
|
|
});
|
|
|
|
return { state: 'ok' };
|
|
},
|
|
|
|
executeControlCommand_meta: true,
|
|
async executeControlCommand({ sesid, command }) {
|
|
const session = this.opened.find(x => x.sesid == sesid);
|
|
if (!session) {
|
|
throw new Error('Invalid session');
|
|
}
|
|
|
|
logger.info({ sesid, command }, 'DBGM-00020 Processing control command');
|
|
this.dispatchMessage(sesid, `${_.startCase(command)} started`);
|
|
session.subprocess.send({ msgtype: 'executeControlCommand', command });
|
|
|
|
return { state: 'ok' };
|
|
},
|
|
|
|
executeReader_meta: true,
|
|
async executeReader({ conid, database, sql, queryName, appFolder }) {
|
|
const { sesid } = await this.create({ conid, database });
|
|
const session = this.opened.find(x => x.sesid == sesid);
|
|
session.killOnDone = true;
|
|
const jslid = crypto.randomUUID();
|
|
session.loadingReader_jslid = jslid;
|
|
const fileName = queryName && appFolder ? path.join(appdir(), appFolder, `${queryName}.query.sql`) : null;
|
|
|
|
session.subprocess.send({ msgtype: 'executeReader', sql, fileName, jslid });
|
|
|
|
return { jslid };
|
|
},
|
|
|
|
stopLoadingReader_meta: true,
|
|
async stopLoadingReader({ jslid }) {
|
|
const session = this.opened.find(x => x.loadingReader_jslid == jslid);
|
|
if (session) {
|
|
this.kill({ sesid: session.sesid });
|
|
}
|
|
return true;
|
|
},
|
|
|
|
startProfiler_meta: true,
|
|
async startProfiler({ sesid }) {
|
|
const jslid = crypto.randomUUID();
|
|
const session = this.opened.find(x => x.sesid == sesid);
|
|
if (!session) {
|
|
throw new Error('Invalid session');
|
|
}
|
|
|
|
logger.info({ sesid }, 'DBGM-00021 Starting profiler');
|
|
session.loadingReader_jslid = jslid;
|
|
session.subprocess.send({ msgtype: 'startProfiler', jslid });
|
|
|
|
return { state: 'ok', jslid };
|
|
},
|
|
|
|
stopProfiler_meta: true,
|
|
async stopProfiler({ sesid }) {
|
|
const session = this.opened.find(x => x.sesid == sesid);
|
|
if (!session) {
|
|
throw new Error('Invalid session');
|
|
}
|
|
session.subprocess.send({ msgtype: 'stopProfiler' });
|
|
return { state: 'ok' };
|
|
},
|
|
|
|
// cancel_meta: true,
|
|
// async cancel({ sesid }) {
|
|
// const session = this.opened.find((x) => x.sesid == sesid);
|
|
// if (!session) {
|
|
// throw new Error('Invalid session');
|
|
// }
|
|
// session.subprocess.send({ msgtype: 'cancel' });
|
|
// return { state: 'ok' };
|
|
// },
|
|
|
|
kill_meta: true,
|
|
async kill({ sesid }) {
|
|
const session = this.opened.find(x => x.sesid == sesid);
|
|
if (!session) {
|
|
throw new Error('Invalid session');
|
|
}
|
|
session.subprocess.kill();
|
|
this.dispatchMessage(sesid, 'Connection closed');
|
|
return { state: 'ok' };
|
|
},
|
|
|
|
ping_meta: true,
|
|
async ping({ sesid }) {
|
|
const session = this.opened.find(x => x.sesid == sesid);
|
|
if (!session) {
|
|
throw new Error('Invalid session');
|
|
}
|
|
try {
|
|
session.subprocess.send({ msgtype: 'ping' });
|
|
} catch (err) {
|
|
logger.error(extractErrorLogData(err), 'DBGM-00145 Error pinging session');
|
|
|
|
return {
|
|
status: 'error',
|
|
message: 'Ping failed',
|
|
};
|
|
}
|
|
|
|
return { state: 'ok' };
|
|
},
|
|
|
|
// runCommand_meta: true,
|
|
// async runCommand({ conid, database, sql }) {
|
|
// console.log(`Running SQL command , conid=${conid}, database=${database}, sql=${sql}`);
|
|
// const opened = await this.ensureOpened(conid, database);
|
|
// const res = await this.sendRequest(opened, { msgtype: 'queryData', sql });
|
|
// return res;
|
|
// },
|
|
};
|