mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-19 01:26:01 +00:00
shell script runner
This commit is contained in:
93
packages/api/src/controllers/runners.js
Normal file
93
packages/api/src/controllers/runners.js
Normal file
@@ -0,0 +1,93 @@
|
||||
const _ = require('lodash');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const uuidv1 = require('uuid/v1');
|
||||
const socket = require('../utility/socket');
|
||||
const { fork } = require('child_process');
|
||||
const { rundir, uploadsdir } = require('../utility/directories');
|
||||
|
||||
const scriptTemplate = (script) => `
|
||||
const dbgateApi = require(process.env.DBGATE_API || "@dbgate/api");
|
||||
require=null;
|
||||
async function run() {
|
||||
${script}
|
||||
}
|
||||
dbgateApi.runScript(run);
|
||||
`;
|
||||
|
||||
module.exports = {
|
||||
/** @type {import('@dbgate/types').OpenedRunner[]} */
|
||||
opened: [],
|
||||
|
||||
dispatchMessage(runid, message) {
|
||||
console.log('DISPATCHING', message);
|
||||
if (_.isString(message)) {
|
||||
socket.emit(`runner-info-${runid}`, {
|
||||
message,
|
||||
time: new Date(),
|
||||
severity: 'info',
|
||||
});
|
||||
}
|
||||
if (_.isPlainObject(message)) {
|
||||
socket.emit(`runner-info-${runid}`, {
|
||||
time: new Date(),
|
||||
severity: 'info',
|
||||
...message,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
handle_ping() {},
|
||||
|
||||
start_meta: 'post',
|
||||
async start({ script }) {
|
||||
const runid = uuidv1();
|
||||
const directory = path.join(rundir(), runid);
|
||||
const scriptFile = path.join(uploadsdir(), runid + '.js');
|
||||
fs.writeFileSync(`${scriptFile}`, scriptTemplate(script));
|
||||
fs.mkdirSync(directory);
|
||||
console.log(`RUNNING SCRIPT ${scriptFile}`);
|
||||
const subprocess = fork(scriptFile, ['--checkParent'], {
|
||||
cwd: directory,
|
||||
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
|
||||
env: {
|
||||
DBGATE_API: process.argv[1],
|
||||
},
|
||||
});
|
||||
subprocess.stdout.on('data', (data) => this.dispatchMessage(runid, data.toString()));
|
||||
subprocess.stderr.on('data', (data) =>
|
||||
data.toString.split('\n').forEach((message) => {
|
||||
this.dispatchMessage(runid, { severity: 'error', message });
|
||||
})
|
||||
);
|
||||
subprocess.on('exit', (code) => {
|
||||
socket.emit(`runner-done-${runid}`, code);
|
||||
});
|
||||
subprocess.on('error', (error) => {
|
||||
this.dispatchMessage({
|
||||
severity: 'error',
|
||||
message: error.toString(),
|
||||
});
|
||||
});
|
||||
const newOpened = {
|
||||
runid,
|
||||
subprocess,
|
||||
};
|
||||
this.opened.push(newOpened);
|
||||
// @ts-ignore
|
||||
subprocess.on('message', ({ msgtype, ...message }) => {
|
||||
this[`handle_${msgtype}`](runid, message);
|
||||
});
|
||||
return newOpened;
|
||||
},
|
||||
|
||||
cancel_meta: 'post',
|
||||
async cancel({ runid }) {
|
||||
const session = this.opened.find((x) => x.runid == runid);
|
||||
if (!session) {
|
||||
throw new Error('Invalid runner');
|
||||
}
|
||||
session.subprocess.kill();
|
||||
return { state: 'ok' };
|
||||
},
|
||||
};
|
||||
@@ -18,4 +18,4 @@ if (argument && argument.endsWith('Process')) {
|
||||
main.start(argument);
|
||||
}
|
||||
|
||||
module.exports = shell;
|
||||
module.exports = shell;
|
||||
|
||||
@@ -15,11 +15,12 @@ const serverConnections = require('./controllers/serverConnections');
|
||||
const databaseConnections = require('./controllers/databaseConnections');
|
||||
const metadata = require('./controllers/metadata');
|
||||
const sessions = require('./controllers/sessions');
|
||||
const runners = require('./controllers/runners');
|
||||
const jsldata = require('./controllers/jsldata');
|
||||
const config = require('./controllers/config');
|
||||
|
||||
function start(argument = null) {
|
||||
console.log('process.argv', process.argv);
|
||||
// console.log('process.argv', process.argv);
|
||||
|
||||
const app = express();
|
||||
|
||||
@@ -34,6 +35,7 @@ function start(argument = null) {
|
||||
useController(app, '/database-connections', databaseConnections);
|
||||
useController(app, '/metadata', metadata);
|
||||
useController(app, '/sessions', sessions);
|
||||
useController(app, '/runners', runners);
|
||||
useController(app, '/jsldata', jsldata);
|
||||
useController(app, '/config', config);
|
||||
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
const childProcessChecker = require('../utility/childProcessChecker');
|
||||
|
||||
async function runScript(func) {
|
||||
if (process.argv.includes('--checkParent')) {
|
||||
childProcessChecker();
|
||||
}
|
||||
try {
|
||||
await func();
|
||||
process.exit(0);
|
||||
|
||||
@@ -9,7 +9,7 @@ function childProcessChecker() {
|
||||
// One way can be to check for error code ERR_IPC_CHANNEL_CLOSED
|
||||
// and call process.exit()
|
||||
console.log('parent died', ex.toString());
|
||||
process.exit();
|
||||
process.exit(1);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
let createdDatadir = false;
|
||||
let createdJsldir = false;
|
||||
const createDirectories = {};
|
||||
|
||||
function datadir() {
|
||||
const dir = path.join(os.homedir(), 'dbgate-data');
|
||||
@@ -18,20 +18,26 @@ function datadir() {
|
||||
return dir;
|
||||
}
|
||||
|
||||
function jsldir() {
|
||||
const dir = path.join(datadir(), 'jsl');
|
||||
if (!createdJsldir) {
|
||||
const dirFunc = (dirname) => () => {
|
||||
const dir = path.join(datadir(), dirname);
|
||||
if (!createDirectories[dirname]) {
|
||||
if (!fs.existsSync(dir)) {
|
||||
console.log(`Creating jsl directory ${dir}`);
|
||||
fs.mkdirSync(dir);
|
||||
}
|
||||
createdJsldir = true;
|
||||
createDirectories[dirname] = true;
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
};
|
||||
|
||||
const jsldir = dirFunc('jsl');
|
||||
const rundir = dirFunc('run');
|
||||
const uploadsdir = dirFunc('uploads');
|
||||
|
||||
module.exports = {
|
||||
datadir,
|
||||
jsldir,
|
||||
rundir,
|
||||
uploadsdir,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user