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 _ = require('lodash');
const path = require('path'); const path = require('path');
const fs = require('fs'); const fs = require('fs-extra');
const uuidv1 = require('uuid/v1'); const uuidv1 = require('uuid/v1');
const socket = require('../utility/socket'); const socket = require('../utility/socket');
const { fork } = require('child_process'); const { fork } = require('child_process');
@@ -83,11 +83,26 @@ module.exports = {
cancel_meta: 'post', cancel_meta: 'post',
async cancel({ runid }) { async cancel({ runid }) {
const session = this.opened.find((x) => x.runid == runid); const runner = this.opened.find((x) => x.runid == runid);
if (!session) { if (!runner) {
throw new Error('Invalid runner'); throw new Error('Invalid runner');
} }
session.subprocess.kill(); runner.subprocess.kill();
return { state: 'ok' }; 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 jsldata = require('./controllers/jsldata');
const config = require('./controllers/config'); const config = require('./controllers/config');
const { rundir } = require('./utility/directories');
function start(argument = null) { function start(argument = null) {
// console.log('process.argv', process.argv); // 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('/pages', express.static(process.env.PAGES_DIRECTORY));
} }
app.use('/runners/data', express.static(rundir()));
if (fs.existsSync('/home/dbgate-docker/build')) { if (fs.existsSync('/home/dbgate-docker/build')) {
// server static files inside docker container // server static files inside docker container
app.use(express.static('/home/dbgate-docker/build')); app.use(express.static('/home/dbgate-docker/build'));
@@ -54,7 +58,7 @@ function start(argument = null) {
if (argument == '--dynport') { if (argument == '--dynport') {
childProcessChecker(); childProcessChecker();
findFreePort(53911, function (err, port) { findFreePort(53911, function (err, port) {
server.listen(port, () => { server.listen(port, () => {
console.log(`DbGate API listening on port ${port}`); console.log(`DbGate API listening on port ${port}`);

View File

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

View File

@@ -51,7 +51,7 @@ function formatDuration(duration) {
return `${Math.round(duration / 1000)} s`; return `${Math.round(duration / 1000)} s`;
} }
function MessagesView({ items, onMessageClick }) { function MessagesView({ items, onMessageClick, showProcedure = false, showLine = false }) {
const handleClick = (row) => { const handleClick = (row) => {
if (onMessageClick) onMessageClick(row); if (onMessageClick) onMessageClick(row);
}; };
@@ -76,8 +76,8 @@ function MessagesView({ items, onMessageClick }) {
<StyledHeader>Time</StyledHeader> <StyledHeader>Time</StyledHeader>
<StyledHeader>Delta</StyledHeader> <StyledHeader>Delta</StyledHeader>
<StyledHeader>Duration</StyledHeader> <StyledHeader>Duration</StyledHeader>
<StyledHeader>Procedure</StyledHeader> {showProcedure && <StyledHeader>Procedure</StyledHeader>}
<StyledHeader>Line</StyledHeader> {showLine && <StyledHeader>Line</StyledHeader>}
</tr> </tr>
{items.map((row, index) => ( {items.map((row, index) => (
// @ts-ignore // @ts-ignore
@@ -91,8 +91,8 @@ function MessagesView({ items, onMessageClick }) {
? formatDuration(new Date(row.time).getTime() - new Date(items[index - 1].time).getTime()) ? formatDuration(new Date(row.time).getTime() - new Date(items[index - 1].time).getTime())
: 'n/a'} : 'n/a'}
</StyledCell> </StyledCell>
<StyledCell>{row.procedure}</StyledCell> {showProcedure && <StyledCell>{row.procedure}</StyledCell>}
<StyledCell>{row.line}</StyledCell> {showLine && <StyledCell>{row.line}</StyledCell>}
</StyledRow> </StyledRow>
))} ))}
</StyledTable> </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 MessagesView from './MessagesView';
import useSocket from '../utility/SocketProvider'; 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 [displayedMessages, setDisplayedMessages] = React.useState([]);
const cachedMessagesRef = React.useRef([]); const cachedMessagesRef = React.useRef([]);
const socket = useSocket(); const socket = useSocket();
@@ -35,5 +41,12 @@ export default function SocketMessagesView({ eventName, onMessageClick = undefin
} }
}, [eventName, socket]); }, [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} eventName={sessionId ? `session-info-${sessionId}` : null}
onMessageClick={handleMesageClick} onMessageClick={handleMesageClick}
executeNumber={executeNumber} executeNumber={executeNumber}
showProcedure
showLine
/> />
</TabPage> </TabPage>
</ResultTabs> </ResultTabs>

View File

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