mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-20 04:16:00 +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' };
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -15,11 +15,12 @@ const serverConnections = require('./controllers/serverConnections');
|
|||||||
const databaseConnections = require('./controllers/databaseConnections');
|
const databaseConnections = require('./controllers/databaseConnections');
|
||||||
const metadata = require('./controllers/metadata');
|
const metadata = require('./controllers/metadata');
|
||||||
const sessions = require('./controllers/sessions');
|
const sessions = require('./controllers/sessions');
|
||||||
|
const runners = require('./controllers/runners');
|
||||||
const jsldata = require('./controllers/jsldata');
|
const jsldata = require('./controllers/jsldata');
|
||||||
const config = require('./controllers/config');
|
const config = require('./controllers/config');
|
||||||
|
|
||||||
function start(argument = null) {
|
function start(argument = null) {
|
||||||
console.log('process.argv', process.argv);
|
// console.log('process.argv', process.argv);
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
@@ -34,6 +35,7 @@ function start(argument = null) {
|
|||||||
useController(app, '/database-connections', databaseConnections);
|
useController(app, '/database-connections', databaseConnections);
|
||||||
useController(app, '/metadata', metadata);
|
useController(app, '/metadata', metadata);
|
||||||
useController(app, '/sessions', sessions);
|
useController(app, '/sessions', sessions);
|
||||||
|
useController(app, '/runners', runners);
|
||||||
useController(app, '/jsldata', jsldata);
|
useController(app, '/jsldata', jsldata);
|
||||||
useController(app, '/config', config);
|
useController(app, '/config', config);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
|
const childProcessChecker = require('../utility/childProcessChecker');
|
||||||
|
|
||||||
async function runScript(func) {
|
async function runScript(func) {
|
||||||
|
if (process.argv.includes('--checkParent')) {
|
||||||
|
childProcessChecker();
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
await func();
|
await func();
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ function childProcessChecker() {
|
|||||||
// One way can be to check for error code ERR_IPC_CHANNEL_CLOSED
|
// One way can be to check for error code ERR_IPC_CHANNEL_CLOSED
|
||||||
// and call process.exit()
|
// and call process.exit()
|
||||||
console.log('parent died', ex.toString());
|
console.log('parent died', ex.toString());
|
||||||
process.exit();
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ const path = require('path');
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
let createdDatadir = false;
|
let createdDatadir = false;
|
||||||
let createdJsldir = false;
|
const createDirectories = {};
|
||||||
|
|
||||||
function datadir() {
|
function datadir() {
|
||||||
const dir = path.join(os.homedir(), 'dbgate-data');
|
const dir = path.join(os.homedir(), 'dbgate-data');
|
||||||
@@ -18,20 +18,26 @@ function datadir() {
|
|||||||
return dir;
|
return dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
function jsldir() {
|
const dirFunc = (dirname) => () => {
|
||||||
const dir = path.join(datadir(), 'jsl');
|
const dir = path.join(datadir(), dirname);
|
||||||
if (!createdJsldir) {
|
if (!createDirectories[dirname]) {
|
||||||
if (!fs.existsSync(dir)) {
|
if (!fs.existsSync(dir)) {
|
||||||
console.log(`Creating jsl directory ${dir}`);
|
console.log(`Creating jsl directory ${dir}`);
|
||||||
fs.mkdirSync(dir);
|
fs.mkdirSync(dir);
|
||||||
}
|
}
|
||||||
createdJsldir = true;
|
createDirectories[dirname] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return dir;
|
return dir;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
const jsldir = dirFunc('jsl');
|
||||||
|
const rundir = dirFunc('run');
|
||||||
|
const uploadsdir = dirFunc('uploads');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
datadir,
|
datadir,
|
||||||
jsldir,
|
jsldir,
|
||||||
|
rundir,
|
||||||
|
uploadsdir,
|
||||||
};
|
};
|
||||||
|
|||||||
5
packages/types/index.d.ts
vendored
5
packages/types/index.d.ts
vendored
@@ -19,6 +19,11 @@ export interface OpenedSession {
|
|||||||
subprocess: ChildProcess;
|
subprocess: ChildProcess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface OpenedRunner {
|
||||||
|
runid: string;
|
||||||
|
subprocess: ChildProcess;
|
||||||
|
}
|
||||||
|
|
||||||
export interface StoredConnection {
|
export interface StoredConnection {
|
||||||
engine: string;
|
engine: string;
|
||||||
server: string;
|
server: string;
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
import ScriptWriter from './ScriptWriter';
|
|
||||||
|
|
||||||
export default class ScriptCreator {
|
|
||||||
constructor() {
|
|
||||||
this.varCount = 0;
|
|
||||||
this.commands = [];
|
|
||||||
}
|
|
||||||
allocVariable(prefix = 'var') {
|
|
||||||
this.varCount += 1;
|
|
||||||
return `${prefix}${this.varCount}`;
|
|
||||||
}
|
|
||||||
getCode() {
|
|
||||||
const writer = new ScriptWriter();
|
|
||||||
for (const command of this.commands) {
|
|
||||||
const { type } = command;
|
|
||||||
switch (type) {
|
|
||||||
case 'assign':
|
|
||||||
{
|
|
||||||
const { variableName, functionName, props } = command;
|
|
||||||
writer.assign(variableName, functionName, props);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'copyStream':
|
|
||||||
{
|
|
||||||
const { sourceVar, targetVar } = command;
|
|
||||||
writer.copyStream(sourceVar, targetVar);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writer.finish();
|
|
||||||
return writer.s;
|
|
||||||
}
|
|
||||||
assign(variableName, functionName, props) {
|
|
||||||
this.commands.push({
|
|
||||||
type: 'assign',
|
|
||||||
variableName,
|
|
||||||
functionName,
|
|
||||||
props,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
copyStream(sourceVar, targetVar) {
|
|
||||||
this.commands.push({
|
|
||||||
type: 'copyStream',
|
|
||||||
sourceVar,
|
|
||||||
targetVar,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
export default class ScriptWriter {
|
export default class ScriptWriter {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.s = '';
|
this.s = '';
|
||||||
this.put('const dbgateApi = require("@dbgate/api");');
|
this.varCount = 0;
|
||||||
this.put();
|
}
|
||||||
this.put('async function run() {');
|
|
||||||
|
allocVariable(prefix = 'var') {
|
||||||
|
this.varCount += 1;
|
||||||
|
return `${prefix}${this.varCount}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
put(s = '') {
|
put(s = '') {
|
||||||
@@ -11,12 +14,6 @@ export default class ScriptWriter {
|
|||||||
this.s += '\n';
|
this.s += '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
finish() {
|
|
||||||
this.put('}');
|
|
||||||
this.put();
|
|
||||||
this.put('dbgateApi.runScript(run);');
|
|
||||||
}
|
|
||||||
|
|
||||||
assign(variableName, functionName, props) {
|
assign(variableName, functionName, props) {
|
||||||
this.put(`const ${variableName} = await dbgateApi.${functionName}(${JSON.stringify(props)});`);
|
this.put(`const ${variableName} = await dbgateApi.${functionName}(${JSON.stringify(props)});`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import ScriptCreator from './ScriptCreator';
|
import ScriptWriter from './ScriptWriter';
|
||||||
import getAsArray from '../utility/getAsArray';
|
import getAsArray from '../utility/getAsArray';
|
||||||
import { getConnectionInfo } from '../utility/metadataLoaders';
|
import { getConnectionInfo } from '../utility/metadataLoaders';
|
||||||
import engines from '@dbgate/engines';
|
import engines from '@dbgate/engines';
|
||||||
import { quoteFullName, fullNameFromString } from '@dbgate/datalib';
|
import { quoteFullName, fullNameFromString } from '@dbgate/datalib';
|
||||||
|
|
||||||
export default async function createImpExpScript(values) {
|
export default async function createImpExpScript(values) {
|
||||||
const script = new ScriptCreator();
|
const script = new ScriptWriter();
|
||||||
if (values.sourceStorageType == 'database') {
|
if (values.sourceStorageType == 'database') {
|
||||||
const tables = getAsArray(values.sourceTables);
|
const tables = getAsArray(values.sourceTables);
|
||||||
for (const table of tables) {
|
for (const table of tables) {
|
||||||
@@ -29,7 +29,8 @@ export default async function createImpExpScript(values) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
script.copyStream(sourceVar, targetVar);
|
script.copyStream(sourceVar, targetVar);
|
||||||
|
script.put();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return script.getCode();
|
return script.s;
|
||||||
}
|
}
|
||||||
|
|||||||
15
packages/web/src/query/ShellToolbar.js
Normal file
15
packages/web/src/query/ShellToolbar.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ToolbarButton from '../widgets/ToolbarButton';
|
||||||
|
|
||||||
|
export default function ShellToolbar({ execute, cancel, busy}) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ToolbarButton disabled={busy} onClick={execute} icon="fas fa-play">
|
||||||
|
Execute
|
||||||
|
</ToolbarButton>
|
||||||
|
<ToolbarButton disabled={!busy} onClick={cancel} icon="fas fa-times">
|
||||||
|
Cancel
|
||||||
|
</ToolbarButton>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ import React from 'react';
|
|||||||
import MessagesView from './MessagesView';
|
import MessagesView from './MessagesView';
|
||||||
import useSocket from '../utility/SocketProvider';
|
import useSocket from '../utility/SocketProvider';
|
||||||
|
|
||||||
export default function SessionMessagesView({ sessionId, onMessageClick, executeNumber }) {
|
export default function SocketMessagesView({ eventName, onMessageClick = undefined, executeNumber }) {
|
||||||
const [displayedMessages, setDisplayedMessages] = React.useState([]);
|
const [displayedMessages, setDisplayedMessages] = React.useState([]);
|
||||||
const cachedMessagesRef = React.useRef([]);
|
const cachedMessagesRef = React.useRef([]);
|
||||||
const socket = useSocket();
|
const socket = useSocket();
|
||||||
@@ -27,13 +27,13 @@ export default function SessionMessagesView({ sessionId, onMessageClick, execute
|
|||||||
}, [executeNumber]);
|
}, [executeNumber]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (sessionId && socket) {
|
if (eventName && socket) {
|
||||||
socket.on(`session-info-${sessionId}`, handleInfo);
|
socket.on(eventName, handleInfo);
|
||||||
return () => {
|
return () => {
|
||||||
socket.off(`session-info-${sessionId}`, handleInfo);
|
socket.off(eventName, handleInfo);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, [sessionId, socket]);
|
}, [eventName, socket]);
|
||||||
|
|
||||||
return <MessagesView items={displayedMessages} onMessageClick={onMessageClick} />;
|
return <MessagesView items={displayedMessages} onMessageClick={onMessageClick} />;
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,7 @@ import { useConnectionInfo, getTableInfo, getConnectionInfo, getSqlObjectInfo }
|
|||||||
import SqlEditor from '../sqleditor/SqlEditor';
|
import SqlEditor from '../sqleditor/SqlEditor';
|
||||||
import { useUpdateDatabaseForTab, useSetOpenedTabs, useOpenedTabs } from '../utility/globalState';
|
import { useUpdateDatabaseForTab, useSetOpenedTabs, useOpenedTabs } from '../utility/globalState';
|
||||||
import QueryToolbar from '../query/QueryToolbar';
|
import QueryToolbar from '../query/QueryToolbar';
|
||||||
import SessionMessagesView from '../query/SessionMessagesView';
|
import SocketMessagesView from '../query/SocketMessagesView';
|
||||||
import { TabPage } from '../widgets/TabControl';
|
import { TabPage } from '../widgets/TabControl';
|
||||||
import ResultTabs from '../sqleditor/ResultTabs';
|
import ResultTabs from '../sqleditor/ResultTabs';
|
||||||
import { VerticalSplitter } from '../widgets/Splitter';
|
import { VerticalSplitter } from '../widgets/Splitter';
|
||||||
@@ -143,6 +143,7 @@ export default function QueryTab({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleExecute = async () => {
|
const handleExecute = async () => {
|
||||||
|
if (busy) return;
|
||||||
setExecuteNumber((num) => num + 1);
|
setExecuteNumber((num) => num + 1);
|
||||||
const selectedText = editorRef.current.editor.getSelectedText();
|
const selectedText = editorRef.current.editor.getSelectedText();
|
||||||
|
|
||||||
@@ -204,8 +205,8 @@ export default function QueryTab({
|
|||||||
{sessionId && (
|
{sessionId && (
|
||||||
<ResultTabs sessionId={sessionId} executeNumber={executeNumber}>
|
<ResultTabs sessionId={sessionId} executeNumber={executeNumber}>
|
||||||
<TabPage label="Messages" key="messages">
|
<TabPage label="Messages" key="messages">
|
||||||
<SessionMessagesView
|
<SocketMessagesView
|
||||||
sessionId={sessionId}
|
eventName={sessionId ? `session-info-${sessionId}` : null}
|
||||||
onMessageClick={handleMesageClick}
|
onMessageClick={handleMesageClick}
|
||||||
executeNumber={executeNumber}
|
executeNumber={executeNumber}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { useConnectionInfo, getTableInfo, getConnectionInfo, getSqlObjectInfo }
|
|||||||
import SqlEditor from '../sqleditor/SqlEditor';
|
import SqlEditor from '../sqleditor/SqlEditor';
|
||||||
import { useUpdateDatabaseForTab, useSetOpenedTabs, useOpenedTabs } from '../utility/globalState';
|
import { useUpdateDatabaseForTab, useSetOpenedTabs, useOpenedTabs } from '../utility/globalState';
|
||||||
import QueryToolbar from '../query/QueryToolbar';
|
import QueryToolbar from '../query/QueryToolbar';
|
||||||
import SessionMessagesView from '../query/SessionMessagesView';
|
import SocketMessagesView from '../query/SocketMessagesView';
|
||||||
import { TabPage } from '../widgets/TabControl';
|
import { TabPage } from '../widgets/TabControl';
|
||||||
import ResultTabs from '../sqleditor/ResultTabs';
|
import ResultTabs from '../sqleditor/ResultTabs';
|
||||||
import { VerticalSplitter } from '../widgets/Splitter';
|
import { VerticalSplitter } from '../widgets/Splitter';
|
||||||
@@ -19,11 +19,10 @@ import SaveSqlFileModal from '../modals/SaveSqlFileModal';
|
|||||||
import useModalState from '../modals/useModalState';
|
import useModalState from '../modals/useModalState';
|
||||||
import sqlFormatter from 'sql-formatter';
|
import sqlFormatter from 'sql-formatter';
|
||||||
import JavaScriptEditor from '../sqleditor/JavaScriptEditor';
|
import JavaScriptEditor from '../sqleditor/JavaScriptEditor';
|
||||||
|
import ShellToolbar from '../query/ShellToolbar';
|
||||||
|
|
||||||
export default function ShellTab({
|
export default function ShellTab({
|
||||||
tabid,
|
tabid,
|
||||||
conid,
|
|
||||||
database,
|
|
||||||
initialArgs,
|
initialArgs,
|
||||||
tabVisible,
|
tabVisible,
|
||||||
toolbarPortalRef,
|
toolbarPortalRef,
|
||||||
@@ -43,6 +42,11 @@ export default function ShellTab({
|
|||||||
const saveToStorageDebounced = React.useMemo(() => _.debounce(saveToStorage, 5000), [saveToStorage]);
|
const saveToStorageDebounced = React.useMemo(() => _.debounce(saveToStorage, 5000), [saveToStorage]);
|
||||||
const setOpenedTabs = useSetOpenedTabs();
|
const setOpenedTabs = useSetOpenedTabs();
|
||||||
|
|
||||||
|
const [executeNumber, setExecuteNumber] = React.useState(0);
|
||||||
|
const [runnerId, setRunnerId] = React.useState(null);
|
||||||
|
|
||||||
|
const socket = useSocket();
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
window.addEventListener('beforeunload', saveToStorage);
|
window.addEventListener('beforeunload', saveToStorage);
|
||||||
return () => {
|
return () => {
|
||||||
@@ -68,8 +72,18 @@ export default function ShellTab({
|
|||||||
|
|
||||||
const editorRef = React.useRef(null);
|
const editorRef = React.useRef(null);
|
||||||
|
|
||||||
useUpdateDatabaseForTab(tabVisible, conid, database);
|
const handleRunnerDone = React.useCallback(() => {
|
||||||
const connection = useConnectionInfo({ conid });
|
setBusy(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (runnerId && socket) {
|
||||||
|
socket.on(`runner-done-${runnerId}`, handleRunnerDone);
|
||||||
|
return () => {
|
||||||
|
socket.off(`runner-done-${runnerId}`, handleRunnerDone);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [runnerId, socket]);
|
||||||
|
|
||||||
const handleChange = (text) => {
|
const handleChange = (text) => {
|
||||||
if (text != null) shellTextRef.current = text;
|
if (text != null) shellTextRef.current = text;
|
||||||
@@ -77,12 +91,24 @@ export default function ShellTab({
|
|||||||
saveToStorageDebounced();
|
saveToStorageDebounced();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleExecute = async () => {};
|
const handleExecute = async () => {
|
||||||
|
if (busy) return;
|
||||||
|
setExecuteNumber((num) => num + 1);
|
||||||
|
const selectedText = editorRef.current.editor.getSelectedText();
|
||||||
|
|
||||||
|
let runid = runnerId;
|
||||||
|
const resp = await axios.post('runners/start', {
|
||||||
|
script: selectedText || shellText,
|
||||||
|
});
|
||||||
|
runid = resp.data.runid;
|
||||||
|
setRunnerId(runid);
|
||||||
|
setBusy(true);
|
||||||
|
};
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
// axios.post('sessions/cancel', {
|
axios.post('runners/cancel', {
|
||||||
// sesid: sessionId,
|
runid: runnerId,
|
||||||
// });
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleKeyDown = (data, hash, keyString, keyCode, event) => {
|
const handleKeyDown = (data, hash, keyString, keyCode, event) => {
|
||||||
@@ -102,7 +128,19 @@ export default function ShellTab({
|
|||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
editorRef={editorRef}
|
editorRef={editorRef}
|
||||||
/>
|
/>
|
||||||
|
<SocketMessagesView eventName={runnerId ? `runner-info-${runnerId}` : null} executeNumber={executeNumber} />
|
||||||
</VerticalSplitter>
|
</VerticalSplitter>
|
||||||
|
{toolbarPortalRef &&
|
||||||
|
toolbarPortalRef.current &&
|
||||||
|
tabVisible &&
|
||||||
|
ReactDOM.createPortal(
|
||||||
|
<ShellToolbar
|
||||||
|
execute={handleExecute}
|
||||||
|
busy={busy}
|
||||||
|
cancel={handleCancel}
|
||||||
|
/>,
|
||||||
|
toolbarPortalRef.current
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user