laconic-console/packages/console-app/src/containers/panels/signal/SignalServers.js
Egor Gripasov 3c731b3b2b
fix: Add running bots tab. (#56)
* fix: Add bots resolver.

* minot fix.

* test commit.

* add bot kill

* Integration.

* add refetch.

* Lint.

* Formatting fix.

* Better error handling.
2020-12-02 05:32:49 -05:00

184 lines
5.6 KiB
JavaScript

//
// Copyright 2020 DXOS.org
//
import React, { useContext, useRef, useEffect, useState, useCallback } from 'react';
import { useQuery } from '@apollo/react-hooks';
import useComponentSize from '@rehooks/component-size';
import moment from 'moment';
import get from 'lodash.get';
import Grid from '@material-ui/core/Grid';
import TableBody from '@material-ui/core/TableBody';
import TableContainer from '@material-ui/core/TableContainer';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import { makeStyles } from '@material-ui/core/styles';
import Table from '../../../components/Table';
import TableCell from '../../../components/TableCell';
import NetworkGraph from '../../../components/NetworkGraph';
import SIGNAL_STATUS from '../../../gql/signal_status.graphql';
import { ConsoleContext, useQueryStatusReducer } from '../../../hooks';
const NODE_ID_LENGTH = 8;
const buildDataGraph = (rootId, prevGraph, nodes) => {
const newGraph = { nodes: [], links: [] };
const rootNode = nodes.find(n => n.id === rootId);
nodes.forEach(node => {
let type = 'detach';
if (rootId === node.id) {
type = 'root';
} else {
const isAdjacent = rootNode.connections.find(conn => conn.target === node.id) || node.connections.find(conn => conn.target === rootId);
if (isAdjacent) {
type = 'adjacent';
}
}
const hostname = get(node, 'kubeStatus.system.network.hostname');
const shortId = node.id.slice(0, NODE_ID_LENGTH).toUpperCase();
const label = hostname || shortId;
const oldNode = prevGraph.nodes.find(n => n.id === node.id) || {};
const newNode = { ...oldNode, id: node.id, label, type, data: node };
if (type === 'root') {
newNode.fx = 0;
newNode.fy = 0;
}
newGraph.nodes.push(newNode);
});
nodes.forEach(node => {
node.connections.forEach(conn => {
newGraph.links.push({ id: conn.id, source: node.id, target: conn.target });
});
});
return newGraph;
};
const useDataGraph = (response) => {
const [dataGraph, setDataGraph] = useState({ updatedAt: 0, nodes: [], links: [] });
useEffect(() => {
if (!response) return;
const { id: rootId, nodes = [] } = response.signal_status;
const updatedAt = moment(response.signal_status.updatedAt).valueOf();
if (dataGraph.updatedAt >= updatedAt) return;
const graph = buildDataGraph(rootId, dataGraph, nodes);
setDataGraph({
updatedAt,
...graph
});
}, [response && response.signal_status.updatedAt]);
return dataGraph;
};
const useRowStyles = makeStyles({
root: {
'& > *': {
borderBottom: 'unset'
}
}
});
function Row (props) {
const { row } = props;
const classes = useRowStyles();
const system = row.kubeStatus.system;
return (
<>
<TableRow className={classes.root}>
<TableCell>{row.id.slice(0, NODE_ID_LENGTH).toUpperCase()}</TableCell>
<TableCell>{system?.network?.hostname}</TableCell>
<TableCell align='right'>{row.signal.topics.reduce((prev, curr) => prev + curr.peers.length, 0)}</TableCell>
<TableCell align='right'>{system?.version || '-'}</TableCell>
<TableCell align='right'>{system?.memory?.used || '-'}</TableCell>
<TableCell align='right'>{system?.memory?.total || '-'}</TableCell>
<TableCell align='right'>{system?.time?.up ? moment(system?.time?.up).format('lll') : '-'}</TableCell>
</TableRow>
</>
);
}
function SignalServers () {
const { config } = useContext(ConsoleContext);
const { data: response } = useQueryStatusReducer(useQuery(SIGNAL_STATUS, { fetchPolicy: 'no-cache', pollInterval: config.api.pollInterval, context: { api: 'signal' } }));
const data = useDataGraph(response);
const sizeRef = useRef(null);
const { width, height } = useComponentSize(sizeRef);
const [open, setOpen] = useState(null);
const handleOpen = useCallback(
(id) => {
if (open && open === id) {
setOpen(null);
} else {
setOpen(id);
}
},
[open]
);
return (
<Grid container spacing={0} direction='column' alignItems='stretch' ref={sizeRef}>
<Grid item xs>
<NetworkGraph
width={width}
height={height / 2}
graph={data}
onTooltip={(node) => {
return (
<>
<strong>WebRTC Peers:</strong> {node.signal.topics.reduce((prev, curr) => prev + curr.peers.length, 0)}
<br />
{node.kubeStatus.services.map((service) => {
return <span key={service.name}><strong>{service.name}:</strong> {service.status}<br /></span>;
})}
</>);
}}
/>
</Grid>
<Grid item xs style={{ overflow: 'scroll' }}>
<TableContainer>
<Table>
<TableHead>
<TableRow>
<TableCell>ID</TableCell>
<TableCell>Hostname</TableCell>
<TableCell align='right'>Peers (WebRTC)</TableCell>
<TableCell align='right'>Kube version</TableCell>
<TableCell align='right'>Memory usage</TableCell>
<TableCell align='right'>Memory total</TableCell>
<TableCell align='right'>Uptime</TableCell>
</TableRow>
</TableHead>
<TableBody>
{data && data.nodes.map(({ data }) => {
return <Row key={data.id} row={data} open={open && open === data.id} setOpen={handleOpen} />;
})}
</TableBody>
</Table>
</TableContainer>
</Grid>
</Grid>
);
}
export default SignalServers;