diff --git a/packages/api/src/controllers/serverConnections.js b/packages/api/src/controllers/serverConnections.js index aeaf1fdb0..b30f977bc 100644 --- a/packages/api/src/controllers/serverConnections.js +++ b/packages/api/src/controllers/serverConnections.js @@ -5,7 +5,7 @@ const _ = require('lodash'); module.exports = { opened: [], - closed: [], + closed: {}, handle_databases(conid, { databases }) { const existing = this.opened.find((x) => x.conid == conid); @@ -40,7 +40,7 @@ module.exports = { disconnected: false, }; this.opened.push(newOpened); - this.closed = this.closed.filter((x) => x != conid); + delete this.closed[conid]; socket.emitChanged(`server-status-changed`); // @ts-ignore subprocess.on('message', ({ msgtype, ...message }) => { @@ -49,21 +49,24 @@ module.exports = { }); subprocess.on('exit', () => { if (newOpened.disconnected) return; - this.opened = this.opened.filter((x) => x.conid != conid); - this.closed.push(conid); - socket.emitChanged(`server-status-changed`); + this.close(conid, false); }); subprocess.send({ msgtype: 'connect', ...connection }); return newOpened; }, - close(conid) { + close(conid, kill = true) { const existing = this.opened.find((x) => x.conid == conid); if (existing) { existing.disconnected = true; - existing.subprocess.kill(); + if (kill) existing.subprocess.kill(); + const last = this.opened.find((x) => x.conid == conid); this.opened = this.opened.filter((x) => x.conid != conid); - this.closed.push(conid); + this.closed[conid] = { + ...(last && last.status), + name: 'error', + }; + socket.emitChanged(`server-status-changed`); } }, @@ -76,15 +79,7 @@ module.exports = { serverStatus_meta: 'get', async serverStatus() { return { - ...this.closed.reduce( - (res, conid) => ({ - ...res, - [conid]: { - name: 'error', - }, - }), - {} - ), + ...this.closed, ..._.mapValues(_.keyBy(this.opened, 'conid'), 'status'), }; }, diff --git a/packages/api/src/proc/serverConnectionProcess.js b/packages/api/src/proc/serverConnectionProcess.js index 745875ac6..552ca422f 100644 --- a/packages/api/src/proc/serverConnectionProcess.js +++ b/packages/api/src/proc/serverConnectionProcess.js @@ -20,9 +20,12 @@ async function handleRefresh() { lastDatabases = databasesString; } } catch (err) { - setStatusName('error'); - console.error(err); - process.exit(1); + setStatus({ + name: 'error', + message: err.message, + }); + // console.error(err); + setTimeout(() => process.exit(1), 1000); } } @@ -49,9 +52,12 @@ async function handleConnect(connection) { handleRefresh(); setInterval(handleRefresh, 30 * 1000); } catch (err) { - setStatusName('error'); - console.error(err); - process.exit(1); + setStatus({ + name: 'error', + message: err.message, + }); + // console.error(err); + setTimeout(() => process.exit(1), 1000); } } diff --git a/packages/web/src/appobj/AppObjectList.js b/packages/web/src/appobj/AppObjectList.js index c36da4cbf..56689c9af 100644 --- a/packages/web/src/appobj/AppObjectList.js +++ b/packages/web/src/appobj/AppObjectList.js @@ -34,14 +34,30 @@ function AppObjectListItem({ makeAppObj, data, filter, appobj, onObjectClick, Su const [isExpanded, setIsExpanded] = React.useState(false); const [isHover, setIsHover] = React.useState(false); + React.useEffect(() => { + if (!appobj.isExpandable) { + // if (data._id == '6pOY2iFY8Gsq7mk6') console.log('COLLAPSE1'); + setIsExpanded(false); + } + }, [appobj && appobj.isExpandable]); + // const { matcher } = appobj; // if (matcher && !matcher(filter)) return null; - if (onObjectClick) appobj.onClick = onObjectClick; + + if (onObjectClick) + appobj = { + ...appobj, + onClick: onObjectClick, + }; if (SubItems) { const oldClick = appobj.onClick; - appobj.onClick = () => { - if (oldClick) oldClick(); - setIsExpanded(!isExpanded); + appobj = { + ...appobj, + onClick: () => { + if (oldClick) oldClick(); + // if (data._id == '6pOY2iFY8Gsq7mk6') console.log('COLLAPSE2'); + setIsExpanded((v) => !v); + }, }; } diff --git a/packages/web/src/appobj/AppObjects.js b/packages/web/src/appobj/AppObjects.js index e52281c49..d074714f9 100644 --- a/packages/web/src/appobj/AppObjects.js +++ b/packages/web/src/appobj/AppObjects.js @@ -42,6 +42,7 @@ export function AppObjectCore({ component = 'div', prefix = null, statusIcon, + statusTitle, ...other }) { const appObjectParams = useAppObjectParams(); @@ -71,7 +72,7 @@ export function AppObjectCore({ {title} {statusIcon && ( - + )} diff --git a/packages/web/src/appobj/connectionAppObject.js b/packages/web/src/appobj/connectionAppObject.js index f51b6b0de..d84fc9e1c 100644 --- a/packages/web/src/appobj/connectionAppObject.js +++ b/packages/web/src/appobj/connectionAppObject.js @@ -7,7 +7,7 @@ import ConnectionModal from '../modals/ConnectionModal'; import axios from '../utility/axios'; import { filterName } from '@dbgate/datalib'; -function Menu({ data, setOpenedConnections }) { +function Menu({ data, setOpenedConnections, openedConnections }) { const handleEdit = () => { showModal((modalState) => ); }; @@ -24,8 +24,12 @@ function Menu({ data, setOpenedConnections }) { <> Edit Delete - Refresh - Disconnect + {openedConnections.includes(data._id) && data.status && ( + Refresh + )} + {openedConnections.includes(data._id) && ( + Disconnect + )} ); } @@ -47,13 +51,16 @@ const connectionAppObject = (flags) => ( : null; const onClick = () => setOpenedConnections((c) => [...c, _id]); - // let isBusy = false; let statusIcon = null; + let statusTitle = null; if (openedConnections.includes(_id)) { if (!status) statusIcon = 'fas fa-spinner fa-spin'; else if (status.name == 'pending') statusIcon = 'fas fa-spinner fa-spin'; else if (status.name == 'ok') statusIcon = 'fas fa-check-circle green'; else statusIcon = 'fas fa-times-circle red'; + if (status && status.name == 'error') { + statusTitle = status.message; + } } return { @@ -65,8 +72,8 @@ const connectionAppObject = (flags) => ( isBold, isExpandable, onClick, - // isBusy, statusIcon, + statusTitle, }; }; diff --git a/packages/web/src/icons.js b/packages/web/src/icons.js index 2748bd9f7..16de8be49 100644 --- a/packages/web/src/icons.js +++ b/packages/web/src/icons.js @@ -26,15 +26,12 @@ export function FontIcon({ icon, ...props }) { let className = props.className || ''; - // if (_.startsWith(name, 'bs-')) className += ` glyphicon glyphicon-${name.substr(3)}`; if (type == 'fas' || type == 'far') className += ` ${type} ${name} ${parts.join(' ')}`; - if (_.includes(parts, 'spin')) className += ' fa-spin'; - const style = { ...props.style }; const last = parts[parts.length - 1]; - if (last && last != 'spin') { + if (last && last != 'fa-spin') { style['color'] = last; } diff --git a/packages/web/src/widgets/DatabaseWidget.js b/packages/web/src/widgets/DatabaseWidget.js index 50c699804..acfbe76bc 100644 --- a/packages/web/src/widgets/DatabaseWidget.js +++ b/packages/web/src/widgets/DatabaseWidget.js @@ -4,11 +4,12 @@ import _ from 'lodash'; import { AppObjectList } from '../appobj/AppObjectList'; import connectionAppObject from '../appobj/connectionAppObject'; import databaseAppObject from '../appobj/databaseAppObject'; -import { useSetCurrentDatabase, useCurrentDatabase } from '../utility/globalState'; +import { useSetCurrentDatabase, useCurrentDatabase, useOpenedConnections } from '../utility/globalState'; import InlineButton from './InlineButton'; import databaseObjectAppObject from '../appobj/databaseObjectAppObject'; import { useSqlObjectList, useDatabaseList, useConnectionList, useServerStatus } from '../utility/metadataLoaders'; import { SearchBoxWrapper, InnerContainer, Input, MainContainer, OuterContainer, WidgetTitle } from './WidgetStyles'; +import axios from '../utility/axios'; function SubDatabaseList({ data }) { const setDb = useSetCurrentDatabase(); @@ -32,8 +33,17 @@ function SubDatabaseList({ data }) { function ConnectionList() { const connections = useConnectionList(); const serverStatus = useServerStatus(); + const openedConnections = useOpenedConnections(); const connectionsWithStatus = - connections && serverStatus && connections.map((conn) => ({ ...conn, status: serverStatus[conn._id] })); + connections && serverStatus + ? connections.map((conn) => ({ ...conn, status: serverStatus[conn._id] })) + : connections; + + const handleRefreshConnections = () => { + for (const conid of openedConnections) { + axios.post('server-connections/refresh', { conid }); + } + }; const [filter, setFilter] = React.useState(''); return ( @@ -41,7 +51,7 @@ function ConnectionList() { Connections setFilter(e.target.value)} /> - Refresh + Refresh