From 9e4e58f1600e2dcda4cc75f24b5c68bca9b5973e Mon Sep 17 00:00:00 2001 From: Vojtech Simetka Date: Fri, 9 Apr 2021 15:09:17 +0200 Subject: [PATCH] feat: reviewed and simplified the node status check (#63) * refactor: status page nested ternary logic * refactor: move the fetch latest bee release to a hook * refactor: solved node status rerendering, improved performance and clarity * refactor: step components now use unified hooks interface * style: removed component margins, layout should be handled by pages --- src/components/EthereumAddressCard.tsx | 74 +++--- src/hooks/apiHooks.tsx | 29 ++ src/hooks/status.ts | 73 ++++++ src/pages/accounting/index.tsx | 46 ++-- src/pages/status/NodeSetupWorkflow.tsx | 248 ++++++------------ .../SetupSteps/ChequebookDeployFund.tsx | 55 ++-- .../SetupSteps/DebugConnectionCheck.tsx | 167 ++++++------ .../SetupSteps/EthereumConnectionCheck.tsx | 80 +++--- .../status/SetupSteps/NodeConnectionCheck.tsx | 30 +-- .../status/SetupSteps/PeerConnection.tsx | 82 +++--- src/pages/status/SetupSteps/VersionCheck.tsx | 133 +++++----- src/pages/status/StatusCard.tsx | 202 ++++++-------- src/pages/status/index.tsx | 180 ++++--------- src/react-app-env.d.ts | 21 ++ 14 files changed, 656 insertions(+), 764 deletions(-) create mode 100644 src/hooks/status.ts diff --git a/src/components/EthereumAddressCard.tsx b/src/components/EthereumAddressCard.tsx index 6e0f7e0..1ca5a08 100644 --- a/src/components/EthereumAddressCard.tsx +++ b/src/components/EthereumAddressCard.tsx @@ -12,7 +12,9 @@ const useStyles = makeStyles(() => createStyles({ root: { display: 'flex', - marginTop: '20px', + alignItems: 'center', + justifyContent: 'space-around', + flexWrap: 'wrap', }, details: { display: 'flex', @@ -21,10 +23,6 @@ const useStyles = makeStyles(() => content: { flex: '1 0 auto', }, - status: { - color: '#fff', - backgroundColor: '#76a9fa', - }, }), ) @@ -39,40 +37,38 @@ function EthereumAddressCard(props: Props): ReactElement { const classes = useStyles() return ( -
- - {props.isLoadingNodeAddresses ? ( -
- - -
- ) : ( -
- - - Ethereum Address - - - -
- )} - {props.isLoadingChequebookAddress ? ( -
- - -
- ) : ( -
- - - Chequebook Contract Address - - - -
- )} -
-
+ + {props.isLoadingNodeAddresses ? ( +
+ + +
+ ) : ( +
+ + + Ethereum Address + + + +
+ )} + {props.isLoadingChequebookAddress ? ( +
+ + +
+ ) : ( +
+ + + Chequebook Contract Address + + + +
+ )} +
) } diff --git a/src/hooks/apiHooks.tsx b/src/hooks/apiHooks.tsx index d6adb4e..7c27d23 100644 --- a/src/hooks/apiHooks.tsx +++ b/src/hooks/apiHooks.tsx @@ -15,6 +15,7 @@ import { } from '@ethersphere/bee-js' import { beeDebugApi, beeApi } from '../services/bee' +import axios from 'axios' export interface HealthHook { health: boolean @@ -361,3 +362,31 @@ export const useApiPeerLastCashout = (peerId: string): PeerLastCashoutHook => { return { peerCashout, isLoadingPeerCashout, error } } + +export interface LatestBeeReleaseHook { + latestBeeRelease: LatestBeeRelease | null + isLoadingLatestBeeRelease: boolean + error: Error | null +} + +export const useLatestBeeRelease = (): LatestBeeReleaseHook => { + const [latestBeeRelease, setLatestBeeRelease] = useState(null) + const [isLoadingLatestBeeRelease, setLoading] = useState(false) + const [error, setError] = useState(null) + + useEffect(() => { + axios + .get(`${process.env.REACT_APP_BEE_GITHUB_REPO_URL}/releases/latest`) + .then(res => { + setLatestBeeRelease(res.data) + }) + .catch((error: Error) => { + setError(error) + }) + .finally(() => { + setLoading(false) + }) + }, []) + + return { latestBeeRelease, isLoadingLatestBeeRelease, error } +} diff --git a/src/hooks/status.ts b/src/hooks/status.ts new file mode 100644 index 0000000..26ac6ce --- /dev/null +++ b/src/hooks/status.ts @@ -0,0 +1,73 @@ +import { + useApiChequebookAddress, + useApiChequebookBalance, + useApiHealth, + useApiNodeAddresses, + useApiNodeTopology, + useDebugApiHealth, + useLatestBeeRelease, +} from './apiHooks' + +export const useStatusNodeVersion = (): StatusNodeVersionHook => { + const { latestBeeRelease, isLoadingLatestBeeRelease } = useLatestBeeRelease() + const { nodeHealth, isLoadingNodeHealth } = useDebugApiHealth() + + return { + isLoading: isLoadingNodeHealth || isLoadingLatestBeeRelease, + isOk: Boolean(latestBeeRelease && latestBeeRelease.name === `v${nodeHealth?.version?.split('-')[0]}`), + userVersion: nodeHealth?.version?.split('-')[0] || '-', + latestVersion: latestBeeRelease?.name.substring(1) || '-', + latestUrl: latestBeeRelease?.html_url || 'https://github.com/ethersphere/bee/releases/latest', + } +} + +export const useStatusEthereumConnection = (): StatusEthereumConnectionHook => { + const { isLoadingNodeAddresses, nodeAddresses } = useApiNodeAddresses() + + return { + isLoading: isLoadingNodeAddresses, + isOk: Boolean(nodeAddresses?.ethereum), + nodeAddresses, + } +} + +export const useStatusDebugConnection = (): StatusHookCommon => { + const { isLoadingNodeHealth, nodeHealth } = useDebugApiHealth() + + return { + isLoading: isLoadingNodeHealth, + isOk: Boolean(nodeHealth?.status === 'ok'), + } +} + +export const useStatusConnection = (): StatusHookCommon => { + const { isLoadingHealth, health } = useApiHealth() + + return { + isLoading: isLoadingHealth, + isOk: health, + } +} + +export const useStatusTopology = (): StatusTopologyHook => { + const { topology, isLoading } = useApiNodeTopology() + + return { + isLoading, + isOk: Boolean(topology?.connected && topology?.connected > 0), + topology, + } +} + +export const useStatusChequebook = (): StatusChequebookHook => { + const { chequebookAddress, isLoadingChequebookAddress } = useApiChequebookAddress() + const { chequebookBalance, isLoadingChequebookBalance } = useApiChequebookBalance() + + return { + isLoading: isLoadingChequebookAddress || isLoadingChequebookBalance, + isOk: + Boolean(chequebookAddress?.chequebookaddress) && chequebookBalance !== null && chequebookBalance.totalBalance > 0, + chequebookBalance, + chequebookAddress, + } +} diff --git a/src/pages/accounting/index.tsx b/src/pages/accounting/index.tsx index c516217..5f32c8c 100644 --- a/src/pages/accounting/index.tsx +++ b/src/pages/accounting/index.tsx @@ -1,5 +1,5 @@ import { ReactElement, useState, ChangeEvent, ReactChild } from 'react' -import { withStyles, Theme, createStyles } from '@material-ui/core/styles' +import { withStyles, Theme, createStyles, makeStyles } from '@material-ui/core/styles' import { Tabs, Tab, Box, Typography, Container, CircularProgress } from '@material-ui/core' import AccountCard from '../accounting/AccountCard' @@ -33,8 +33,19 @@ function a11yProps(index: number) { } } +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + display: 'grid', + rowGap: theme.spacing(3), + }, + }), +) + export default function Accounting(): ReactElement { const [value, setValue] = useState(0) + const classes = useStyles() const handleChange = (event: ChangeEvent, newValue: number) => { setValue(newValue) @@ -115,7 +126,6 @@ export default function Accounting(): ReactElement { color: '#3f51b5', }, }, - selected: {}, }), )((props: StyledTabProps) => ) @@ -125,7 +135,7 @@ export default function Accounting(): ReactElement { // FIXME: this should be broken up /* eslint-disable no-nested-ternary */ nodeHealth?.status === 'ok' && health ? ( -
+
- - - - - - - - - - - - - - +
+ + + + + + + + + + + + + + +
) : isLoadingHealth || isLoadingNodeHealth ? ( diff --git a/src/pages/status/NodeSetupWorkflow.tsx b/src/pages/status/NodeSetupWorkflow.tsx index fb9bd8a..f3cb4ce 100644 --- a/src/pages/status/NodeSetupWorkflow.tsx +++ b/src/pages/status/NodeSetupWorkflow.tsx @@ -1,7 +1,7 @@ import { ReactElement, useEffect, useState } from 'react' import { makeStyles, Theme, createStyles } from '@material-ui/core/styles' import { Typography, Paper, Button, Step, StepLabel, StepContent, Stepper, StepButton } from '@material-ui/core/' -import { CheckCircle, Error, Sync, ExpandLessSharp, ExpandMoreSharp } from '@material-ui/icons/' +import { CheckCircle, Error, Sync, ExpandLessSharp, ExpandMoreSharp, Autorenew } from '@material-ui/icons/' import DebugConnectionCheck from './SetupSteps/DebugConnectionCheck' import NodeConnectionCheck from './SetupSteps/NodeConnectionCheck' @@ -9,17 +9,11 @@ import VersionCheck from './SetupSteps/VersionCheck' import EthereumConnectionCheck from './SetupSteps/EthereumConnectionCheck' import ChequebookDeployFund from './SetupSteps/ChequebookDeployFund' import PeerConnection from './SetupSteps/PeerConnection' -import type { - ChequebookAddressResponse, - ChequebookBalanceResponse, - Health, - NodeAddresses, - Topology, -} from '@ethersphere/bee-js' const useStyles = makeStyles((theme: Theme) => createStyles({ root: { + padding: theme.spacing(2), width: '100%', }, button: { @@ -29,147 +23,88 @@ const useStyles = makeStyles((theme: Theme) => actionsContainer: { margin: theme.spacing(2), }, - resetContainer: { - padding: theme.spacing(5), - }, }), ) -function getSteps() { - return [ - 'Debug Connection Check', - 'Version Check', - 'Connect to Ethereum Blockchain', - 'Deploy and Fund Chequebook', - 'Node Connection Check', - 'Connect to Peers', - ] +interface Step { + label: string + condition: boolean + isLoading: boolean + component: ReactElement } + interface Props { - nodeHealth: Health | null - nodeApiHealth: boolean - nodeAddresses: NodeAddresses | null - chequebookAddress: ChequebookAddressResponse | null - chequebookBalance: ChequebookBalanceResponse | null - beeRelease: LatestBeeRelease | null - nodeTopology: Topology | null - isLoadingBeeRelease: boolean - isLoadingNodeHealth: boolean - isLoadingNodeAddresses: boolean - isLoadingNodeTopology: boolean - isLoadingHealth: boolean - isLoadingChequebookAddress: boolean - isLoadingChequebookBalance: boolean - setStatusChecksVisible: (value: boolean) => void - apiHost: string - debugApiHost: string + nodeVersion: StatusNodeVersionHook + ethereumConnection: StatusEthereumConnectionHook + debugApiConnection: StatusHookCommon + apiConnection: StatusHookCommon + topology: StatusTopologyHook + chequebook: StatusChequebookHook } -function getStepContent(step: number, props: Props) { - const { - nodeHealth, - debugApiHost, - beeRelease, - isLoadingBeeRelease, - nodeAddresses, - isLoadingNodeAddresses, - isLoadingChequebookBalance, - chequebookAddress, - chequebookBalance, - isLoadingChequebookAddress, - nodeApiHealth, - apiHost, - isLoadingNodeTopology, - nodeTopology, - } = props - switch (step) { - case 0: - return - case 1: - return - case 2: - return - case 3: - return ( - - ) - case 4: - return - default: - return - } -} - -export default function NodeSetupWorkflow(props: Props): ReactElement { - const { - nodeHealth, - nodeApiHealth, - nodeAddresses, - chequebookAddress, - chequebookBalance, - beeRelease, - nodeTopology, - setStatusChecksVisible, - } = props +export default function NodeSetupWorkflow({ + nodeVersion, + ethereumConnection, + debugApiConnection, + apiConnection, + topology, + chequebook, +}: Props): ReactElement { const classes = useStyles() - const [activeStep, setActiveStep] = useState(0) - const [completed, setCompleted] = useState<{ [k: number]: boolean }>({}) - const steps = getSteps() + const [activeStep, setActiveStep] = useState(-1) + + const steps: Step[] = [ + { + label: 'Connected to Node DebugAPI', + condition: debugApiConnection.isOk, + isLoading: debugApiConnection.isLoading, + component: , + }, + { + label: 'Running latest Bee version', + condition: nodeVersion.isOk, + isLoading: nodeVersion.isLoading, + component: , + }, + { + label: 'Connected to Ethereum Blockchain', + condition: ethereumConnection.isOk, + isLoading: ethereumConnection.isLoading, + component: , + }, + { + label: 'Deployed and Funded Chequebook', + condition: chequebook.isOk, + isLoading: chequebook.isLoading, + component: , + }, + { + label: 'Connected to Node API', + condition: apiConnection.isOk, + isLoading: apiConnection.isLoading, + component: , + }, + { + label: 'Connected to Peers', + condition: topology.isOk, + isLoading: topology.isLoading, + component: , + }, + ] useEffect(() => { - const handleComplete = (index: number) => { - const newCompleted = completed - newCompleted[index] = true - setCompleted(newCompleted) - } + // If the user already changed the active step we don't want to overwrite it + if (activeStep > 0 && activeStep < steps.length) return - const evaluateNodeStatus = () => { - if (nodeHealth?.status === 'ok') { - handleComplete(0) - setActiveStep(1) - } + // Select first step that is not OK + for (let i = 0; i < steps.length; ++i) { + if (!steps[i].condition) { + setActiveStep(i) - if (beeRelease && beeRelease.name === `v${nodeHealth?.version?.split('-')[0]}`) { - handleComplete(1) - setActiveStep(2) - } - - if (nodeAddresses?.ethereum) { - handleComplete(2) - setActiveStep(3) - } - - if (chequebookAddress?.chequebookaddress && chequebookBalance && chequebookBalance.totalBalance > 0) { - handleComplete(3) - setActiveStep(4) - } - - if (nodeApiHealth) { - handleComplete(4) - setActiveStep(5) - } - - if (nodeTopology?.connected && nodeTopology?.connected > 0) { - handleComplete(5) - setActiveStep(6) + return } } - evaluateNodeStatus() - }, [ - nodeHealth, - nodeApiHealth, - nodeAddresses, - chequebookAddress, - beeRelease, - chequebookBalance, - nodeTopology, - completed, - ]) + }, [steps]) const handleNext = () => { setActiveStep(prevActiveStep => prevActiveStep + 1) @@ -179,13 +114,9 @@ export default function NodeSetupWorkflow(props: Props): ReactElement { setActiveStep(prevActiveStep => prevActiveStep - 1) } - const handleSetupComplete = () => { - setStatusChecksVisible(false) - } - return ( -
- + + Node Setup
@@ -235,17 +168,6 @@ export default function NodeSetupWorkflow(props: Props): ReactElement { ))} - {Object.values(completed).filter(value => value).length === 6 ? ( - - Bee setup complete! Welcome to the swarm and the internet of decentralized storage - - - - ) : null} - + ) } diff --git a/src/pages/status/SetupSteps/ChequebookDeployFund.tsx b/src/pages/status/SetupSteps/ChequebookDeployFund.tsx index 528bd29..4b0455c 100644 --- a/src/pages/status/SetupSteps/ChequebookDeployFund.tsx +++ b/src/pages/status/SetupSteps/ChequebookDeployFund.tsx @@ -1,38 +1,22 @@ import { Typography } from '@material-ui/core/' -import { CheckCircle, Warning } from '@material-ui/icons/' import EthereumAddress from '../../../components/EthereumAddress' import DepositModal from '../../../components/DepositModal' import CodeBlockTabs from '../../../components/CodeBlockTabs' -import type { ChequebookAddressResponse, ChequebookBalanceResponse } from '@ethersphere/bee-js' import type { ReactElement } from 'react' -interface Props { - chequebookAddress: ChequebookAddressResponse | null - chequebookBalance: ChequebookBalanceResponse | null - isLoadingChequebookAddress: boolean - isLoadingChequebookBalance: boolean -} +type Props = StatusChequebookHook -const ChequebookDeployFund = (props: Props): ReactElement => ( -
-

- Deploy chequebook and fund with BZZ - {props.chequebookAddress?.chequebookaddress ? : null} -

-
- { - // FIXME: this should be broken up - /* eslint-disable no-nested-ternary */ - props.chequebookAddress?.chequebookaddress && - props.chequebookBalance && - props.chequebookBalance?.totalBalance > 0 ? ( +const ChequebookDeployFund = ({ isLoading, chequebookAddress, chequebookBalance }: Props): ReactElement | null => { + if (isLoading) return null + + return ( +
+

+ {chequebookAddress?.chequebookaddress && } +

+
+ {!(chequebookAddress?.chequebookaddress && chequebookBalance && chequebookBalance?.totalBalance > 0) && (
- - Your chequebook is deployed and funded! -
- ) : props.isLoadingChequebookAddress || props.isLoadingChequebookBalance ? null : ( -
- Your chequebook is either not deployed or funded. Run the below commands to get your address and deposit ETH. Then visit the BZZaar here{' '} @@ -43,15 +27,14 @@ const ChequebookDeployFund = (props: Props): ReactElement => (
- ) - /* eslint-enable no-nested-ternary */ - } + )} +
+ + Chequebook Address + +
- - Chequebook Address - - -
-) + ) +} export default ChequebookDeployFund diff --git a/src/pages/status/SetupSteps/DebugConnectionCheck.tsx b/src/pages/status/SetupSteps/DebugConnectionCheck.tsx index aa8f7cd..c7a7409 100644 --- a/src/pages/status/SetupSteps/DebugConnectionCheck.tsx +++ b/src/pages/status/SetupSteps/DebugConnectionCheck.tsx @@ -1,100 +1,95 @@ import type { ReactElement } from 'react' import { Typography, Accordion, AccordionSummary, AccordionDetails } from '@material-ui/core/' import MuiAlert from '@material-ui/lab/Alert' -import { CheckCircle, Error, ExpandMoreSharp } from '@material-ui/icons/' +import { ExpandMoreSharp } from '@material-ui/icons/' import ConnectToHost from '../../../components/ConnectToHost' import CodeBlockTabs from '../../../components/CodeBlockTabs' -import type { Health } from '@ethersphere/bee-js' +import { debugApiHost } from '../../../constants' -interface Props { - nodeHealth: Health | null - debugApiHost: string -} +type Props = StatusHookCommon + +export default function NodeConnectionCheck({ isLoading, isOk }: Props): ReactElement | null { + if (isLoading) return null + + const changeDebugApiUrl = ( +
+ + Debug API ({debugApiHost}) + + +
+ ) + + if (isOk) { + return changeDebugApiUrl + } -export default function NodeConnectionCheck(props: Props): ReactElement { return (
-

Connect to Bee Node Debug API

+ {changeDebugApiUrl} +
-
- {props.nodeHealth?.status === 'ok' ? ( - - ) : ( - - )} - - Debug API ({props.debugApiHost}) - - -
-
- {props.nodeHealth?.status !== 'ok' ? ( - - We cannot connect to your nodes debug API at{' '} - {props.debugApiHost}. Please check the following to troubleshoot - your issue. - - } aria-controls="panel1a-content" id="panel1a-header"> - Troubleshoot - - - -
    -
  1. - Check the status of your node by running the below command to see if your node is running. -
  2. - -
  3. - If your node is running, check your firewall settings to make sure that port 1635 (or your - custom specified port) is bound to localhost. If your node is not running try executing the - below command to start your bee node -
  4. - - Your debug node API should never be completely open to the internet. If you want to connect - remotely, make sure your firewall settings are set to only allow specific trusted IP addresses - and block all other ports. A simple google search for "what is my ip" will show you - your computers public IP address to allow. - - -
  5. Run the commands to validate your node is running and see the log output.
  6. - -
  7. - Lastly, check your nodes configuration settings to validate the debug API is enabled and the - Cross Origin Resource Sharing (CORS) setting is configured to allow your host. Config parameter{' '} - debug-api-enable must be set to true and{' '} - cors-allowed-origins must be set to your host domain or IP. If edits are made - to the configuration run the restart command below for changes to take effect. -
  8. - -
-
-
-
-
- ) : null} -
+ + We cannot connect to your nodes debug API at {debugApiHost}. Please + check the following to troubleshoot your issue. + + } aria-controls="panel1a-content" id="panel1a-header"> + Troubleshoot + + + +
    +
  1. Check the status of your node by running the below command to see if your node is running.
  2. + +
  3. + If your node is running, check your firewall settings to make sure that port 1635 (or your custom + specified port) is bound to localhost. If your node is not running try executing the below command + to start your bee node +
  4. + + Your debug node API should never be completely open to the internet. If you want to connect + remotely, make sure your firewall settings are set to only allow specific trusted IP addresses and + block all other ports. A simple google search for "what is my ip" will show you your + computers public IP address to allow. + + +
  5. Run the commands to validate your node is running and see the log output.
  6. + +
  7. + Lastly, check your nodes configuration settings to validate the debug API is enabled and the Cross + Origin Resource Sharing (CORS) setting is configured to allow your host. Config parameter{' '} + debug-api-enable must be set to true and{' '} + cors-allowed-origins must be set to your host domain or IP. If edits are made to + the configuration run the restart command below for changes to take effect. +
  8. + +
+
+
+
+
) diff --git a/src/pages/status/SetupSteps/EthereumConnectionCheck.tsx b/src/pages/status/SetupSteps/EthereumConnectionCheck.tsx index 57fe9ab..f7a98f2 100644 --- a/src/pages/status/SetupSteps/EthereumConnectionCheck.tsx +++ b/src/pages/status/SetupSteps/EthereumConnectionCheck.tsx @@ -1,56 +1,40 @@ import type { ReactElement } from 'react' import { Typography } from '@material-ui/core/' -import { CheckCircle, Warning } from '@material-ui/icons/' import EthereumAddress from '../../../components/EthereumAddress' -import type { NodeAddresses } from '@ethersphere/bee-js' -interface Props { - nodeAddresses: NodeAddresses | null - isLoadingNodeAddresses: boolean -} +type Props = StatusEthereumConnectionHook -export default function EthereumConnectionCheck(props: Props): ReactElement { - return ( -
-

Connect to the ethereum blockchain.

-
- { - // FIXME: this should be broken up - /* eslint-disable no-nested-ternary */ - props.nodeAddresses?.ethereum ? ( -
- - Your connected to the Ethereum network -
- ) : props.isLoadingNodeAddresses ? null : ( -
- - Your not connected to the Ethereum network. -

- Your Bee node must have access to the Ethereum blockchain, so that it can interact and deploy your - chequebook contract. You can run{' '} - - your own Goerli node - - , or use a provider such as{' '} - - rpc.slock.it/goerli - {' '} - or{' '} - - Infura - - . By default, Bee expects a local Goerli node at http://localhost:8545. To use a provider instead, - simply change your --swap-endpoint in your configuration file. -

-
- ) /* eslint-enable no-nested-ternary */ - } +export default function EthereumConnectionCheck({ isLoading, isOk, nodeAddresses }: Props): ReactElement | null { + if (isLoading) return null + + if (isOk) { + return ( +
+ + Node Address + +
- - Node Address - - -
+ ) + } + + return ( +

+ Your Bee node must have access to the Ethereum blockchain, so that it can interact and deploy your chequebook + contract. You can run{' '} + + your own Goerli node + + , or use a provider such as{' '} + + rpc.slock.it/goerli + {' '} + or{' '} + + Infura + + . By default, Bee expects a local Goerli node at http://localhost:8545. To use a provider instead, simply change + your --swap-endpoint in your configuration file. +

) } diff --git a/src/pages/status/SetupSteps/NodeConnectionCheck.tsx b/src/pages/status/SetupSteps/NodeConnectionCheck.tsx index 64cdc57..1bc871e 100644 --- a/src/pages/status/SetupSteps/NodeConnectionCheck.tsx +++ b/src/pages/status/SetupSteps/NodeConnectionCheck.tsx @@ -1,35 +1,29 @@ import React, { ReactElement } from 'react' import { Typography, Accordion, AccordionSummary, AccordionDetails } from '@material-ui/core/' -import { CheckCircle, Error, ExpandMoreSharp } from '@material-ui/icons/' +import { ExpandMoreSharp } from '@material-ui/icons/' import ConnectToHost from '../../../components/ConnectToHost' import CodeBlockTabs from '../../../components/CodeBlockTabs' +import { apiHost } from '../../../constants' -interface Props { - nodeApiHealth: boolean - apiHost: string -} +type Props = StatusHookCommon + +export default function NodeConnectionCheck({ isLoading, isOk }: Props): ReactElement | null { + if (isLoading) return null -export default function NodeConnectionCheck(props: Props): ReactElement { return (
-

Connect to Bee Node API

- {props.nodeApiHealth ? ( - - ) : ( - - )} - Node API ({props.apiHost}) + Node API ({apiHost}) - +
- {!props.nodeApiHealth ? ( + {!isOk && ( - We cannot connect to your nodes API at {props.apiHost}. Please - check the following to troubleshoot your issue. + We cannot connect to your nodes API at {apiHost}. Please check the + following to troubleshoot your issue. } aria-controls="panel1a-content" id="panel1a-header"> Troubleshoot @@ -64,7 +58,7 @@ export default function NodeConnectionCheck(props: Props): ReactElement { - ) : null} + )}
) diff --git a/src/pages/status/SetupSteps/PeerConnection.tsx b/src/pages/status/SetupSteps/PeerConnection.tsx index c38852b..bb4cd6b 100644 --- a/src/pages/status/SetupSteps/PeerConnection.tsx +++ b/src/pages/status/SetupSteps/PeerConnection.tsx @@ -1,53 +1,45 @@ -import React, { ReactElement } from 'react' +import type { ReactElement } from 'react' import { Typography } from '@material-ui/core/' -import { CheckCircle, Warning } from '@material-ui/icons/' -import { Topology } from '@ethersphere/bee-js' -interface Props { - nodeTopology: Topology | null - isLoadingNodeTopology: boolean -} +type Props = StatusTopologyHook -export default function PeerConnection(props: Props): ReactElement { - return ( -
-

Connect to Peers

-
- html_url - { - // FIXME: this should be broken up - /* eslint-disable no-nested-ternary */ - props.nodeTopology?.connected && props.nodeTopology?.connected > 0 ? ( -
- - Your connected to {props.nodeTopology.connected} peers! -
- ) : props.isLoadingNodeTopology ? null : ( -
- - Your node is not connected to any peers -
- ) /* eslint-enable no-nested-ternary */ - } +export default function PeerConnection({ isLoading, isOk, topology }: Props): ReactElement | null { + if (isLoading) return null + + const peers = ( +
+
+ + Connected Peers + + + {topology?.connected ? topology.connected : '-'} +
-
-
- - Connected Peers - - - {props.nodeTopology?.connected} - -
-
- - Discovered Nodes - - - {props.nodeTopology?.population} - -
+
+ + Discovered Nodes + + + {topology?.population ? topology.population : '-'} +
) + + if (isOk) { + return ( + <> + You are connected to peers! + {peers} + + ) + } + + return ( + <> + Your node is not connected to any peers + {peers} + + ) } diff --git a/src/pages/status/SetupSteps/VersionCheck.tsx b/src/pages/status/SetupSteps/VersionCheck.tsx index f0887b2..89ac0fd 100644 --- a/src/pages/status/SetupSteps/VersionCheck.tsx +++ b/src/pages/status/SetupSteps/VersionCheck.tsx @@ -1,81 +1,68 @@ -import React, { ReactElement } from 'react' +import type { ReactElement } from 'react' import { Typography } from '@material-ui/core/' -import { CheckCircle, Warning } from '@material-ui/icons/' import CodeBlockTabs from '../../../components/CodeBlockTabs' -import { Health } from '@ethersphere/bee-js' -interface Props { - beeRelease: LatestBeeRelease | null - isLoadingBeeRelease: boolean - nodeHealth: Health | null -} +type Props = StatusNodeVersionHook -export default function VersionCheck(props: Props): ReactElement { - return ( -
-

- Check to make sure the latest version of{' '} - - Bee - {' '} - is running -

- { - // FIXME: this should be broken up - /* eslint-disable no-nested-ternary */ - props.beeRelease && props.beeRelease.name === `v${props.nodeHealth?.version?.split('-')[0]}` ? ( -
- - Your running the latest version of Bee -
- ) : props.isLoadingBeeRelease ? null : ( -
- - - Your Bee version is out of date. Please update to the{' '} - - latest - {' '} - before continuing. Rerun the installation script below to upgrade. Reference the docs for help with - updating.{' '} - - Docs - - - -
- ) /* eslint-enable no-nested-ternary */ - } -
-
-

- Current Version -

- - {props.nodeHealth?.version ? ` v${props.nodeHealth?.version?.split('-')[0]}` : '-'} - -
-
-

- Latest Version -

- - {props.beeRelease && props.beeRelease.name ? props.beeRelease.name : '-'} - -
+export default function VersionCheck({ + isLoading, + isOk, + userVersion, + latestVersion, + latestUrl, +}: Props): ReactElement | null { + if (isLoading) return null + + const version = ( +
+
+

+ User Version +

+ + {userVersion} + +
+
+

+ Latest Version +

+ + {latestVersion} +
) + + // Running latest bee version + if (isOk) { + return ( + <> + You are running the latest version of Bee + {version} + + ) + } + + // Old version or not connected to bee debug API + return ( + <> + + Your Bee version is out of date. Please update to the{' '} + + latest + {' '} + before continuing. Rerun the installation script below to upgrade. Reference the docs for help with updating.{' '} + + Docs + + + + {version} + + ) } diff --git a/src/pages/status/StatusCard.tsx b/src/pages/status/StatusCard.tsx index 967189d..4d01f51 100644 --- a/src/pages/status/StatusCard.tsx +++ b/src/pages/status/StatusCard.tsx @@ -4,23 +4,15 @@ import { Link } from 'react-router-dom' import { createStyles, makeStyles } from '@material-ui/core/styles' import { Card, CardContent, Typography, Chip, Button } from '@material-ui/core/' import { CheckCircle, Error, ArrowRight, ArrowDropUp } from '@material-ui/icons/' -import { Skeleton } from '@material-ui/lab' -import type { Health, NodeAddresses, Topology } from '@ethersphere/bee-js' +import { NodeAddresses, Topology } from '@ethersphere/bee-js' const useStyles = makeStyles(() => createStyles({ root: { display: 'flex', - }, - details: { - display: 'flex', - flex: '1 0 auto', - + flex: '1 1 auto', flexDirection: 'column', }, - content: { - flex: '1 0 auto', - }, status: { color: '#2145a0', backgroundColor: '#e1effe', @@ -29,124 +21,104 @@ const useStyles = makeStyles(() => ) interface Props { - nodeHealth: Health | null - loadingNodeHealth: boolean - beeRelease: LatestBeeRelease | null - loadingBeeRelease: boolean - nodeAddresses: NodeAddresses - nodeTopology: Topology - loadingNodeTopology: boolean - setStatusChecksVisible: (value: boolean) => void + nodeAddresses: NodeAddresses | null + nodeTopology: Topology | null + userBeeVersion: string | null + latestBeeVersion: string | null + isOk: boolean } -function StatusCard(props: Props): ReactElement { +function StatusCard({ + userBeeVersion, + nodeAddresses, + nodeTopology, + latestBeeVersion, + isOk, +}: Props): ReactElement | null { const classes = useStyles() const [underlayAddressesVisible, setUnderlayAddresessVisible] = useState(false) return ( -
- - {!props.loadingNodeHealth && props.nodeHealth ? ( -
- - - {props.nodeHealth.status === 'ok' ? ( -
- - Connected to Bee Node -
+ + + + {isOk && ( +
+ + Your Bee node is running as expected +
+ )} + {!isOk && ( +
+ + Could not connect to Bee Node +
+ )} +
+ {isOk && ( + <> +
+ Discovered Nodes: {nodeTopology?.population} + + Connected Peers: + {nodeTopology?.connected} + +
+
+ + AGENT: + + Bee + {' '} + {userBeeVersion || '-'} + {userBeeVersion && latestBeeVersion && userBeeVersion === latestBeeVersion ? ( + ) : ( + update + )} + + + PUBLIC KEY: + {nodeAddresses?.public_key ? nodeAddresses.public_key : '-'} + + + PSS PUBLIC KEY: + {nodeAddresses?.pss_public_key ? nodeAddresses.pss_public_key : '-'} + + + OVERLAY ADDRESS (PEER ID): + {nodeAddresses?.overlay ? nodeAddresses.overlay : '-'} + + + setUnderlayAddresessVisible(!underlayAddressesVisible)}> + + + {underlayAddressesVisible && (
- - Could not connect to Bee Node + {nodeAddresses?.underlay.map(item => ( +
  • {item}
  • + ))}
    )} -
    -
    - Discovered Nodes: {props.nodeTopology.population} - - Connected Peers: - {props.nodeTopology.connected} - -
    -
    - - AGENT: - - Bee - - {props.nodeHealth?.version ? ` v${props.nodeHealth.version}` : '-'} - { - // FIXME: this should be broken up - /* eslint-disable no-nested-ternary */ - props.beeRelease && props.beeRelease.name === `v${props.nodeHealth?.version?.split('-')[0]}` ? ( - - ) : props.loadingBeeRelease ? ( - '' - ) : ( - update - ) - /* eslint-enable no-nested-ternary */ - } - - - PUBLIC KEY: - {props.nodeAddresses.public_key ? props.nodeAddresses.public_key : '-'} - - - PSS PUBLIC KEY: - {props.nodeAddresses.pss_public_key ? props.nodeAddresses.pss_public_key : '-'} - - - - OVERLAY ADDRESS (PEER ID): - {props.nodeAddresses.overlay ? props.nodeAddresses.overlay : '-'} - - setUnderlayAddresessVisible(!underlayAddressesVisible)}> - - - {underlayAddressesVisible ? ( -
    - {props.nodeAddresses.underlay - ? props.nodeAddresses.underlay.map(item =>
  • {item}
  • ) - : '-'} -
    - ) : null} -
    -
    - -
    - ) : ( -
    - - - -
    +
    + )} -
    -
    + + ) } diff --git a/src/pages/status/index.tsx b/src/pages/status/index.tsx index 151ecf8..ab2dab6 100644 --- a/src/pages/status/index.tsx +++ b/src/pages/status/index.tsx @@ -1,143 +1,75 @@ -import { useState, useEffect, ReactElement } from 'react' -import axios from 'axios' +import { ReactElement } from 'react' import { Container, CircularProgress } from '@material-ui/core' +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles' import NodeSetupWorkflow from './NodeSetupWorkflow' import StatusCard from './StatusCard' import EthereumAddressCard from '../../components/EthereumAddressCard' import { - useApiHealth, - useDebugApiHealth, - useApiNodeAddresses, - useApiChequebookAddress, - useApiNodeTopology, - useApiChequebookBalance, -} from '../../hooks/apiHooks' + useStatusEthereumConnection, + useStatusNodeVersion, + useStatusDebugConnection, + useStatusConnection, + useStatusTopology, + useStatusChequebook, +} from '../../hooks/status' + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + display: 'grid', + rowGap: theme.spacing(3), + }, + }), +) export default function Status(): ReactElement { - const [beeRelease, setBeeRelease] = useState(null) - const [isLoadingBeeRelease, setIsLoadingBeeRelease] = useState(false) + const classes = useStyles() - const [apiHost, setApiHost] = useState('') - const [debugApiHost, setDebugApiHost] = useState('') + const nodeVersion = useStatusNodeVersion() + const ethereumConnection = useStatusEthereumConnection() + const debugApiConnection = useStatusDebugConnection() + const apiConnection = useStatusConnection() + const topology = useStatusTopology() + const chequebook = useStatusChequebook() - const [statusChecksVisible, setStatusChecksVisible] = useState(false) + const checks = [nodeVersion, ethereumConnection, debugApiConnection, apiConnection, topology, chequebook] - const { health, isLoadingHealth } = useApiHealth() - const { nodeHealth, isLoadingNodeHealth } = useDebugApiHealth() - const { nodeAddresses, isLoadingNodeAddresses } = useApiNodeAddresses() - const { chequebookAddress, isLoadingChequebookAddress } = useApiChequebookAddress() - const { topology: nodeTopology, isLoading: isLoadingNodeTopology } = useApiNodeTopology() - const { chequebookBalance, isLoadingChequebookBalance } = useApiChequebookBalance() - - const fetchLatestBeeRelease = () => { - setIsLoadingBeeRelease(true) - axios - .get(`${process.env.REACT_APP_BEE_GITHUB_REPO_URL}/releases/latest`) - .then(res => { - setBeeRelease(res.data) - }) - .catch(() => { - // FIXME: should do something about the error - }) - .finally(() => { - setIsLoadingBeeRelease(false) - }) + // If any check data are still loading + if (!checks.every(c => !c.isLoading)) { + return ( + + + + ) } - const fetchApiHost = () => { - let apiHost - - if (sessionStorage.getItem('api_host')) { - apiHost = String(sessionStorage.getItem('api_host') || '') - } else { - apiHost = String(process.env.REACT_APP_BEE_HOST) - } - setApiHost(apiHost) - } - - const fetchDebugApiHost = () => { - let debugApiHost - - if (sessionStorage.getItem('debug_api_host')) { - debugApiHost = String(sessionStorage.getItem('debug_api_host') || '') - } else { - debugApiHost = String(process.env.REACT_APP_BEE_DEBUG_HOST) - } - setDebugApiHost(debugApiHost) - } - - useEffect(() => { - fetchApiHost() - fetchDebugApiHost() - fetchLatestBeeRelease() - }, []) - - // FIXME: this should be broken up - /* eslint-disable no-nested-ternary */ return ( -
    - {nodeHealth?.status === 'ok' && - health && - beeRelease && - beeRelease.name === `v${nodeHealth?.version?.split('-')[0]}` && - nodeAddresses?.ethereum && - chequebookAddress?.chequebookaddress && - chequebookBalance && - chequebookBalance?.totalBalance > 0 && - nodeTopology?.connected && - nodeTopology?.connected > 0 && - !statusChecksVisible ? ( -
    - - -
    - ) : isLoadingNodeHealth || - isLoadingHealth || - isLoadingChequebookAddress || - isLoadingNodeTopology || - isLoadingBeeRelease || - isLoadingNodeAddresses || - isLoadingBeeRelease || - isLoadingChequebookBalance ? ( - - - - ) : ( - + c.isOk)} + nodeTopology={topology.topology} + nodeAddresses={ethereumConnection.nodeAddresses} + /> + {ethereumConnection.nodeAddresses && chequebook.chequebookAddress && ( + )} +
    ) } diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts index b86041e..5b75ca4 100644 --- a/src/react-app-env.d.ts +++ b/src/react-app-env.d.ts @@ -4,3 +4,24 @@ interface LatestBeeRelease { name: string html_url: string } + +interface StatusHookCommon { + isLoading: boolean + isOk: boolean +} + +interface StatusNodeVersionHook extends StatusHookCommon { + userVersion: string + latestVersion: string + latestUrl: string +} +interface StatusEthereumConnectionHook extends StatusHookCommon { + nodeAddresses: NodeAddresses | null +} +interface StatusTopologyHook extends StatusHookCommon { + topology: Topology | null +} +interface StatusChequebookHook extends StatusHookCommon { + chequebookBalance: ChequebookBalanceResponse | null + chequebookAddress: ChequebookAddressResponse | null +}