diff --git a/packages/console-app/src/components/Log.js b/packages/console-app/src/components/Log.js index 5b909cc..de5abd3 100644 --- a/packages/console-app/src/components/Log.js +++ b/packages/console-app/src/components/Log.js @@ -23,7 +23,7 @@ const useStyles = makeStyles(theme => ({ padding: theme.spacing(1), '& div': { - fontSize: 16, + fontSize: 14, fontFamily: 'monospace', whiteSpace: 'nowrap' } diff --git a/packages/console-app/src/components/QueryLink.js b/packages/console-app/src/components/QueryLink.js index 72ba73a..92199a4 100644 --- a/packages/console-app/src/components/QueryLink.js +++ b/packages/console-app/src/components/QueryLink.js @@ -4,37 +4,56 @@ import React from 'react'; -import ExitToApp from '@material-ui/icons/ExitToApp'; import Link from '@material-ui/core/Link'; +import LinkIcon from '@material-ui/icons/ExitToApp'; import { getServiceUrl } from '../util/config'; -const QUERY = `{ - queryRecords(attributes: [ - { key: "name", value: { string: "%NAME%" }}]) { - id type name bondId createTime expiryTime owners attributes { key, value { string, json } } - } -}`; +const QUERY = ` + query { + queryRecords(attributes: [{ key: "name", value: { string: "%NAME%" } }]) { + id + type + name + bondId + createTime + expiryTime + owners + attributes { + key + value { + string + json + } + } + } + } +`; /** * Render link to record in WNS. * @param {Object} config * @param {string} name * @param {string} [text] + * @param {boolean} icon */ const QueryLink = ({ config, name, text, icon = false }) => { const baseURL = getServiceUrl(config, 'wns.webui'); const query = QUERY.replace('%NAME%', name); + + // NOTE: Playground bug opens two tabs. const fullURL = encodeURI(`${baseURL}?query=${query}`); - if (icon) { - return ( - - - - ); - } - return {text || name}; + return ( + + {icon && ( + + )} + {!icon && ( + text || name + )} + + ); }; export default QueryLink; diff --git a/packages/console-app/src/components/Sidebar.js b/packages/console-app/src/components/Sidebar.js index e841479..fd81ea4 100644 --- a/packages/console-app/src/components/Sidebar.js +++ b/packages/console-app/src/components/Sidebar.js @@ -5,6 +5,7 @@ import clsx from 'clsx'; import React from 'react'; import { useHistory, useParams } from 'react-router'; + import { makeStyles } from '@material-ui/core'; import List from '@material-ui/core/List'; import ListItem from '@material-ui/core/ListItem'; @@ -17,7 +18,6 @@ const useStyles = makeStyles(theme => ({ flex: 1, flexDirection: 'column', justifyContent: 'space-between' - // backgroundColor: theme.palette.grey[100] }, list: { diff --git a/packages/console-app/src/components/TableCell.js b/packages/console-app/src/components/TableCell.js index 85ef475..1f4f199 100644 --- a/packages/console-app/src/components/TableCell.js +++ b/packages/console-app/src/components/TableCell.js @@ -9,18 +9,21 @@ import MuiTableCell from '@material-ui/core/TableCell'; import { makeStyles } from '@material-ui/core'; const useStyles = makeStyles(() => ({ + icon: { + width: 48 + }, small: { - width: 160 + width: 130 }, medium: { - width: 220 + width: 170 }, - icon: { - width: 120 + large: { + width: 400 } })); -const TableCell = ({ children, size, monospace = false, title, ...rest }) => { +const TableCell = ({ children, size, monospace = false, style, title, ...rest }) => { const classes = useStyles(); return ( @@ -31,8 +34,10 @@ const TableCell = ({ children, size, monospace = false, title, ...rest }) => { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', + verticalAlign: 'top', fontFamily: monospace ? 'monospace' : 'inherit', - fontSize: monospace ? 14 : 'inherit' + fontSize: monospace ? 14 : 13, + ...style }} title={title} > diff --git a/packages/console-app/src/containers/Main.js b/packages/console-app/src/containers/Main.js index e689724..86a1e1d 100644 --- a/packages/console-app/src/containers/Main.js +++ b/packages/console-app/src/containers/Main.js @@ -25,7 +25,7 @@ import Config from './panels/Config'; import IPFS from './panels/ipfs/IPFS'; import Metadata from './panels/Metadata'; import Signaling from './panels/signal/Signaling'; -import Status from './panels/Status'; +import System from './panels/system/Status'; import WNS from './panels/wns/WNS'; // Global error handler. @@ -52,7 +52,7 @@ const Main = ({ config }) => { - + diff --git a/packages/console-app/src/containers/panels/apps/AppRecords.js b/packages/console-app/src/containers/panels/apps/AppRecords.js index 820585b..581945f 100644 --- a/packages/console-app/src/containers/panels/apps/AppRecords.js +++ b/packages/console-app/src/containers/panels/apps/AppRecords.js @@ -30,14 +30,12 @@ const AppRecords = () => { // TODO(telackey): Does this also need an interval? const ipfsResponse = useQueryStatusReducer(useQuery(IPFS_STATUS)); - if (!appResponse || !ipfsResponse) { return null; } const appData = JSON.parse(appResponse.wns_records.json); const ipfsData = JSON.parse(ipfsResponse.ipfs_status.json); - const localRefs = new Set(ipfsData.refs.local); return ( @@ -45,23 +43,25 @@ const AppRecords = () => { Identifier - Name Version + Name Created - Downloaded + - {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 ( - {displayName} + + {displayName} + {moment.utc(createTime).fromNow()} diff --git a/packages/console-app/src/containers/panels/bots/BotRecords.js b/packages/console-app/src/containers/panels/bots/BotRecords.js index 258235a..4b07c66 100644 --- a/packages/console-app/src/containers/panels/bots/BotRecords.js +++ b/packages/console-app/src/containers/panels/bots/BotRecords.js @@ -37,9 +37,9 @@ const BotRecords = () => { Identifier Version - Created Name - + Created + @@ -48,8 +48,8 @@ const BotRecords = () => { {name} {version} - {moment.utc(createTime).fromNow()} {displayName} + {moment.utc(createTime).fromNow()} ); diff --git a/packages/console-app/src/containers/panels/ipfs/IPFS.js b/packages/console-app/src/containers/panels/ipfs/IPFS.js index 87c3ff3..23c169a 100644 --- a/packages/console-app/src/containers/panels/ipfs/IPFS.js +++ b/packages/console-app/src/containers/panels/ipfs/IPFS.js @@ -13,9 +13,12 @@ import Panel from '../../../components/Panel'; import Toolbar from '../../../components/Toolbar'; import LogPoller from '../../../components/LogPoller'; + +import IPFSNetwork from './IPFSNetwork'; import IPFSStatus from './IPFSStatus'; const TAB_STATUS = 'status'; +const TAB_NETWORK = 'network'; const TAB_LOG = 'log'; const TAB_SWARM_LOG = 'swarm'; @@ -47,6 +50,7 @@ const IPFS = () => { setTab(value)}> + @@ -55,23 +59,27 @@ const IPFS = () => { > {tab === TAB_STATUS && ( -
- - - -
+ + + + )} + + {tab === TAB_NETWORK && ( + + + )} {tab === TAB_LOG && ( -
+ -
+ )} {tab === TAB_SWARM_LOG && ( -
+ -
+ )}
diff --git a/packages/console-app/src/containers/panels/ipfs/IPFSNetwork.js b/packages/console-app/src/containers/panels/ipfs/IPFSNetwork.js new file mode 100644 index 0000000..a457c59 --- /dev/null +++ b/packages/console-app/src/containers/panels/ipfs/IPFSNetwork.js @@ -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 ( + + + + Identifier + Description + Address + Connected + + + + {displayServers.map(({ name, description, ipfs, connected }) => ( + + {name} + {description} + + {ipfs.addresses} + + + + + + ))} + +
+ ); +}; + +export default IPFSStatus; diff --git a/packages/console-app/src/containers/panels/ipfs/IPFSStatus.js b/packages/console-app/src/containers/panels/ipfs/IPFSStatus.js index cd9605b..2829e98 100644 --- a/packages/console-app/src/containers/panels/ipfs/IPFSStatus.js +++ b/packages/console-app/src/containers/panels/ipfs/IPFSStatus.js @@ -3,13 +3,8 @@ // 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'; @@ -17,59 +12,11 @@ import WNS_RECORDS from '../../../gql/wns_records.graphql'; import { useQueryStatusReducer } from '../../../hooks'; 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 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 } } @@ -80,77 +27,21 @@ const IPFSStatus = () => { } const ipfsData = JSON.parse(ipfsResponse.ipfs_status.json); - const registeredServers = JSON.parse(wnsResponse.wns_records.json); - - const displayServers = registeredServers.map((service) => { - console.error(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; - } + const data = { + id: ipfsData.id.id, + version: ipfsData.id.agentVersion, + addresses: ipfsData.id.addresses, + swarm: { + peers: ipfsData.swarm.peers.length + }, + repo: { + numObjects: ipfsData.repo.stats.numObjects, + repoSize: ipfsData.repo.stats.repoSize } - - 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 ( - -

WNS-registered IPFS Servers

- - - - Identifier - Description - Connected - Address - - - - {displayServers.map(({ name, description, ipfs, connected }) => ( - - {name} - {description} - - - - - {ipfs.addresses} - - - ))} - -
- -

Local IPFS Server

- -
+ ); }; diff --git a/packages/console-app/src/containers/panels/signal/SignalServers.js b/packages/console-app/src/containers/panels/signal/SignalServers.js new file mode 100644 index 0000000..f0eeca1 --- /dev/null +++ b/packages/console-app/src/containers/panels/signal/SignalServers.js @@ -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 ( + + + + + Server + Peers + + + + {channels.map(({ channel, peers = [] }) => { + return ( + + + {channel} + + + {peers.length} + + + ); + })} + +
+
+ ); +}; + +export default SignalServers; diff --git a/packages/console-app/src/containers/panels/signal/SignalStatus.js b/packages/console-app/src/containers/panels/signal/SignalStatus.js deleted file mode 100644 index 46792aa..0000000 --- a/packages/console-app/src/containers/panels/signal/SignalStatus.js +++ /dev/null @@ -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 ( - - ); -}; - -export default SignalStatus; diff --git a/packages/console-app/src/containers/panels/signal/Signaling.js b/packages/console-app/src/containers/panels/signal/Signaling.js index be7cec2..7a2542b 100644 --- a/packages/console-app/src/containers/panels/signal/Signaling.js +++ b/packages/console-app/src/containers/panels/signal/Signaling.js @@ -11,10 +11,10 @@ import TabContext from '@material-ui/lab/TabContext'; import Panel from '../../../components/Panel'; import Toolbar from '../../../components/Toolbar'; - -import SignalStatus from './SignalStatus'; import LogPoller from '../../../components/LogPoller'; +import SignalServers from './SignalServers'; + const TAB_STATUS = 'status'; const TAB_LOG = 'log'; @@ -55,7 +55,7 @@ const Signal = () => { {tab === TAB_STATUS && (
- +
)} diff --git a/packages/console-app/src/containers/panels/system/Services.js b/packages/console-app/src/containers/panels/system/Services.js new file mode 100644 index 0000000..dfe49e3 --- /dev/null +++ b/packages/console-app/src/containers/panels/system/Services.js @@ -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 ( + + + + + Service + Status + Memory + CPU + Command + + + + {services.sort(sorter).map(({ name, memory, cpu, status, exec }) => { + return ( + + + {name} + + + {status} + + + {format(memory, 1000, 'K')} + + + {cpu.toFixed(1)} + + + {exec} + + + ); + })} + + + + Total + + + {format(total, 1000, 'K')} + + + + + +
+
+ ); +}; + +export default SignalServers; diff --git a/packages/console-app/src/containers/panels/Status.js b/packages/console-app/src/containers/panels/system/Status.js similarity index 66% rename from packages/console-app/src/containers/panels/Status.js rename to packages/console-app/src/containers/panels/system/Status.js index c04cd90..e11a8c5 100644 --- a/packages/console-app/src/containers/panels/Status.js +++ b/packages/console-app/src/containers/panels/system/Status.js @@ -9,17 +9,19 @@ import Tab from '@material-ui/core/Tab'; import Tabs from '@material-ui/core/Tabs'; 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 SYSTEM_STATUS from '../../gql/system_status.graphql'; +import SERVICE_STATUS from '../../../gql/service_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 Toolbar from '../../components/Toolbar'; +import Panel from '../../../components/Panel'; +import Toolbar from '../../../components/Toolbar'; -const TAB_SYSTEM = 'system'; +import Services from './Services'; + +const TAB_INFO = 'status'; const TAB_SERVICES = 'services'; const useStyles = makeStyles(() => ({ @@ -43,37 +45,37 @@ const useStyles = makeStyles(() => ({ const Status = () => { const classes = useStyles(); 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 serviceResponse = useQueryStatusReducer(useQuery(SERVICE_STATUS, { pollInterval: config.api.intervalQuery })); if (!systemResponse || !serviceResponse) { return null; } - const systemData = JSON.parse(systemResponse.system_status.json); - const serviceData = JSON.parse(serviceResponse.service_status.json); + const status = JSON.parse(systemResponse.system_status.json); + const services = JSON.parse(serviceResponse.service_status.json); return ( setTab(value)}> - +
} > - {tab === TAB_SYSTEM && ( + {tab === TAB_SERVICES && (
- +
)} - {tab === TAB_SERVICES && ( + {tab === TAB_INFO && (
- +
)}
diff --git a/packages/console-app/src/containers/panels/wns/WNSRecords.js b/packages/console-app/src/containers/panels/wns/WNSRecords.js index 1b31a16..d13cdc9 100644 --- a/packages/console-app/src/containers/panels/wns/WNSRecords.js +++ b/packages/console-app/src/containers/panels/wns/WNSRecords.js @@ -83,13 +83,13 @@ const WNSRecords = ({ type }) => { - Type + Type Identifier - GraphQL - Name Version + Name Created - Package + Package + @@ -116,17 +116,21 @@ const WNSRecords = ({ type }) => { {appLink || name} - - - - {displayName || service || description} {verLink || version} - {moment.utc(createTime).fromNow()} + + {displayName || service || description} + + + {moment.utc(createTime).fromNow()} + {pkgLink} + + + ); } diff --git a/packages/console-app/src/hooks/sorter.js b/packages/console-app/src/hooks/sorter.js index 69f61bf..63889bc 100644 --- a/packages/console-app/src/hooks/sorter.js +++ b/packages/console-app/src/hooks/sorter.js @@ -6,7 +6,7 @@ import get from 'lodash.get'; import { useState } from 'react'; // 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 sorter = (item1, item2) => { diff --git a/packages/console-app/src/modules.js b/packages/console-app/src/modules.js index 95f7a85..54b38ef 100644 --- a/packages/console-app/src/modules.js +++ b/packages/console-app/src/modules.js @@ -14,8 +14,8 @@ import ServicesIcon from '@material-ui/icons/Storage'; export default { services: [ { - path: '/status', - title: 'Status', + path: '/system', + title: 'System', icon: StatsIcon }, { diff --git a/packages/console-app/src/version.json b/packages/console-app/src/version.json index 608d691..9226535 100644 --- a/packages/console-app/src/version.json +++ b/packages/console-app/src/version.json @@ -1,7 +1,7 @@ { "build": { "name": "@dxos/console-app", - "buildDate": "2020-07-20T20:55:14.656Z", - "version": "1.0.0-beta.17" + "buildDate": "2020-07-20T22:54:23.462Z", + "version": "1.0.0-beta.19" } }