export - output files

This commit is contained in:
Jan Prochazka
2020-06-07 18:11:06 +02:00
parent f37524f76f
commit f63788f79b
10 changed files with 131 additions and 21 deletions

View File

@@ -1,6 +1,6 @@
const _ = require('lodash');
const path = require('path');
const fs = require('fs');
const fs = require('fs-extra');
const uuidv1 = require('uuid/v1');
const socket = require('../utility/socket');
const { fork } = require('child_process');
@@ -83,11 +83,26 @@ module.exports = {
cancel_meta: 'post',
async cancel({ runid }) {
const session = this.opened.find((x) => x.runid == runid);
if (!session) {
const runner = this.opened.find((x) => x.runid == runid);
if (!runner) {
throw new Error('Invalid runner');
}
session.subprocess.kill();
runner.subprocess.kill();
return { state: 'ok' };
},
files_meta: 'get',
async files({ runid }) {
const directory = path.join(rundir(), runid);
const files = await fs.readdir(directory);
const res = [];
for (const file of files) {
const stat = await fs.stat(path.join(directory, file));
res.push({
name: file,
size: stat.size,
});
}
return res;
},
};

View File

@@ -19,6 +19,8 @@ const runners = require('./controllers/runners');
const jsldata = require('./controllers/jsldata');
const config = require('./controllers/config');
const { rundir } = require('./utility/directories');
function start(argument = null) {
// console.log('process.argv', process.argv);
@@ -43,6 +45,8 @@ function start(argument = null) {
app.use('/pages', express.static(process.env.PAGES_DIRECTORY));
}
app.use('/runners/data', express.static(rundir()));
if (fs.existsSync('/home/dbgate-docker/build')) {
// server static files inside docker container
app.use(express.static('/home/dbgate-docker/build'));
@@ -54,7 +58,7 @@ function start(argument = null) {
if (argument == '--dynport') {
childProcessChecker();
findFreePort(53911, function (err, port) {
server.listen(port, () => {
console.log(`DbGate API listening on port ${port}`);

View File

@@ -42,7 +42,7 @@ export default function ImportExportModal({ modalState, initialValues }) {
<ImportExportConfigurator />
</ModalContent>
<ModalFooter>
<FormStyledButton type="submit" value="Export" />
<FormStyledButton type="submit" value="Generate script" />
<FormStyledButton type="button" value="Close" onClick={modalState.close} />
</ModalFooter>
</Form>

View File

@@ -51,7 +51,7 @@ function formatDuration(duration) {
return `${Math.round(duration / 1000)} s`;
}
function MessagesView({ items, onMessageClick }) {
function MessagesView({ items, onMessageClick, showProcedure = false, showLine = false }) {
const handleClick = (row) => {
if (onMessageClick) onMessageClick(row);
};
@@ -76,8 +76,8 @@ function MessagesView({ items, onMessageClick }) {
<StyledHeader>Time</StyledHeader>
<StyledHeader>Delta</StyledHeader>
<StyledHeader>Duration</StyledHeader>
<StyledHeader>Procedure</StyledHeader>
<StyledHeader>Line</StyledHeader>
{showProcedure && <StyledHeader>Procedure</StyledHeader>}
{showLine && <StyledHeader>Line</StyledHeader>}
</tr>
{items.map((row, index) => (
// @ts-ignore
@@ -91,8 +91,8 @@ function MessagesView({ items, onMessageClick }) {
? formatDuration(new Date(row.time).getTime() - new Date(items[index - 1].time).getTime())
: 'n/a'}
</StyledCell>
<StyledCell>{row.procedure}</StyledCell>
<StyledCell>{row.line}</StyledCell>
{showProcedure && <StyledCell>{row.procedure}</StyledCell>}
{showLine && <StyledCell>{row.line}</StyledCell>}
</StyledRow>
))}
</StyledTable>
@@ -100,4 +100,4 @@ function MessagesView({ items, onMessageClick }) {
);
}
export default React.memo(MessagesView);
export default React.memo(MessagesView);

View File

@@ -0,0 +1,46 @@
import React from 'react';
import useSocket from '../utility/SocketProvider';
import axios from '../utility/axios';
import styled from 'styled-components';
import TableControl, { TableColumn } from '../utility/TableControl';
import formatFileSize from '../utility/formatFileSize';
import resolveApi from '../utility/resolveApi';
export default function RunnerOutputFiles({ runnerId, executeNumber }) {
const socket = useSocket();
const [files, setFiles] = React.useState([]);
const handleRunnerDone = React.useCallback(async () => {
const resp = await axios.get(`runners/files?runid=${runnerId}`);
setFiles(resp.data);
}, [runnerId]);
React.useEffect(() => {
if (runnerId && socket) {
socket.on(`runner-done-${runnerId}`, handleRunnerDone);
return () => {
socket.off(`runner-done-${runnerId}`, handleRunnerDone);
};
}
}, [runnerId, socket]);
React.useEffect(() => {
setFiles([]);
}, [executeNumber]);
return (
<TableControl rows={files}>
<TableColumn fieldName="name" header="Name" />
<TableColumn fieldName="size" header="Size" formatter={(row) => formatFileSize(row.size)} />
<TableColumn
fieldName="download"
header="Download"
formatter={(row) => (
<a href={`${resolveApi()}/runners/data/${runnerId}/${row.name}`} target="_blank" rel="noopener noreferrer">
download
</a>
)}
/>
</TableControl>
);
}

View File

@@ -0,0 +1,27 @@
import React from 'react';
import { HorizontalSplitter } from '../widgets/Splitter';
import SocketMessagesView from './SocketMessagesView';
import { WidgetTitle } from '../widgets/WidgetStyles';
import RunnerOutputFiles from './RunnerOuputFiles';
import styled from 'styled-components';
const Container = styled.div`
flex: 1;
display: flex;
flex-direction: column;
`;
export default function RunnerOutputPane({ runnerId, executeNumber }) {
return (
<HorizontalSplitter>
<Container>
<WidgetTitle>Messages</WidgetTitle>
<SocketMessagesView eventName={runnerId ? `runner-info-${runnerId}` : null} executeNumber={executeNumber} />
</Container>
<Container>
<WidgetTitle>Output files</WidgetTitle>
<RunnerOutputFiles runnerId={runnerId} executeNumber={executeNumber} />
</Container>
</HorizontalSplitter>
);
}

View File

@@ -3,7 +3,13 @@ import React from 'react';
import MessagesView from './MessagesView';
import useSocket from '../utility/SocketProvider';
export default function SocketMessagesView({ eventName, onMessageClick = undefined, executeNumber }) {
export default function SocketMessagesView({
eventName,
onMessageClick = undefined,
executeNumber,
showProcedure = false,
showLine = false,
}) {
const [displayedMessages, setDisplayedMessages] = React.useState([]);
const cachedMessagesRef = React.useRef([]);
const socket = useSocket();
@@ -35,5 +41,12 @@ export default function SocketMessagesView({ eventName, onMessageClick = undefin
}
}, [eventName, socket]);
return <MessagesView items={displayedMessages} onMessageClick={onMessageClick} />;
return (
<MessagesView
items={displayedMessages}
onMessageClick={onMessageClick}
showProcedure={showProcedure}
showLine={showLine}
/>
);
}

View File

@@ -209,6 +209,8 @@ export default function QueryTab({
eventName={sessionId ? `session-info-${sessionId}` : null}
onMessageClick={handleMesageClick}
executeNumber={executeNumber}
showProcedure
showLine
/>
</TabPage>
</ResultTabs>

View File

@@ -11,7 +11,7 @@ import QueryToolbar from '../query/QueryToolbar';
import SocketMessagesView from '../query/SocketMessagesView';
import { TabPage } from '../widgets/TabControl';
import ResultTabs from '../sqleditor/ResultTabs';
import { VerticalSplitter } from '../widgets/Splitter';
import { VerticalSplitter, HorizontalSplitter } from '../widgets/Splitter';
import keycodes from '../utility/keycodes';
import { changeTab } from '../utility/common';
import useSocket from '../utility/SocketProvider';
@@ -20,6 +20,7 @@ import useModalState from '../modals/useModalState';
import sqlFormatter from 'sql-formatter';
import JavaScriptEditor from '../sqleditor/JavaScriptEditor';
import ShellToolbar from '../query/ShellToolbar';
import RunnerOutputPane from '../query/RunnerOutputPane';
export default function ShellTab({
tabid,
@@ -128,17 +129,13 @@ export default function ShellTab({
onKeyDown={handleKeyDown}
editorRef={editorRef}
/>
<SocketMessagesView eventName={runnerId ? `runner-info-${runnerId}` : null} executeNumber={executeNumber} />
<RunnerOutputPane runnerId={runnerId} executeNumber={executeNumber}/>
</VerticalSplitter>
{toolbarPortalRef &&
toolbarPortalRef.current &&
tabVisible &&
ReactDOM.createPortal(
<ShellToolbar
execute={handleExecute}
busy={busy}
cancel={handleCancel}
/>,
<ShellToolbar execute={handleExecute} busy={busy} cancel={handleCancel} />,
toolbarPortalRef.current
)}
</>

View File

@@ -0,0 +1,6 @@
export default function formatFileSize(size) {
if (size > 1000000000) return `${Math.round(size / 10000000000) * 10} GB`;
if (size > 1000000) return `${Math.round(size / 10000000) * 10} MB`;
if (size > 1000) return `${Math.round(size / 10000) * 10} KB`;
return `${size} bytes`;
}