commit
e55d056844
@ -23,7 +23,7 @@ const useStyles = makeStyles(theme => ({
|
|||||||
padding: theme.spacing(1),
|
padding: theme.spacing(1),
|
||||||
|
|
||||||
'& div': {
|
'& div': {
|
||||||
fontSize: 16,
|
fontSize: 14,
|
||||||
fontFamily: 'monospace',
|
fontFamily: 'monospace',
|
||||||
whiteSpace: 'nowrap'
|
whiteSpace: 'nowrap'
|
||||||
}
|
}
|
||||||
|
@ -4,37 +4,56 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import ExitToApp from '@material-ui/icons/ExitToApp';
|
|
||||||
import Link from '@material-ui/core/Link';
|
import Link from '@material-ui/core/Link';
|
||||||
|
import LinkIcon from '@material-ui/icons/ExitToApp';
|
||||||
|
|
||||||
import { getServiceUrl } from '../util/config';
|
import { getServiceUrl } from '../util/config';
|
||||||
|
|
||||||
const QUERY = `{
|
const QUERY = `
|
||||||
queryRecords(attributes: [
|
query {
|
||||||
{ key: "name", value: { string: "%NAME%" }}]) {
|
queryRecords(attributes: [{ key: "name", value: { string: "%NAME%" } }]) {
|
||||||
id type name bondId createTime expiryTime owners attributes { key, value { string, json } }
|
id
|
||||||
}
|
type
|
||||||
}`;
|
name
|
||||||
|
bondId
|
||||||
|
createTime
|
||||||
|
expiryTime
|
||||||
|
owners
|
||||||
|
attributes {
|
||||||
|
key
|
||||||
|
value {
|
||||||
|
string
|
||||||
|
json
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render link to record in WNS.
|
* Render link to record in WNS.
|
||||||
* @param {Object} config
|
* @param {Object} config
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @param {string} [text]
|
* @param {string} [text]
|
||||||
|
* @param {boolean} icon
|
||||||
*/
|
*/
|
||||||
const QueryLink = ({ config, name, text, icon = false }) => {
|
const QueryLink = ({ config, name, text, icon = false }) => {
|
||||||
const baseURL = getServiceUrl(config, 'wns.webui');
|
const baseURL = getServiceUrl(config, 'wns.webui');
|
||||||
const query = QUERY.replace('%NAME%', name);
|
const query = QUERY.replace('%NAME%', name);
|
||||||
|
|
||||||
|
// NOTE: Playground bug opens two tabs.
|
||||||
const fullURL = encodeURI(`${baseURL}?query=${query}`);
|
const fullURL = encodeURI(`${baseURL}?query=${query}`);
|
||||||
|
|
||||||
if (icon) {
|
return (
|
||||||
return (
|
<Link href={fullURL} target='gql'>
|
||||||
<Link href={fullURL} target='wns'>
|
{icon && (
|
||||||
<ExitToApp />
|
<LinkIcon />
|
||||||
</Link>
|
)}
|
||||||
);
|
{!icon && (
|
||||||
}
|
text || name
|
||||||
return <Link href={fullURL} target='wns'>{text || name}</Link>;
|
)}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default QueryLink;
|
export default QueryLink;
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useHistory, useParams } from 'react-router';
|
import { useHistory, useParams } from 'react-router';
|
||||||
|
|
||||||
import { makeStyles } from '@material-ui/core';
|
import { makeStyles } from '@material-ui/core';
|
||||||
import List from '@material-ui/core/List';
|
import List from '@material-ui/core/List';
|
||||||
import ListItem from '@material-ui/core/ListItem';
|
import ListItem from '@material-ui/core/ListItem';
|
||||||
@ -17,7 +18,6 @@ const useStyles = makeStyles(theme => ({
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
justifyContent: 'space-between'
|
justifyContent: 'space-between'
|
||||||
// backgroundColor: theme.palette.grey[100]
|
|
||||||
},
|
},
|
||||||
|
|
||||||
list: {
|
list: {
|
||||||
|
@ -9,18 +9,21 @@ import MuiTableCell from '@material-ui/core/TableCell';
|
|||||||
import { makeStyles } from '@material-ui/core';
|
import { makeStyles } from '@material-ui/core';
|
||||||
|
|
||||||
const useStyles = makeStyles(() => ({
|
const useStyles = makeStyles(() => ({
|
||||||
|
icon: {
|
||||||
|
width: 48
|
||||||
|
},
|
||||||
small: {
|
small: {
|
||||||
width: 160
|
width: 130
|
||||||
},
|
},
|
||||||
medium: {
|
medium: {
|
||||||
width: 220
|
width: 170
|
||||||
},
|
},
|
||||||
icon: {
|
large: {
|
||||||
width: 120
|
width: 400
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const TableCell = ({ children, size, monospace = false, title, ...rest }) => {
|
const TableCell = ({ children, size, monospace = false, style, title, ...rest }) => {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -31,8 +34,10 @@ const TableCell = ({ children, size, monospace = false, title, ...rest }) => {
|
|||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
textOverflow: 'ellipsis',
|
textOverflow: 'ellipsis',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
|
verticalAlign: 'top',
|
||||||
fontFamily: monospace ? 'monospace' : 'inherit',
|
fontFamily: monospace ? 'monospace' : 'inherit',
|
||||||
fontSize: monospace ? 14 : 'inherit'
|
fontSize: monospace ? 14 : 13,
|
||||||
|
...style
|
||||||
}}
|
}}
|
||||||
title={title}
|
title={title}
|
||||||
>
|
>
|
||||||
|
@ -25,7 +25,7 @@ import Config from './panels/Config';
|
|||||||
import IPFS from './panels/ipfs/IPFS';
|
import IPFS from './panels/ipfs/IPFS';
|
||||||
import Metadata from './panels/Metadata';
|
import Metadata from './panels/Metadata';
|
||||||
import Signaling from './panels/signal/Signaling';
|
import Signaling from './panels/signal/Signaling';
|
||||||
import Status from './panels/Status';
|
import System from './panels/system/Status';
|
||||||
import WNS from './panels/wns/WNS';
|
import WNS from './panels/wns/WNS';
|
||||||
|
|
||||||
// Global error handler.
|
// Global error handler.
|
||||||
@ -52,7 +52,7 @@ const Main = ({ config }) => {
|
|||||||
<Route path='/ipfs' component={IPFS} />
|
<Route path='/ipfs' component={IPFS} />
|
||||||
<Route path='/metadata' component={Metadata} />
|
<Route path='/metadata' component={Metadata} />
|
||||||
<Route path='/signaling' component={Signaling} />
|
<Route path='/signaling' component={Signaling} />
|
||||||
<Route path='/status' component={Status} />
|
<Route path='/system' component={System} />
|
||||||
<Route path='/wns' component={WNS} />
|
<Route path='/wns' component={WNS} />
|
||||||
</Layout>
|
</Layout>
|
||||||
</Route>
|
</Route>
|
||||||
|
@ -30,14 +30,12 @@ const AppRecords = () => {
|
|||||||
|
|
||||||
// TODO(telackey): Does this also need an interval?
|
// TODO(telackey): Does this also need an interval?
|
||||||
const ipfsResponse = useQueryStatusReducer(useQuery(IPFS_STATUS));
|
const ipfsResponse = useQueryStatusReducer(useQuery(IPFS_STATUS));
|
||||||
|
|
||||||
if (!appResponse || !ipfsResponse) {
|
if (!appResponse || !ipfsResponse) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const appData = JSON.parse(appResponse.wns_records.json);
|
const appData = JSON.parse(appResponse.wns_records.json);
|
||||||
const ipfsData = JSON.parse(ipfsResponse.ipfs_status.json);
|
const ipfsData = JSON.parse(ipfsResponse.ipfs_status.json);
|
||||||
|
|
||||||
const localRefs = new Set(ipfsData.refs.local);
|
const localRefs = new Set(ipfsData.refs.local);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -45,23 +43,25 @@ const AppRecords = () => {
|
|||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell onClick={sortBy('name')}>Identifier</TableCell>
|
<TableCell onClick={sortBy('name')}>Identifier</TableCell>
|
||||||
<TableCell onClick={sortBy('attributes.displayName')}>Name</TableCell>
|
|
||||||
<TableCell onClick={sortBy('version')} size='small'>Version</TableCell>
|
<TableCell onClick={sortBy('version')} size='small'>Version</TableCell>
|
||||||
|
<TableCell onClick={sortBy('attributes.displayName')}>Name</TableCell>
|
||||||
<TableCell onClick={sortBy('createTime')} size='small'>Created</TableCell>
|
<TableCell onClick={sortBy('createTime')} size='small'>Created</TableCell>
|
||||||
<TableCell size='icon'>Downloaded</TableCell>
|
<TableCell size='icon' />
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{appData.sort(sorter).map(({ id, name, version, createTime, attributes: { displayName, publicUrl, package: hash } }) => {
|
{appData.sort(sorter).map(({ id, name, version, createTime, attributes: { displayName, package: hash } }) => {
|
||||||
return (
|
return (
|
||||||
<TableRow key={id} size='small'>
|
<TableRow key={id} size='small'>
|
||||||
<TableCell monospace>
|
<TableCell monospace>
|
||||||
<AppLink config={config} name={name} />
|
<AppLink config={config} name={name} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>{displayName}</TableCell>
|
|
||||||
<TableCell monospace>
|
<TableCell monospace>
|
||||||
<AppLink config={config} name={name} version={version} text={version} />
|
<AppLink config={config} name={name} version={version} text={version} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{displayName}
|
||||||
|
</TableCell>
|
||||||
<TableCell>{moment.utc(createTime).fromNow()}</TableCell>
|
<TableCell>{moment.utc(createTime).fromNow()}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<BooleanIcon yes={localRefs && localRefs.has(hash)} />
|
<BooleanIcon yes={localRefs && localRefs.has(hash)} />
|
||||||
|
@ -37,9 +37,9 @@ const BotRecords = () => {
|
|||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell onClick={sortBy('name')}>Identifier</TableCell>
|
<TableCell onClick={sortBy('name')}>Identifier</TableCell>
|
||||||
<TableCell onClick={sortBy('version')} size='small'>Version</TableCell>
|
<TableCell onClick={sortBy('version')} size='small'>Version</TableCell>
|
||||||
<TableCell onClick={sortBy('createTime')} size='small'>Created</TableCell>
|
|
||||||
<TableCell onClick={sortBy('attributes.displayName')}>Name</TableCell>
|
<TableCell onClick={sortBy('attributes.displayName')}>Name</TableCell>
|
||||||
<TableCell />
|
<TableCell onClick={sortBy('createTime')} size='small'>Created</TableCell>
|
||||||
|
<TableCell size='icon' />
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
@ -48,8 +48,8 @@ const BotRecords = () => {
|
|||||||
<TableRow key={id} size='small'>
|
<TableRow key={id} size='small'>
|
||||||
<TableCell monospace>{name}</TableCell>
|
<TableCell monospace>{name}</TableCell>
|
||||||
<TableCell monospace>{version}</TableCell>
|
<TableCell monospace>{version}</TableCell>
|
||||||
<TableCell>{moment.utc(createTime).fromNow()}</TableCell>
|
|
||||||
<TableCell>{displayName}</TableCell>
|
<TableCell>{displayName}</TableCell>
|
||||||
|
<TableCell>{moment.utc(createTime).fromNow()}</TableCell>
|
||||||
<TableCell />
|
<TableCell />
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
|
@ -13,9 +13,12 @@ import Panel from '../../../components/Panel';
|
|||||||
import Toolbar from '../../../components/Toolbar';
|
import Toolbar from '../../../components/Toolbar';
|
||||||
|
|
||||||
import LogPoller from '../../../components/LogPoller';
|
import LogPoller from '../../../components/LogPoller';
|
||||||
|
|
||||||
|
import IPFSNetwork from './IPFSNetwork';
|
||||||
import IPFSStatus from './IPFSStatus';
|
import IPFSStatus from './IPFSStatus';
|
||||||
|
|
||||||
const TAB_STATUS = 'status';
|
const TAB_STATUS = 'status';
|
||||||
|
const TAB_NETWORK = 'network';
|
||||||
const TAB_LOG = 'log';
|
const TAB_LOG = 'log';
|
||||||
const TAB_SWARM_LOG = 'swarm';
|
const TAB_SWARM_LOG = 'swarm';
|
||||||
|
|
||||||
@ -47,6 +50,7 @@ const IPFS = () => {
|
|||||||
<Toolbar>
|
<Toolbar>
|
||||||
<Tabs value={tab} onChange={(_, value) => setTab(value)}>
|
<Tabs value={tab} onChange={(_, value) => setTab(value)}>
|
||||||
<Tab value={TAB_STATUS} label='Status' />
|
<Tab value={TAB_STATUS} label='Status' />
|
||||||
|
<Tab value={TAB_NETWORK} label='Network' />
|
||||||
<Tab value={TAB_LOG} label='Log' />
|
<Tab value={TAB_LOG} label='Log' />
|
||||||
<Tab value={TAB_SWARM_LOG} label='Connection Log' />
|
<Tab value={TAB_SWARM_LOG} label='Connection Log' />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
@ -55,23 +59,27 @@ const IPFS = () => {
|
|||||||
>
|
>
|
||||||
<TabContext value={tab}>
|
<TabContext value={tab}>
|
||||||
{tab === TAB_STATUS && (
|
{tab === TAB_STATUS && (
|
||||||
<div className={classes.panel}>
|
<Paper className={classes.paper}>
|
||||||
<Paper className={classes.paper}>
|
<IPFSStatus />
|
||||||
<IPFSStatus />
|
</Paper>
|
||||||
</Paper>
|
)}
|
||||||
</div>
|
|
||||||
|
{tab === TAB_NETWORK && (
|
||||||
|
<Paper className={classes.paper}>
|
||||||
|
<IPFSNetwork />
|
||||||
|
</Paper>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{tab === TAB_LOG && (
|
{tab === TAB_LOG && (
|
||||||
<div className={classes.panel}>
|
<Paper className={classes.paper}>
|
||||||
<LogPoller service='ipfs' />
|
<LogPoller service='ipfs' />
|
||||||
</div>
|
</Paper>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{tab === TAB_SWARM_LOG && (
|
{tab === TAB_SWARM_LOG && (
|
||||||
<div className={classes.panel}>
|
<Paper className={classes.paper}>
|
||||||
<LogPoller service='ipfs-swarm-connect' />
|
<LogPoller service='ipfs-swarm-connect' />
|
||||||
</div>
|
</Paper>
|
||||||
)}
|
)}
|
||||||
</TabContext>
|
</TabContext>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
142
packages/console-app/src/containers/panels/ipfs/IPFSNetwork.js
Normal file
142
packages/console-app/src/containers/panels/ipfs/IPFSNetwork.js
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2020 DXOS.org
|
||||||
|
//
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import get from 'lodash.get';
|
||||||
|
|
||||||
|
import { useQuery } from '@apollo/react-hooks';
|
||||||
|
import { makeStyles } from '@material-ui/core';
|
||||||
|
import TableBody from '@material-ui/core/TableBody';
|
||||||
|
import TableHead from '@material-ui/core/TableHead';
|
||||||
|
import TableRow from '@material-ui/core/TableRow';
|
||||||
|
|
||||||
|
import IPFS_STATUS from '../../../gql/ipfs_status.graphql';
|
||||||
|
import WNS_RECORDS from '../../../gql/wns_records.graphql';
|
||||||
|
|
||||||
|
import { useQueryStatusReducer } from '../../../hooks';
|
||||||
|
|
||||||
|
import Table from '../../../components/Table';
|
||||||
|
import TableCell from '../../../components/TableCell';
|
||||||
|
import { BooleanIcon } from '../../../components/BooleanIcon';
|
||||||
|
|
||||||
|
const RECORD_TYPE = 'wrn:service';
|
||||||
|
const SERVICE_TYPE = 'ipfs';
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
tableContainer: {
|
||||||
|
flex: 1,
|
||||||
|
overflowY: 'scroll'
|
||||||
|
},
|
||||||
|
|
||||||
|
table: {
|
||||||
|
tableLayout: 'fixed',
|
||||||
|
|
||||||
|
'& th': {
|
||||||
|
fontVariant: 'all-small-caps',
|
||||||
|
fontSize: 18,
|
||||||
|
cursor: 'ns-resize'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
connected: {
|
||||||
|
fontWeight: 'bold'
|
||||||
|
},
|
||||||
|
|
||||||
|
disconnected: {
|
||||||
|
fontStyle: 'italic'
|
||||||
|
},
|
||||||
|
|
||||||
|
colShort: {
|
||||||
|
width: '30%'
|
||||||
|
},
|
||||||
|
|
||||||
|
colWide: {},
|
||||||
|
|
||||||
|
colBoolean: {
|
||||||
|
width: '10%'
|
||||||
|
},
|
||||||
|
|
||||||
|
caption: {
|
||||||
|
backgroundColor: theme.palette.primary[500],
|
||||||
|
color: theme.palette.primary.contrastText,
|
||||||
|
paddingLeft: '1em',
|
||||||
|
margin: 0
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
const IPFSStatus = () => {
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
|
const ipfsResponse = useQueryStatusReducer(useQuery(IPFS_STATUS));
|
||||||
|
const wnsResponse = useQueryStatusReducer(useQuery(WNS_RECORDS, {
|
||||||
|
variables: { attributes: { type: RECORD_TYPE, service: SERVICE_TYPE } }
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (!wnsResponse || !ipfsResponse) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ipfsData = JSON.parse(ipfsResponse.ipfs_status.json);
|
||||||
|
const registeredServers = JSON.parse(wnsResponse.wns_records.json);
|
||||||
|
|
||||||
|
const displayServers = registeredServers.map((service) => {
|
||||||
|
const addresses = get(service, 'attributes.ipfs.addresses');
|
||||||
|
let connected = false;
|
||||||
|
for (const address of addresses) {
|
||||||
|
const parts = address.split('/');
|
||||||
|
const nodeId = parts[parts.length - 1];
|
||||||
|
connected = !!ipfsData.swarm.peers.find(({ peer }) => peer === nodeId);
|
||||||
|
if (connected) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: get(service, 'name'),
|
||||||
|
version: get(service, 'version'),
|
||||||
|
description: get(service, 'attributes.description'),
|
||||||
|
ipfs: get(service, 'attributes.ipfs'),
|
||||||
|
connected
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
displayServers.sort((a, b) => {
|
||||||
|
return a.connected && !b.connected ? -1 : b.connected && !a.connected ? 1 : b.name < a.name ? 1 : -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (displayServers.length === 0) {
|
||||||
|
displayServers.push({ name: 'None' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(burdon): Get Address (currenlty truncated).
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table stickyHeader size='small' className={classes.table}>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Identifier</TableCell>
|
||||||
|
<TableCell size='medium'>Description</TableCell>
|
||||||
|
<TableCell>Address</TableCell>
|
||||||
|
<TableCell size='small'>Connected</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{displayServers.map(({ name, description, ipfs, connected }) => (
|
||||||
|
<TableRow key={name}>
|
||||||
|
<TableCell>{name}</TableCell>
|
||||||
|
<TableCell>{description}</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{ipfs.addresses}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<BooleanIcon yes={connected} />
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default IPFSStatus;
|
@ -3,13 +3,8 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import get from 'lodash.get';
|
|
||||||
|
|
||||||
import { useQuery } from '@apollo/react-hooks';
|
import { useQuery } from '@apollo/react-hooks';
|
||||||
import { makeStyles } from '@material-ui/core';
|
|
||||||
import TableBody from '@material-ui/core/TableBody';
|
|
||||||
import TableHead from '@material-ui/core/TableHead';
|
|
||||||
import TableRow from '@material-ui/core/TableRow';
|
|
||||||
|
|
||||||
import IPFS_STATUS from '../../../gql/ipfs_status.graphql';
|
import IPFS_STATUS from '../../../gql/ipfs_status.graphql';
|
||||||
import WNS_RECORDS from '../../../gql/wns_records.graphql';
|
import WNS_RECORDS from '../../../gql/wns_records.graphql';
|
||||||
@ -17,59 +12,11 @@ import WNS_RECORDS from '../../../gql/wns_records.graphql';
|
|||||||
import { useQueryStatusReducer } from '../../../hooks';
|
import { useQueryStatusReducer } from '../../../hooks';
|
||||||
|
|
||||||
import Json from '../../../components/Json';
|
import Json from '../../../components/Json';
|
||||||
import Panel from '../../../components/Panel';
|
|
||||||
import Table from '../../../components/Table';
|
|
||||||
import TableCell from '../../../components/TableCell';
|
|
||||||
import { BooleanIcon } from '../../../components/BooleanIcon';
|
|
||||||
|
|
||||||
const RECORD_TYPE = 'wrn:service';
|
const RECORD_TYPE = 'wrn:service';
|
||||||
const SERVICE_TYPE = 'ipfs';
|
const SERVICE_TYPE = 'ipfs';
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
|
||||||
tableContainer: {
|
|
||||||
flex: 1,
|
|
||||||
overflowY: 'scroll'
|
|
||||||
},
|
|
||||||
|
|
||||||
table: {
|
|
||||||
tableLayout: 'fixed',
|
|
||||||
|
|
||||||
'& th': {
|
|
||||||
fontVariant: 'all-small-caps',
|
|
||||||
fontSize: 18,
|
|
||||||
cursor: 'ns-resize'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
connected: {
|
|
||||||
fontWeight: 'bold'
|
|
||||||
},
|
|
||||||
|
|
||||||
disconnected: {
|
|
||||||
fontStyle: 'italic'
|
|
||||||
},
|
|
||||||
|
|
||||||
colShort: {
|
|
||||||
width: '30%'
|
|
||||||
},
|
|
||||||
|
|
||||||
colWide: {},
|
|
||||||
|
|
||||||
colBoolean: {
|
|
||||||
width: '10%'
|
|
||||||
},
|
|
||||||
|
|
||||||
caption: {
|
|
||||||
backgroundColor: theme.palette.primary[500],
|
|
||||||
color: theme.palette.primary.contrastText,
|
|
||||||
paddingLeft: '1em',
|
|
||||||
margin: 0
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
const IPFSStatus = () => {
|
const IPFSStatus = () => {
|
||||||
const classes = useStyles();
|
|
||||||
|
|
||||||
const ipfsResponse = useQueryStatusReducer(useQuery(IPFS_STATUS));
|
const ipfsResponse = useQueryStatusReducer(useQuery(IPFS_STATUS));
|
||||||
const wnsResponse = useQueryStatusReducer(useQuery(WNS_RECORDS, {
|
const wnsResponse = useQueryStatusReducer(useQuery(WNS_RECORDS, {
|
||||||
variables: { attributes: { type: RECORD_TYPE, service: SERVICE_TYPE } }
|
variables: { attributes: { type: RECORD_TYPE, service: SERVICE_TYPE } }
|
||||||
@ -80,77 +27,21 @@ const IPFSStatus = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ipfsData = JSON.parse(ipfsResponse.ipfs_status.json);
|
const ipfsData = JSON.parse(ipfsResponse.ipfs_status.json);
|
||||||
const registeredServers = JSON.parse(wnsResponse.wns_records.json);
|
const data = {
|
||||||
|
id: ipfsData.id.id,
|
||||||
const displayServers = registeredServers.map((service) => {
|
version: ipfsData.id.agentVersion,
|
||||||
console.error(service);
|
addresses: ipfsData.id.addresses,
|
||||||
const addresses = get(service, 'attributes.ipfs.addresses');
|
swarm: {
|
||||||
let connected = false;
|
peers: ipfsData.swarm.peers.length
|
||||||
for (const address of addresses) {
|
},
|
||||||
const parts = address.split('/');
|
repo: {
|
||||||
const nodeId = parts[parts.length - 1];
|
numObjects: ipfsData.repo.stats.numObjects,
|
||||||
connected = !!ipfsData.swarm.peers.find(({ peer }) => peer === nodeId);
|
repoSize: ipfsData.repo.stats.repoSize
|
||||||
if (connected) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
return {
|
|
||||||
name: get(service, 'name'),
|
|
||||||
version: get(service, 'version'),
|
|
||||||
description: get(service, 'attributes.description'),
|
|
||||||
ipfs: get(service, 'attributes.ipfs'),
|
|
||||||
connected
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
displayServers.sort((a, b) => {
|
|
||||||
return a.connected && !b.connected ? -1 : b.connected && !a.connected ? 1 : b.name < a.name ? 1 : -1;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (displayServers.length === 0) {
|
|
||||||
displayServers.push({ name: 'None' });
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Panel>
|
<Json data={data} />
|
||||||
<h4 className={classes.caption}>WNS-registered IPFS Servers</h4>
|
|
||||||
<Table stickyHeader size='small' className={classes.table}>
|
|
||||||
<TableHead>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell>Identifier</TableCell>
|
|
||||||
<TableCell size='medium'>Description</TableCell>
|
|
||||||
<TableCell size='icon'>Connected</TableCell>
|
|
||||||
<TableCell>Address</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
</TableHead>
|
|
||||||
<TableBody>
|
|
||||||
{displayServers.map(({ name, description, ipfs, connected }) => (
|
|
||||||
<TableRow key={name}>
|
|
||||||
<TableCell>{name}</TableCell>
|
|
||||||
<TableCell>{description}</TableCell>
|
|
||||||
<TableCell>
|
|
||||||
<BooleanIcon yes={connected} />
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>
|
|
||||||
{ipfs.addresses}
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
|
|
||||||
<h4 className={classes.caption}>Local IPFS Server</h4>
|
|
||||||
<Json data={{
|
|
||||||
id: ipfsData.id.id,
|
|
||||||
version: ipfsData.id.agentVersion,
|
|
||||||
addresses: ipfsData.id.addresses,
|
|
||||||
peers: ipfsData.swarm.peers.length,
|
|
||||||
numObjects: ipfsData.repo.stats.numObjects,
|
|
||||||
repoSize: ipfsData.repo.stats.repoSize
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Panel>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2020 DXOS.org
|
||||||
|
//
|
||||||
|
|
||||||
|
import React, { useContext } from 'react';
|
||||||
|
import { useQuery } from '@apollo/react-hooks';
|
||||||
|
|
||||||
|
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 Table from '../../../components/Table';
|
||||||
|
import TableCell from '../../../components/TableCell';
|
||||||
|
|
||||||
|
import SIGNAL_STATUS from '../../../gql/signal_status.graphql';
|
||||||
|
|
||||||
|
import { ConsoleContext, useQueryStatusReducer } from '../../../hooks';
|
||||||
|
|
||||||
|
const SignalServers = () => {
|
||||||
|
const { config } = useContext(ConsoleContext);
|
||||||
|
const data = useQueryStatusReducer(useQuery(SIGNAL_STATUS, { pollInterval: config.api.intervalQuery }));
|
||||||
|
if (!data) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { json: { channels = [] } } = data.signal_status;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableContainer>
|
||||||
|
<Table>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Server</TableCell>
|
||||||
|
<TableCell size='small' s>Peers</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{channels.map(({ channel, peers = [] }) => {
|
||||||
|
return (
|
||||||
|
<TableRow key={channel} size='small'>
|
||||||
|
<TableCell monospace>
|
||||||
|
{channel}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell monospace>
|
||||||
|
{peers.length}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SignalServers;
|
@ -1,26 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2020 DXOS.org
|
|
||||||
//
|
|
||||||
|
|
||||||
import React, { useContext } from 'react';
|
|
||||||
import { useQuery } from '@apollo/react-hooks';
|
|
||||||
|
|
||||||
import SIGNAL_STATUS from '../../../gql/signal_status.graphql';
|
|
||||||
|
|
||||||
import { ConsoleContext, useQueryStatusReducer } from '../../../hooks';
|
|
||||||
|
|
||||||
import Json from '../../../components/Json';
|
|
||||||
|
|
||||||
const SignalStatus = () => {
|
|
||||||
const { config } = useContext(ConsoleContext);
|
|
||||||
const data = useQueryStatusReducer(useQuery(SIGNAL_STATUS, { pollInterval: config.api.intervalQuery }));
|
|
||||||
if (!data) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Json data={data.signal_status.json} />
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SignalStatus;
|
|
@ -11,10 +11,10 @@ import TabContext from '@material-ui/lab/TabContext';
|
|||||||
|
|
||||||
import Panel from '../../../components/Panel';
|
import Panel from '../../../components/Panel';
|
||||||
import Toolbar from '../../../components/Toolbar';
|
import Toolbar from '../../../components/Toolbar';
|
||||||
|
|
||||||
import SignalStatus from './SignalStatus';
|
|
||||||
import LogPoller from '../../../components/LogPoller';
|
import LogPoller from '../../../components/LogPoller';
|
||||||
|
|
||||||
|
import SignalServers from './SignalServers';
|
||||||
|
|
||||||
const TAB_STATUS = 'status';
|
const TAB_STATUS = 'status';
|
||||||
const TAB_LOG = 'log';
|
const TAB_LOG = 'log';
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ const Signal = () => {
|
|||||||
{tab === TAB_STATUS && (
|
{tab === TAB_STATUS && (
|
||||||
<div className={classes.panel}>
|
<div className={classes.panel}>
|
||||||
<Paper className={classes.paper}>
|
<Paper className={classes.paper}>
|
||||||
<SignalStatus />
|
<SignalServers />
|
||||||
</Paper>
|
</Paper>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2020 DXOS.org
|
||||||
|
//
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
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 Table from '../../../components/Table';
|
||||||
|
import TableCell from '../../../components/TableCell';
|
||||||
|
import { useSorter } from '../../../hooks';
|
||||||
|
|
||||||
|
const format = (value, unit, symbol = '') => Math.floor(value / unit).toLocaleString() + symbol;
|
||||||
|
|
||||||
|
const SignalServers = ({ services }) => {
|
||||||
|
const [sorter] = useSorter('name');
|
||||||
|
|
||||||
|
const total = services.reduce((value, { memory }) => value + memory, 0);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableContainer>
|
||||||
|
<Table>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Service</TableCell>
|
||||||
|
<TableCell style={{ width: 120 }}>Status</TableCell>
|
||||||
|
<TableCell style={{ width: 140, textAlign: 'right' }}>Memory</TableCell>
|
||||||
|
<TableCell style={{ width: 120, textAlign: 'right' }}>CPU</TableCell>
|
||||||
|
<TableCell>Command</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{services.sort(sorter).map(({ name, memory, cpu, status, exec }) => {
|
||||||
|
return (
|
||||||
|
<TableRow key={name} size='small'>
|
||||||
|
<TableCell monospace>
|
||||||
|
{name}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{status}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell style={{ textAlign: 'right' }} monospace>
|
||||||
|
{format(memory, 1000, 'K')}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell style={{ textAlign: 'right' }} monospace>
|
||||||
|
{cpu.toFixed(1)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell monospace>
|
||||||
|
{exec}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
<TableBody>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell style={{ fontVariant: 'all-small-caps' }}>Total</TableCell>
|
||||||
|
<TableCell />
|
||||||
|
<TableCell style={{ textAlign: 'right' }} monospace>
|
||||||
|
{format(total, 1000, 'K')}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell />
|
||||||
|
<TableCell />
|
||||||
|
</TableRow>
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SignalServers;
|
@ -9,17 +9,19 @@ import Tab from '@material-ui/core/Tab';
|
|||||||
import Tabs from '@material-ui/core/Tabs';
|
import Tabs from '@material-ui/core/Tabs';
|
||||||
import TabContext from '@material-ui/lab/TabContext';
|
import TabContext from '@material-ui/lab/TabContext';
|
||||||
|
|
||||||
import Json from '../../components/Json';
|
import Json from '../../../components/Json';
|
||||||
|
|
||||||
import SERVICE_STATUS from '../../gql/service_status.graphql';
|
import SERVICE_STATUS from '../../../gql/service_status.graphql';
|
||||||
import SYSTEM_STATUS from '../../gql/system_status.graphql';
|
import SYSTEM_STATUS from '../../../gql/system_status.graphql';
|
||||||
|
|
||||||
import { ConsoleContext, useQueryStatusReducer } from '../../hooks';
|
import { ConsoleContext, useQueryStatusReducer } from '../../../hooks';
|
||||||
|
|
||||||
import Panel from '../../components/Panel';
|
import Panel from '../../../components/Panel';
|
||||||
import Toolbar from '../../components/Toolbar';
|
import Toolbar from '../../../components/Toolbar';
|
||||||
|
|
||||||
const TAB_SYSTEM = 'system';
|
import Services from './Services';
|
||||||
|
|
||||||
|
const TAB_INFO = 'status';
|
||||||
const TAB_SERVICES = 'services';
|
const TAB_SERVICES = 'services';
|
||||||
|
|
||||||
const useStyles = makeStyles(() => ({
|
const useStyles = makeStyles(() => ({
|
||||||
@ -43,37 +45,37 @@ const useStyles = makeStyles(() => ({
|
|||||||
const Status = () => {
|
const Status = () => {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const { config } = useContext(ConsoleContext);
|
const { config } = useContext(ConsoleContext);
|
||||||
const [tab, setTab] = useState(TAB_SYSTEM);
|
const [tab, setTab] = useState(TAB_SERVICES);
|
||||||
const systemResponse = useQueryStatusReducer(useQuery(SYSTEM_STATUS, { pollInterval: config.api.intervalQuery }));
|
const systemResponse = useQueryStatusReducer(useQuery(SYSTEM_STATUS, { pollInterval: config.api.intervalQuery }));
|
||||||
const serviceResponse = useQueryStatusReducer(useQuery(SERVICE_STATUS, { pollInterval: config.api.intervalQuery }));
|
const serviceResponse = useQueryStatusReducer(useQuery(SERVICE_STATUS, { pollInterval: config.api.intervalQuery }));
|
||||||
if (!systemResponse || !serviceResponse) {
|
if (!systemResponse || !serviceResponse) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const systemData = JSON.parse(systemResponse.system_status.json);
|
const status = JSON.parse(systemResponse.system_status.json);
|
||||||
const serviceData = JSON.parse(serviceResponse.service_status.json);
|
const services = JSON.parse(serviceResponse.service_status.json);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Panel
|
<Panel
|
||||||
toolbar={
|
toolbar={
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<Tabs value={tab} onChange={(_, value) => setTab(value)}>
|
<Tabs value={tab} onChange={(_, value) => setTab(value)}>
|
||||||
<Tab value={TAB_SYSTEM} label='System' />
|
|
||||||
<Tab value={TAB_SERVICES} label='Services' />
|
<Tab value={TAB_SERVICES} label='Services' />
|
||||||
|
<Tab value={TAB_INFO} label='Info' />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<TabContext value={tab}>
|
<TabContext value={tab}>
|
||||||
{tab === TAB_SYSTEM && (
|
{tab === TAB_SERVICES && (
|
||||||
<div className={classes.panel}>
|
<div className={classes.panel}>
|
||||||
<Json data={systemData} />
|
<Services services={services} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{tab === TAB_SERVICES && (
|
{tab === TAB_INFO && (
|
||||||
<div className={classes.panel}>
|
<div className={classes.panel}>
|
||||||
<Json data={serviceData} />
|
<Json data={status} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</TabContext>
|
</TabContext>
|
@ -83,13 +83,13 @@ const WNSRecords = ({ type }) => {
|
|||||||
<Table>
|
<Table>
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell onClick={sortBy('type')} size='small'>Type</TableCell>
|
<TableCell onClick={sortBy('type')} size='medium'>Type</TableCell>
|
||||||
<TableCell onClick={sortBy('name')}>Identifier</TableCell>
|
<TableCell onClick={sortBy('name')}>Identifier</TableCell>
|
||||||
<TableCell size='icon'>GraphQL</TableCell>
|
|
||||||
<TableCell onClick={sortBy('attributes.displayName')}>Name</TableCell>
|
|
||||||
<TableCell onClick={sortBy('version')} size='small'>Version</TableCell>
|
<TableCell onClick={sortBy('version')} size='small'>Version</TableCell>
|
||||||
|
<TableCell onClick={sortBy('attributes.displayName')}>Name</TableCell>
|
||||||
<TableCell onClick={sortBy('createTime')} size='small'>Created</TableCell>
|
<TableCell onClick={sortBy('createTime')} size='small'>Created</TableCell>
|
||||||
<TableCell onClick={sortBy('package')} size='small'>Package</TableCell>
|
<TableCell onClick={sortBy('package')}>Package</TableCell>
|
||||||
|
<TableCell size='icon' />
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
@ -116,17 +116,21 @@ const WNSRecords = ({ type }) => {
|
|||||||
<TableCell monospace>
|
<TableCell monospace>
|
||||||
{appLink || name}
|
{appLink || name}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
|
||||||
<QueryLink config={config} name={name} icon />
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>{displayName || service || description}</TableCell>
|
|
||||||
<TableCell monospace>
|
<TableCell monospace>
|
||||||
{verLink || version}
|
{verLink || version}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>{moment.utc(createTime).fromNow()}</TableCell>
|
<TableCell>
|
||||||
|
{displayName || service || description}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{moment.utc(createTime).fromNow()}
|
||||||
|
</TableCell>
|
||||||
<TableCell monospace>
|
<TableCell monospace>
|
||||||
{pkgLink}
|
{pkgLink}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<QueryLink config={config} name={name} icon />
|
||||||
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import get from 'lodash.get';
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
// TODO(burdon): Enable multiple sort order (e.g., id, version).
|
// TODO(burdon): Enable multiple sort order (e.g., id, version).
|
||||||
export const useSorter = (initSort, initAscend) => {
|
export const useSorter = (initSort, initAscend = true) => {
|
||||||
const [{ sort, ascend }, setSort] = useState({ sort: initSort, ascend: initAscend });
|
const [{ sort, ascend }, setSort] = useState({ sort: initSort, ascend: initAscend });
|
||||||
|
|
||||||
const sorter = (item1, item2) => {
|
const sorter = (item1, item2) => {
|
||||||
|
@ -14,8 +14,8 @@ import ServicesIcon from '@material-ui/icons/Storage';
|
|||||||
export default {
|
export default {
|
||||||
services: [
|
services: [
|
||||||
{
|
{
|
||||||
path: '/status',
|
path: '/system',
|
||||||
title: 'Status',
|
title: 'System',
|
||||||
icon: StatsIcon
|
icon: StatsIcon
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"build": {
|
"build": {
|
||||||
"name": "@dxos/console-app",
|
"name": "@dxos/console-app",
|
||||||
"buildDate": "2020-07-20T20:55:14.656Z",
|
"buildDate": "2020-07-20T22:54:23.462Z",
|
||||||
"version": "1.0.0-beta.17"
|
"version": "1.0.0-beta.19"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user