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
This commit is contained in:
Vojtech Simetka
2021-04-09 15:09:17 +02:00
committed by GitHub
parent 5608b91966
commit 9e4e58f160
14 changed files with 656 additions and 764 deletions
+35 -39
View File
@@ -12,7 +12,9 @@ const useStyles = makeStyles(() =>
createStyles({ createStyles({
root: { root: {
display: 'flex', display: 'flex',
marginTop: '20px', alignItems: 'center',
justifyContent: 'space-around',
flexWrap: 'wrap',
}, },
details: { details: {
display: 'flex', display: 'flex',
@@ -21,10 +23,6 @@ const useStyles = makeStyles(() =>
content: { content: {
flex: '1 0 auto', flex: '1 0 auto',
}, },
status: {
color: '#fff',
backgroundColor: '#76a9fa',
},
}), }),
) )
@@ -39,40 +37,38 @@ function EthereumAddressCard(props: Props): ReactElement {
const classes = useStyles() const classes = useStyles()
return ( return (
<div> <Card className={classes.root}>
<Card className={classes.root}> {props.isLoadingNodeAddresses ? (
{props.isLoadingNodeAddresses ? ( <div style={{ padding: '16px' }}>
<div style={{ padding: '16px' }}> <Skeleton width={300} height={30} animation="wave" />
<Skeleton width={300} height={30} animation="wave" /> <Skeleton width={300} height={50} animation="wave" />
<Skeleton width={300} height={50} animation="wave" /> </div>
</div> ) : (
) : ( <div className={classes.details}>
<div className={classes.details}> <CardContent className={classes.content}>
<CardContent className={classes.content}> <Typography variant="subtitle1" gutterBottom>
<Typography variant="subtitle1" gutterBottom> Ethereum Address
Ethereum Address </Typography>
</Typography> <EthereumAddress address={props.nodeAddresses?.ethereum} network={'goerli'} />
<EthereumAddress address={props.nodeAddresses?.ethereum} network={'goerli'} /> </CardContent>
</CardContent> </div>
</div> )}
)} {props.isLoadingChequebookAddress ? (
{props.isLoadingChequebookAddress ? ( <div style={{ padding: '16px' }}>
<div style={{ padding: '16px' }}> <Skeleton width={300} height={30} animation="wave" />
<Skeleton width={300} height={30} animation="wave" /> <Skeleton width={300} height={50} animation="wave" />
<Skeleton width={300} height={50} animation="wave" /> </div>
</div> ) : (
) : ( <div className={classes.details}>
<div className={classes.details}> <CardContent className={classes.content}>
<CardContent className={classes.content}> <Typography variant="subtitle1" gutterBottom>
<Typography variant="subtitle1" gutterBottom> Chequebook Contract Address
Chequebook Contract Address </Typography>
</Typography> <EthereumAddress address={props.chequebookAddress?.chequebookaddress} network={'goerli'} />
<EthereumAddress address={props.chequebookAddress?.chequebookaddress} network={'goerli'} /> </CardContent>
</CardContent> </div>
</div> )}
)} </Card>
</Card>
</div>
) )
} }
+29
View File
@@ -15,6 +15,7 @@ import {
} from '@ethersphere/bee-js' } from '@ethersphere/bee-js'
import { beeDebugApi, beeApi } from '../services/bee' import { beeDebugApi, beeApi } from '../services/bee'
import axios from 'axios'
export interface HealthHook { export interface HealthHook {
health: boolean health: boolean
@@ -361,3 +362,31 @@ export const useApiPeerLastCashout = (peerId: string): PeerLastCashoutHook => {
return { peerCashout, isLoadingPeerCashout, error } return { peerCashout, isLoadingPeerCashout, error }
} }
export interface LatestBeeReleaseHook {
latestBeeRelease: LatestBeeRelease | null
isLoadingLatestBeeRelease: boolean
error: Error | null
}
export const useLatestBeeRelease = (): LatestBeeReleaseHook => {
const [latestBeeRelease, setLatestBeeRelease] = useState<LatestBeeRelease | null>(null)
const [isLoadingLatestBeeRelease, setLoading] = useState<boolean>(false)
const [error, setError] = useState<Error | null>(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 }
}
+73
View File
@@ -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,
}
}
+29 -17
View File
@@ -1,5 +1,5 @@
import { ReactElement, useState, ChangeEvent, ReactChild } from 'react' 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 { Tabs, Tab, Box, Typography, Container, CircularProgress } from '@material-ui/core'
import AccountCard from '../accounting/AccountCard' 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 { export default function Accounting(): ReactElement {
const [value, setValue] = useState(0) const [value, setValue] = useState(0)
const classes = useStyles()
const handleChange = (event: ChangeEvent<unknown>, newValue: number) => { const handleChange = (event: ChangeEvent<unknown>, newValue: number) => {
setValue(newValue) setValue(newValue)
@@ -115,7 +126,6 @@ export default function Accounting(): ReactElement {
color: '#3f51b5', color: '#3f51b5',
}, },
}, },
selected: {},
}), }),
)((props: StyledTabProps) => <Tab disableRipple {...props} />) )((props: StyledTabProps) => <Tab disableRipple {...props} />)
@@ -125,7 +135,7 @@ export default function Accounting(): ReactElement {
// FIXME: this should be broken up // FIXME: this should be broken up
/* eslint-disable no-nested-ternary */ /* eslint-disable no-nested-ternary */
nodeHealth?.status === 'ok' && health ? ( nodeHealth?.status === 'ok' && health ? (
<div> <div className={classes.root}>
<AccountCard <AccountCard
chequebookAddress={chequebookAddress} chequebookAddress={chequebookAddress}
isLoadingChequebookAddress={isLoadingChequebookAddress} isLoadingChequebookAddress={isLoadingChequebookAddress}
@@ -140,20 +150,22 @@ export default function Accounting(): ReactElement {
chequebookAddress={chequebookAddress} chequebookAddress={chequebookAddress}
isLoadingChequebookAddress={isLoadingChequebookAddress} isLoadingChequebookAddress={isLoadingChequebookAddress}
/> />
<AntTabs style={{ marginTop: '12px' }} value={value} onChange={handleChange} aria-label="ant example"> <div>
<AntTab label="Balances" {...a11yProps(0)} /> <AntTabs style={{ marginTop: '12px' }} value={value} onChange={handleChange} aria-label="ant example">
<AntTab label="Chequebook" {...a11yProps(1)} /> <AntTab label="Balances" {...a11yProps(0)} />
<AntTab label="Settlements" {...a11yProps(2)} /> <AntTab label="Chequebook" {...a11yProps(1)} />
</AntTabs> <AntTab label="Settlements" {...a11yProps(2)} />
<TabPanel value={value} index={0}> </AntTabs>
<BalancesTable peerBalances={peerBalances} loading={isLoadingPeerBalances} /> <TabPanel value={value} index={0}>
</TabPanel> <BalancesTable peerBalances={peerBalances} loading={isLoadingPeerBalances} />
<TabPanel value={value} index={1}> </TabPanel>
<ChequebookTable peerCheques={peerCheques} loading={isLoadingPeerCheques} /> <TabPanel value={value} index={1}>
</TabPanel> <ChequebookTable peerCheques={peerCheques} loading={isLoadingPeerCheques} />
<TabPanel value={value} index={2}> </TabPanel>
<SettlementsTable nodeSettlements={settlements} loading={isLoadingSettlements} /> <TabPanel value={value} index={2}>
</TabPanel> <SettlementsTable nodeSettlements={settlements} loading={isLoadingSettlements} />
</TabPanel>
</div>
</div> </div>
) : isLoadingHealth || isLoadingNodeHealth ? ( ) : isLoadingHealth || isLoadingNodeHealth ? (
<Container style={{ textAlign: 'center', padding: '50px' }}> <Container style={{ textAlign: 'center', padding: '50px' }}>
+85 -163
View File
@@ -1,7 +1,7 @@
import { ReactElement, useEffect, useState } from 'react' import { ReactElement, useEffect, useState } from 'react'
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles' import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
import { Typography, Paper, Button, Step, StepLabel, StepContent, Stepper, StepButton } from '@material-ui/core/' 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 DebugConnectionCheck from './SetupSteps/DebugConnectionCheck'
import NodeConnectionCheck from './SetupSteps/NodeConnectionCheck' import NodeConnectionCheck from './SetupSteps/NodeConnectionCheck'
@@ -9,17 +9,11 @@ import VersionCheck from './SetupSteps/VersionCheck'
import EthereumConnectionCheck from './SetupSteps/EthereumConnectionCheck' import EthereumConnectionCheck from './SetupSteps/EthereumConnectionCheck'
import ChequebookDeployFund from './SetupSteps/ChequebookDeployFund' import ChequebookDeployFund from './SetupSteps/ChequebookDeployFund'
import PeerConnection from './SetupSteps/PeerConnection' import PeerConnection from './SetupSteps/PeerConnection'
import type {
ChequebookAddressResponse,
ChequebookBalanceResponse,
Health,
NodeAddresses,
Topology,
} from '@ethersphere/bee-js'
const useStyles = makeStyles((theme: Theme) => const useStyles = makeStyles((theme: Theme) =>
createStyles({ createStyles({
root: { root: {
padding: theme.spacing(2),
width: '100%', width: '100%',
}, },
button: { button: {
@@ -29,147 +23,88 @@ const useStyles = makeStyles((theme: Theme) =>
actionsContainer: { actionsContainer: {
margin: theme.spacing(2), margin: theme.spacing(2),
}, },
resetContainer: {
padding: theme.spacing(5),
},
}), }),
) )
function getSteps() { interface Step {
return [ label: string
'Debug Connection Check', condition: boolean
'Version Check', isLoading: boolean
'Connect to Ethereum Blockchain', component: ReactElement
'Deploy and Fund Chequebook',
'Node Connection Check',
'Connect to Peers',
]
} }
interface Props { interface Props {
nodeHealth: Health | null nodeVersion: StatusNodeVersionHook
nodeApiHealth: boolean ethereumConnection: StatusEthereumConnectionHook
nodeAddresses: NodeAddresses | null debugApiConnection: StatusHookCommon
chequebookAddress: ChequebookAddressResponse | null apiConnection: StatusHookCommon
chequebookBalance: ChequebookBalanceResponse | null topology: StatusTopologyHook
beeRelease: LatestBeeRelease | null chequebook: StatusChequebookHook
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
} }
function getStepContent(step: number, props: Props) { export default function NodeSetupWorkflow({
const { nodeVersion,
nodeHealth, ethereumConnection,
debugApiHost, debugApiConnection,
beeRelease, apiConnection,
isLoadingBeeRelease, topology,
nodeAddresses, chequebook,
isLoadingNodeAddresses, }: Props): ReactElement {
isLoadingChequebookBalance,
chequebookAddress,
chequebookBalance,
isLoadingChequebookAddress,
nodeApiHealth,
apiHost,
isLoadingNodeTopology,
nodeTopology,
} = props
switch (step) {
case 0:
return <DebugConnectionCheck nodeHealth={nodeHealth} debugApiHost={debugApiHost} />
case 1:
return <VersionCheck nodeHealth={nodeHealth} beeRelease={beeRelease} isLoadingBeeRelease={isLoadingBeeRelease} />
case 2:
return <EthereumConnectionCheck nodeAddresses={nodeAddresses} isLoadingNodeAddresses={isLoadingNodeAddresses} />
case 3:
return (
<ChequebookDeployFund
chequebookAddress={chequebookAddress}
chequebookBalance={chequebookBalance}
isLoadingChequebookAddress={isLoadingChequebookAddress}
isLoadingChequebookBalance={isLoadingChequebookBalance}
/>
)
case 4:
return <NodeConnectionCheck nodeApiHealth={nodeApiHealth} apiHost={apiHost} />
default:
return <PeerConnection nodeTopology={nodeTopology} isLoadingNodeTopology={isLoadingNodeTopology} />
}
}
export default function NodeSetupWorkflow(props: Props): ReactElement {
const {
nodeHealth,
nodeApiHealth,
nodeAddresses,
chequebookAddress,
chequebookBalance,
beeRelease,
nodeTopology,
setStatusChecksVisible,
} = props
const classes = useStyles() const classes = useStyles()
const [activeStep, setActiveStep] = useState(0) const [activeStep, setActiveStep] = useState(-1)
const [completed, setCompleted] = useState<{ [k: number]: boolean }>({})
const steps = getSteps() const steps: Step[] = [
{
label: 'Connected to Node DebugAPI',
condition: debugApiConnection.isOk,
isLoading: debugApiConnection.isLoading,
component: <DebugConnectionCheck {...debugApiConnection} />,
},
{
label: 'Running latest Bee version',
condition: nodeVersion.isOk,
isLoading: nodeVersion.isLoading,
component: <VersionCheck {...nodeVersion} />,
},
{
label: 'Connected to Ethereum Blockchain',
condition: ethereumConnection.isOk,
isLoading: ethereumConnection.isLoading,
component: <EthereumConnectionCheck {...ethereumConnection} />,
},
{
label: 'Deployed and Funded Chequebook',
condition: chequebook.isOk,
isLoading: chequebook.isLoading,
component: <ChequebookDeployFund {...chequebook} />,
},
{
label: 'Connected to Node API',
condition: apiConnection.isOk,
isLoading: apiConnection.isLoading,
component: <NodeConnectionCheck {...apiConnection} />,
},
{
label: 'Connected to Peers',
condition: topology.isOk,
isLoading: topology.isLoading,
component: <PeerConnection {...topology} />,
},
]
useEffect(() => { useEffect(() => {
const handleComplete = (index: number) => { // If the user already changed the active step we don't want to overwrite it
const newCompleted = completed if (activeStep > 0 && activeStep < steps.length) return
newCompleted[index] = true
setCompleted(newCompleted)
}
const evaluateNodeStatus = () => { // Select first step that is not OK
if (nodeHealth?.status === 'ok') { for (let i = 0; i < steps.length; ++i) {
handleComplete(0) if (!steps[i].condition) {
setActiveStep(1) setActiveStep(i)
}
if (beeRelease && beeRelease.name === `v${nodeHealth?.version?.split('-')[0]}`) { return
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)
} }
} }
evaluateNodeStatus() }, [steps])
}, [
nodeHealth,
nodeApiHealth,
nodeAddresses,
chequebookAddress,
beeRelease,
chequebookBalance,
nodeTopology,
completed,
])
const handleNext = () => { const handleNext = () => {
setActiveStep(prevActiveStep => prevActiveStep + 1) setActiveStep(prevActiveStep => prevActiveStep + 1)
@@ -179,13 +114,9 @@ export default function NodeSetupWorkflow(props: Props): ReactElement {
setActiveStep(prevActiveStep => prevActiveStep - 1) setActiveStep(prevActiveStep => prevActiveStep - 1)
} }
const handleSetupComplete = () => {
setStatusChecksVisible(false)
}
return ( return (
<div className={classes.root}> <Paper className={classes.root}>
<Typography variant="h4" gutterBottom> <Typography variant="h5" gutterBottom>
Node Setup Node Setup
<span style={{ marginLeft: '25px' }}> <span style={{ marginLeft: '25px' }}>
<Button variant="outlined" size="small" onClick={() => window.location.reload()}> <Button variant="outlined" size="small" onClick={() => window.location.reload()}>
@@ -195,20 +126,22 @@ export default function NodeSetupWorkflow(props: Props): ReactElement {
</span> </span>
</Typography> </Typography>
<Stepper nonLinear activeStep={activeStep} orientation="vertical"> <Stepper nonLinear activeStep={activeStep} orientation="vertical">
{steps.map((label, index) => ( {steps.map(({ label, condition, component, isLoading }, index) => (
<Step key={label}> <Step key={label}>
<StepLabel <StepLabel
onClick={() => setActiveStep(index === activeStep ? 6 : index)} disabled={isLoading}
onClick={() => setActiveStep(index === activeStep ? steps.length : index)}
StepIconComponent={() => { StepIconComponent={() => {
if (completed[index]) { if (isLoading) return <Autorenew style={{ height: '25px', cursor: 'pointer' }} />
return <CheckCircle style={{ color: '#32c48d', height: '25px', cursor: 'pointer' }} />
} else { if (condition) return <CheckCircle style={{ color: '#32c48d', height: '25px', cursor: 'pointer' }} />
return <Error style={{ color: '#c9201f', height: '25px', cursor: 'pointer' }} />
} return <Error style={{ color: '#c9201f', height: '25px', cursor: 'pointer' }} />
}} }}
> >
<StepButton <StepButton
onClick={() => setActiveStep(index === activeStep ? 6 : index)} disabled={isLoading}
onClick={() => setActiveStep(index === activeStep ? steps.length : index)}
style={{ justifyContent: 'space-between' }} style={{ justifyContent: 'space-between' }}
> >
<div style={{ display: 'flex' }}> <div style={{ display: 'flex' }}>
@@ -220,14 +153,14 @@ export default function NodeSetupWorkflow(props: Props): ReactElement {
</StepButton> </StepButton>
</StepLabel> </StepLabel>
<StepContent> <StepContent>
<Typography component="div">{getStepContent(index, props)}</Typography> <Typography component="div">{component}</Typography>
<div className={classes.actionsContainer}> <div className={classes.actionsContainer}>
<div> <div>
<Button disabled={activeStep === 0} onClick={handleBack} className={classes.button}> <Button disabled={activeStep === 0} onClick={handleBack} className={classes.button}>
Back Back
</Button> </Button>
<Button variant="contained" color="primary" onClick={handleNext} className={classes.button}> <Button variant="contained" color="primary" onClick={handleNext} className={classes.button}>
Next {index < steps.length - 1 ? 'Next' : 'Finish'}
</Button> </Button>
</div> </div>
</div> </div>
@@ -235,17 +168,6 @@ export default function NodeSetupWorkflow(props: Props): ReactElement {
</Step> </Step>
))} ))}
</Stepper> </Stepper>
{Object.values(completed).filter(value => value).length === 6 ? ( </Paper>
<Paper square elevation={0} className={classes.resetContainer}>
<Typography>Bee setup complete! Welcome to the swarm and the internet of decentralized storage</Typography>
<Button onClick={handleBack} className={classes.button}>
Back
</Button>
<Button onClick={handleSetupComplete} variant="contained" color="primary" className={classes.button}>
Complete
</Button>
</Paper>
) : null}
</div>
) )
} }
@@ -1,38 +1,22 @@
import { Typography } from '@material-ui/core/' import { Typography } from '@material-ui/core/'
import { CheckCircle, Warning } from '@material-ui/icons/'
import EthereumAddress from '../../../components/EthereumAddress' import EthereumAddress from '../../../components/EthereumAddress'
import DepositModal from '../../../components/DepositModal' import DepositModal from '../../../components/DepositModal'
import CodeBlockTabs from '../../../components/CodeBlockTabs' import CodeBlockTabs from '../../../components/CodeBlockTabs'
import type { ChequebookAddressResponse, ChequebookBalanceResponse } from '@ethersphere/bee-js'
import type { ReactElement } from 'react' import type { ReactElement } from 'react'
interface Props { type Props = StatusChequebookHook
chequebookAddress: ChequebookAddressResponse | null
chequebookBalance: ChequebookBalanceResponse | null
isLoadingChequebookAddress: boolean
isLoadingChequebookBalance: boolean
}
const ChequebookDeployFund = (props: Props): ReactElement => ( const ChequebookDeployFund = ({ isLoading, chequebookAddress, chequebookBalance }: Props): ReactElement | null => {
<div> if (isLoading) return null
<p style={{ marginBottom: '20px', display: 'flex' }}>
<span style={{ marginRight: '40px' }}>Deploy chequebook and fund with BZZ</span> return (
{props.chequebookAddress?.chequebookaddress ? <DepositModal /> : null} <div>
</p> <p style={{ marginBottom: '20px', display: 'flex' }}>
<div style={{ marginBottom: '10px' }}> {chequebookAddress?.chequebookaddress && <DepositModal />}
{ </p>
// FIXME: this should be broken up <div style={{ marginBottom: '10px' }}>
/* eslint-disable no-nested-ternary */ {!(chequebookAddress?.chequebookaddress && chequebookBalance && chequebookBalance?.totalBalance > 0) && (
props.chequebookAddress?.chequebookaddress &&
props.chequebookBalance &&
props.chequebookBalance?.totalBalance > 0 ? (
<div> <div>
<CheckCircle style={{ color: '#32c48d', marginRight: '7px', height: '18px' }} />
<span>Your chequebook is deployed and funded!</span>
</div>
) : props.isLoadingChequebookAddress || props.isLoadingChequebookBalance ? null : (
<div>
<Warning style={{ color: '#ff9800', marginRight: '7px', height: '18px' }} />
<span> <span>
Your chequebook is either not deployed or funded. Run the below commands to get your address and deposit Your chequebook is either not deployed or funded. Run the below commands to get your address and deposit
ETH. Then visit the BZZaar here{' '} ETH. Then visit the BZZaar here{' '}
@@ -43,15 +27,14 @@ const ChequebookDeployFund = (props: Props): ReactElement => (
</span> </span>
<CodeBlockTabs showLineNumbers linux={`bee-get-addr`} mac={`bee-get-addr`} /> <CodeBlockTabs showLineNumbers linux={`bee-get-addr`} mac={`bee-get-addr`} />
</div> </div>
) )}
/* eslint-enable no-nested-ternary */ </div>
} <Typography variant="subtitle1" gutterBottom>
Chequebook Address
</Typography>
<EthereumAddress address={chequebookAddress?.chequebookaddress} network={'goerli'} />
</div> </div>
<Typography variant="subtitle1" gutterBottom> )
Chequebook Address }
</Typography>
<EthereumAddress address={props.chequebookAddress?.chequebookaddress} network={'goerli'} />
</div>
)
export default ChequebookDeployFund export default ChequebookDeployFund
@@ -1,100 +1,95 @@
import type { ReactElement } from 'react' import type { ReactElement } from 'react'
import { Typography, Accordion, AccordionSummary, AccordionDetails } from '@material-ui/core/' import { Typography, Accordion, AccordionSummary, AccordionDetails } from '@material-ui/core/'
import MuiAlert from '@material-ui/lab/Alert' 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 ConnectToHost from '../../../components/ConnectToHost'
import CodeBlockTabs from '../../../components/CodeBlockTabs' import CodeBlockTabs from '../../../components/CodeBlockTabs'
import type { Health } from '@ethersphere/bee-js' import { debugApiHost } from '../../../constants'
interface Props { type Props = StatusHookCommon
nodeHealth: Health | null
debugApiHost: string export default function NodeConnectionCheck({ isLoading, isOk }: Props): ReactElement | null {
} if (isLoading) return null
const changeDebugApiUrl = (
<div style={{ display: 'flex', marginTop: '25px', marginBottom: '25px' }}>
<span style={{ marginRight: '15px' }}>
Debug API (<Typography variant="button">{debugApiHost}</Typography>)
</span>
<ConnectToHost hostName={'debug_api_host'} defaultHost={debugApiHost} />
</div>
)
if (isOk) {
return changeDebugApiUrl
}
export default function NodeConnectionCheck(props: Props): ReactElement {
return ( return (
<div> <div>
<p>Connect to Bee Node Debug API</p> {changeDebugApiUrl}
<div> <div>
<div style={{ display: 'flex', marginBottom: '25px' }}> <Typography component="div" variant="body2" gutterBottom style={{ margin: '15px' }}>
{props.nodeHealth?.status === 'ok' ? ( We cannot connect to your nodes debug API at <Typography variant="button">{debugApiHost}</Typography>. Please
<CheckCircle style={{ color: '#32c48d', marginRight: '7px', height: '18px' }} /> check the following to troubleshoot your issue.
) : ( <Accordion style={{ marginTop: '20px' }}>
<Error style={{ color: '#c9201f', marginRight: '7px', height: '18px' }} /> <AccordionSummary expandIcon={<ExpandMoreSharp />} aria-controls="panel1a-content" id="panel1a-header">
)} <Typography>Troubleshoot</Typography>
<span style={{ marginRight: '15px' }}> </AccordionSummary>
Debug API (<Typography variant="button">{props.debugApiHost}</Typography>) <AccordionDetails>
</span> <Typography component="div">
<ConnectToHost hostName={'debug_api_host'} defaultHost={props.debugApiHost} /> <ol>
</div> <li>Check the status of your node by running the below command to see if your node is running.</li>
<div> <CodeBlockTabs
{props.nodeHealth?.status !== 'ok' ? ( showLineNumbers
<Typography component="div" variant="body2" gutterBottom style={{ margin: '15px' }}> linux={`sudo systemctl status bee`}
We cannot connect to your nodes debug API at{' '} mac={`brew services status swarm-bee`}
<Typography variant="button">{props.debugApiHost}</Typography>. Please check the following to troubleshoot />
your issue. <li>
<Accordion style={{ marginTop: '20px' }}> If your node is running, check your firewall settings to make sure that port 1635 (or your custom
<AccordionSummary expandIcon={<ExpandMoreSharp />} aria-controls="panel1a-content" id="panel1a-header"> specified port) is bound to localhost. If your node is not running try executing the below command
<Typography>Troubleshoot</Typography> to start your bee node
</AccordionSummary> </li>
<AccordionDetails> <MuiAlert
<Typography component="div"> style={{ marginTop: '10px', marginBottom: '10px' }}
<ol> elevation={6}
<li> variant="filled"
Check the status of your node by running the below command to see if your node is running. severity="error"
</li> >
<CodeBlockTabs Your debug node API should never be completely open to the internet. If you want to connect
showLineNumbers remotely, make sure your firewall settings are set to only allow specific trusted IP addresses and
linux={`sudo systemctl status bee`} block all other ports. A simple google search for &quot;what is my ip&quot; will show you your
mac={`brew services status swarm-bee`} computers public IP address to allow.
/> </MuiAlert>
<li> <CodeBlockTabs
If your node is running, check your firewall settings to make sure that port 1635 (or your showLineNumbers
custom specified port) is bound to localhost. If your node is not running try executing the linux={`sudo systemctl start bee`}
below command to start your bee node mac={`brew services start swarm-bee`}
</li> />
<MuiAlert <li>Run the commands to validate your node is running and see the log output.</li>
style={{ marginTop: '10px', marginBottom: '10px' }} <CodeBlockTabs
elevation={6} showLineNumbers
variant="filled" linux={`sudo systemctl status bee \njournalctl --lines=100 --follow --unit bee`}
severity="error" mac={`brew services status swarm-bee \ntail -f /usr/local/var/log/swarm-bee/bee.log`}
> />
Your debug node API should never be completely open to the internet. If you want to connect <li>
remotely, make sure your firewall settings are set to only allow specific trusted IP addresses Lastly, check your nodes configuration settings to validate the debug API is enabled and the Cross
and block all other ports. A simple google search for &quot;what is my ip&quot; will show you Origin Resource Sharing (CORS) setting is configured to allow your host. Config parameter{' '}
your computers public IP address to allow. <strong>debug-api-enable</strong> must be set to <strong>true</strong> and{' '}
</MuiAlert> <strong>cors-allowed-origins</strong> must be set to your host domain or IP. If edits are made to
<CodeBlockTabs the configuration run the restart command below for changes to take effect.
showLineNumbers </li>
linux={`sudo systemctl start bee`} <CodeBlockTabs
mac={`brew services start swarm-bee`} showLineNumbers
/> linux={`sudo vi /etc/bee/bee.yaml\nsudo systemctl restart bee`}
<li>Run the commands to validate your node is running and see the log output.</li> mac={`sudo vi /etc/bee/bee.yaml \nbrew services restart swarm-bee`}
<CodeBlockTabs />
showLineNumbers </ol>
linux={`sudo systemctl status bee \njournalctl --lines=100 --follow --unit bee`} </Typography>
mac={`brew services status swarm-bee \ntail -f /usr/local/var/log/swarm-bee/bee.log`} </AccordionDetails>
/> </Accordion>
<li> </Typography>
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{' '}
<strong>debug-api-enable</strong> must be set to <strong>true</strong> and{' '}
<strong>cors-allowed-origins</strong> 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.
</li>
<CodeBlockTabs
showLineNumbers
linux={`sudo vi /etc/bee/bee.yaml\nsudo systemctl restart bee`}
mac={`sudo vi /etc/bee/bee.yaml \nbrew services restart swarm-bee`}
/>
</ol>
</Typography>
</AccordionDetails>
</Accordion>
</Typography>
) : null}
</div>
</div> </div>
</div> </div>
) )
@@ -1,56 +1,40 @@
import type { ReactElement } from 'react' import type { ReactElement } from 'react'
import { Typography } from '@material-ui/core/' import { Typography } from '@material-ui/core/'
import { CheckCircle, Warning } from '@material-ui/icons/'
import EthereumAddress from '../../../components/EthereumAddress' import EthereumAddress from '../../../components/EthereumAddress'
import type { NodeAddresses } from '@ethersphere/bee-js'
interface Props { type Props = StatusEthereumConnectionHook
nodeAddresses: NodeAddresses | null
isLoadingNodeAddresses: boolean
}
export default function EthereumConnectionCheck(props: Props): ReactElement { export default function EthereumConnectionCheck({ isLoading, isOk, nodeAddresses }: Props): ReactElement | null {
return ( if (isLoading) return null
<div>
<p>Connect to the ethereum blockchain.</p> if (isOk) {
<div style={{ marginBottom: '10px' }}> return (
{ <div>
// FIXME: this should be broken up <Typography variant="subtitle1" gutterBottom>
/* eslint-disable no-nested-ternary */ Node Address
props.nodeAddresses?.ethereum ? ( </Typography>
<div> <EthereumAddress address={nodeAddresses?.ethereum} network={'goerli'} />
<CheckCircle style={{ color: '#32c48d', marginRight: '7px', height: '18px' }} />
<span>Your connected to the Ethereum network</span>
</div>
) : props.isLoadingNodeAddresses ? null : (
<div>
<Warning style={{ color: '#ff9800', marginRight: '7px', height: '18px' }} />
<span>Your not connected to the Ethereum network. </span>
<p>
Your Bee node must have access to the Ethereum blockchain, so that it can interact and deploy your
chequebook contract. You can run{' '}
<a href="https://github.com/goerli/testnet" rel="noreferrer" target="_blank">
your own Goerli node
</a>
, or use a provider such as{' '}
<a href="https://rpc.slock.it/goerli" rel="noreferrer" target="_blank">
rpc.slock.it/goerli
</a>{' '}
or{' '}
<a href="https://infura.io/" rel="noreferrer" target="_blank">
Infura
</a>
. By default, Bee expects a local Goerli node at http://localhost:8545. To use a provider instead,
simply change your <strong>--swap-endpoint</strong> in your configuration file.
</p>
</div>
) /* eslint-enable no-nested-ternary */
}
</div> </div>
<Typography variant="subtitle1" gutterBottom> )
Node Address }
</Typography>
<EthereumAddress address={props.nodeAddresses?.ethereum} network={'goerli'} /> return (
</div> <p>
Your Bee node must have access to the Ethereum blockchain, so that it can interact and deploy your chequebook
contract. You can run{' '}
<a href="https://github.com/goerli/testnet" rel="noreferrer" target="_blank">
your own Goerli node
</a>
, or use a provider such as{' '}
<a href="https://rpc.slock.it/goerli" rel="noreferrer" target="_blank">
rpc.slock.it/goerli
</a>{' '}
or{' '}
<a href="https://infura.io/" rel="noreferrer" target="_blank">
Infura
</a>
. By default, Bee expects a local Goerli node at http://localhost:8545. To use a provider instead, simply change
your <strong>--swap-endpoint</strong> in your configuration file.
</p>
) )
} }
@@ -1,35 +1,29 @@
import React, { ReactElement } from 'react' import React, { ReactElement } from 'react'
import { Typography, Accordion, AccordionSummary, AccordionDetails } from '@material-ui/core/' 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 ConnectToHost from '../../../components/ConnectToHost'
import CodeBlockTabs from '../../../components/CodeBlockTabs' import CodeBlockTabs from '../../../components/CodeBlockTabs'
import { apiHost } from '../../../constants'
interface Props { type Props = StatusHookCommon
nodeApiHealth: boolean
apiHost: string export default function NodeConnectionCheck({ isLoading, isOk }: Props): ReactElement | null {
} if (isLoading) return null
export default function NodeConnectionCheck(props: Props): ReactElement {
return ( return (
<div> <div>
<p>Connect to Bee Node API</p>
<div style={{ display: 'flex', marginBottom: '25px' }}> <div style={{ display: 'flex', marginBottom: '25px' }}>
{props.nodeApiHealth ? (
<CheckCircle style={{ color: '#32c48d', marginRight: '7px', height: '18px' }} />
) : (
<Error style={{ color: '#c9201f', marginRight: '7px', height: '18px' }} />
)}
<span style={{ marginRight: '15px' }}> <span style={{ marginRight: '15px' }}>
Node API (<Typography variant="button">{props.apiHost}</Typography>) Node API (<Typography variant="button">{apiHost}</Typography>)
</span> </span>
<ConnectToHost hostName="api_host" defaultHost={props.apiHost} /> <ConnectToHost hostName="api_host" defaultHost={apiHost} />
</div> </div>
<div> <div>
{!props.nodeApiHealth ? ( {!isOk && (
<Typography component="div" variant="body2" gutterBottom style={{ margin: '15px' }}> <Typography component="div" variant="body2" gutterBottom style={{ margin: '15px' }}>
We cannot connect to your nodes API at <Typography variant="button">{props.apiHost}</Typography>. Please We cannot connect to your nodes API at <Typography variant="button">{apiHost}</Typography>. Please check the
check the following to troubleshoot your issue. following to troubleshoot your issue.
<Accordion style={{ marginTop: '20px' }}> <Accordion style={{ marginTop: '20px' }}>
<AccordionSummary expandIcon={<ExpandMoreSharp />} aria-controls="panel1a-content" id="panel1a-header"> <AccordionSummary expandIcon={<ExpandMoreSharp />} aria-controls="panel1a-content" id="panel1a-header">
<Typography>Troubleshoot</Typography> <Typography>Troubleshoot</Typography>
@@ -64,7 +58,7 @@ export default function NodeConnectionCheck(props: Props): ReactElement {
</AccordionDetails> </AccordionDetails>
</Accordion> </Accordion>
</Typography> </Typography>
) : null} )}
</div> </div>
</div> </div>
) )
+37 -45
View File
@@ -1,53 +1,45 @@
import React, { ReactElement } from 'react' import type { ReactElement } from 'react'
import { Typography } from '@material-ui/core/' import { Typography } from '@material-ui/core/'
import { CheckCircle, Warning } from '@material-ui/icons/'
import { Topology } from '@ethersphere/bee-js'
interface Props { type Props = StatusTopologyHook
nodeTopology: Topology | null
isLoadingNodeTopology: boolean
}
export default function PeerConnection(props: Props): ReactElement { export default function PeerConnection({ isLoading, isOk, topology }: Props): ReactElement | null {
return ( if (isLoading) return null
<div>
<p>Connect to Peers</p> const peers = (
<div style={{ marginBottom: '10px' }}> <div style={{ display: 'flex', marginTop: '15px' }}>
html_url <div style={{ marginRight: '30px' }}>
{ <Typography component="div" variant="subtitle1" gutterBottom color="textSecondary">
// FIXME: this should be broken up <span>Connected Peers</span>
/* eslint-disable no-nested-ternary */ </Typography>
props.nodeTopology?.connected && props.nodeTopology?.connected > 0 ? ( <Typography component="h2" variant="h5">
<div> {topology?.connected ? topology.connected : '-'}
<CheckCircle style={{ color: '#32c48d', marginRight: '7px', height: '18px' }} /> </Typography>
<span>Your connected to {props.nodeTopology.connected} peers!</span>
</div>
) : props.isLoadingNodeTopology ? null : (
<div>
<Warning style={{ color: '#ff9800', marginRight: '7px', height: '18px' }} />
<span>Your node is not connected to any peers</span>
</div>
) /* eslint-enable no-nested-ternary */
}
</div> </div>
<div style={{ display: 'flex' }}> <div>
<div style={{ marginRight: '30px' }}> <Typography component="div" variant="subtitle1" gutterBottom color="textSecondary">
<Typography component="div" variant="subtitle1" gutterBottom color="textSecondary"> <span>Discovered Nodes</span>
<span>Connected Peers</span> </Typography>
</Typography> <Typography component="h2" variant="h5">
<Typography component="h2" variant="h5"> {topology?.population ? topology.population : '-'}
{props.nodeTopology?.connected} </Typography>
</Typography>
</div>
<div>
<Typography component="div" variant="subtitle1" gutterBottom color="textSecondary">
<span>Discovered Nodes</span>
</Typography>
<Typography component="h2" variant="h5">
{props.nodeTopology?.population}
</Typography>
</div>
</div> </div>
</div> </div>
) )
if (isOk) {
return (
<>
<span>You are connected to peers!</span>
{peers}
</>
)
}
return (
<>
<span>Your node is not connected to any peers</span>
{peers}
</>
)
} }
+60 -73
View File
@@ -1,81 +1,68 @@
import React, { ReactElement } from 'react' import type { ReactElement } from 'react'
import { Typography } from '@material-ui/core/' import { Typography } from '@material-ui/core/'
import { CheckCircle, Warning } from '@material-ui/icons/'
import CodeBlockTabs from '../../../components/CodeBlockTabs' import CodeBlockTabs from '../../../components/CodeBlockTabs'
import { Health } from '@ethersphere/bee-js'
interface Props { type Props = StatusNodeVersionHook
beeRelease: LatestBeeRelease | null
isLoadingBeeRelease: boolean
nodeHealth: Health | null
}
export default function VersionCheck(props: Props): ReactElement { export default function VersionCheck({
return ( isLoading,
<div> isOk,
<p> userVersion,
Check to make sure the latest version of{' '} latestVersion,
<a href="https://github.com/ethersphere/bee" rel="noreferrer" target="_blank"> latestUrl,
Bee }: Props): ReactElement | null {
</a>{' '} if (isLoading) return null
is running
</p> const version = (
{ <div style={{ display: 'flex' }}>
// FIXME: this should be broken up <div style={{ marginRight: '30px' }}>
/* eslint-disable no-nested-ternary */ <p>
props.beeRelease && props.beeRelease.name === `v${props.nodeHealth?.version?.split('-')[0]}` ? ( <span>User Version</span>
<div> </p>
<CheckCircle style={{ color: '#32c48d', marginRight: '7px', height: '18px' }} /> <Typography component="h5" variant="h5">
<span>Your running the latest version of Bee</span> <span>{userVersion}</span>
</div> </Typography>
) : props.isLoadingBeeRelease ? null : ( </div>
<div> <div>
<Warning style={{ color: '#ff9800', marginRight: '7px', height: '18px' }} /> <p>
<span> <span>Latest Version</span>
Your Bee version is out of date. Please update to the{' '} </p>
<a href={props.beeRelease?.html_url} rel="noreferrer" target="_blank"> <Typography component="h5" variant="h5">
latest <span>{latestVersion}</span>
</a>{' '} </Typography>
before continuing. Rerun the installation script below to upgrade. Reference the docs for help with
updating.{' '}
<a
href="https://docs.ethswarm.org/docs/installation/manual#upgrading-bee"
rel="noreferrer"
target="_blank"
>
Docs
</a>
</span>
<CodeBlockTabs
showLineNumbers
linux={`bee version\nwget https://github.com/ethersphere/bee/releases/download/${
props.beeRelease?.name
}/bee_${props.nodeHealth?.version?.split('-')[0]}_amd64.deb\nsudo dpkg -i bee_${
props.nodeHealth?.version?.split('-')[0]
}_amd64.deb`}
mac={`bee version\nbrew tap ethersphere/tap\nbrew install swarm-bee\nbrew services start swarm-bee`}
/>
</div>
) /* eslint-enable no-nested-ternary */
}
<div style={{ display: 'flex' }}>
<div style={{ marginRight: '30px' }}>
<p>
<span>Current Version</span>
</p>
<Typography component="h5" variant="h5">
<span>{props.nodeHealth?.version ? ` v${props.nodeHealth?.version?.split('-')[0]}` : '-'}</span>
</Typography>
</div>
<div>
<p>
<span>Latest Version</span>
</p>
<Typography component="h5" variant="h5">
<span>{props.beeRelease && props.beeRelease.name ? props.beeRelease.name : '-'}</span>
</Typography>
</div>
</div> </div>
</div> </div>
) )
// Running latest bee version
if (isOk) {
return (
<>
<span>You are running the latest version of Bee</span>
{version}
</>
)
}
// Old version or not connected to bee debug API
return (
<>
<span>
Your Bee version is out of date. Please update to the{' '}
<a href={latestUrl} rel="noreferrer" target="_blank">
latest
</a>{' '}
before continuing. Rerun the installation script below to upgrade. Reference the docs for help with updating.{' '}
<a href="https://docs.ethswarm.org/docs/installation/manual#upgrading-bee" rel="noreferrer" target="_blank">
Docs
</a>
</span>
<CodeBlockTabs
showLineNumbers
linux={`bee version\nwget https://github.com/ethersphere/bee/releases/download/${latestVersion}/bee_${latestVersion}_amd64.deb\nsudo dpkg -i bee_${latestVersion}_amd64.deb`}
mac={`bee version\nbrew tap ethersphere/tap\nbrew install swarm-bee\nbrew services start swarm-bee`}
/>
{version}
</>
)
} }
+87 -115
View File
@@ -4,23 +4,15 @@ import { Link } from 'react-router-dom'
import { createStyles, makeStyles } from '@material-ui/core/styles' import { createStyles, makeStyles } from '@material-ui/core/styles'
import { Card, CardContent, Typography, Chip, Button } from '@material-ui/core/' import { Card, CardContent, Typography, Chip, Button } from '@material-ui/core/'
import { CheckCircle, Error, ArrowRight, ArrowDropUp } from '@material-ui/icons/' import { CheckCircle, Error, ArrowRight, ArrowDropUp } from '@material-ui/icons/'
import { Skeleton } from '@material-ui/lab' import { NodeAddresses, Topology } from '@ethersphere/bee-js'
import type { Health, NodeAddresses, Topology } from '@ethersphere/bee-js'
const useStyles = makeStyles(() => const useStyles = makeStyles(() =>
createStyles({ createStyles({
root: { root: {
display: 'flex', display: 'flex',
}, flex: '1 1 auto',
details: {
display: 'flex',
flex: '1 0 auto',
flexDirection: 'column', flexDirection: 'column',
}, },
content: {
flex: '1 0 auto',
},
status: { status: {
color: '#2145a0', color: '#2145a0',
backgroundColor: '#e1effe', backgroundColor: '#e1effe',
@@ -29,124 +21,104 @@ const useStyles = makeStyles(() =>
) )
interface Props { interface Props {
nodeHealth: Health | null nodeAddresses: NodeAddresses | null
loadingNodeHealth: boolean nodeTopology: Topology | null
beeRelease: LatestBeeRelease | null userBeeVersion: string | null
loadingBeeRelease: boolean latestBeeVersion: string | null
nodeAddresses: NodeAddresses isOk: boolean
nodeTopology: Topology
loadingNodeTopology: boolean
setStatusChecksVisible: (value: boolean) => void
} }
function StatusCard(props: Props): ReactElement { function StatusCard({
userBeeVersion,
nodeAddresses,
nodeTopology,
latestBeeVersion,
isOk,
}: Props): ReactElement | null {
const classes = useStyles() const classes = useStyles()
const [underlayAddressesVisible, setUnderlayAddresessVisible] = useState<boolean>(false) const [underlayAddressesVisible, setUnderlayAddresessVisible] = useState<boolean>(false)
return ( return (
<div> <Card>
<Card className={classes.root}> <CardContent className={classes.root}>
{!props.loadingNodeHealth && props.nodeHealth ? ( <Typography component="h5" variant="h5" style={{ display: 'flex', justifyContent: 'space-between' }}>
<div className={classes.details}> {isOk && (
<CardContent className={classes.content}> <div>
<Typography component="h5" variant="h5" style={{ display: 'flex', justifyContent: 'space-between' }}> <CheckCircle style={{ color: '#32c48d', marginRight: '7px' }} />
{props.nodeHealth.status === 'ok' ? ( <span>Your Bee node is running as expected</span>
<div> </div>
<CheckCircle style={{ color: '#32c48d', marginRight: '7px' }} /> )}
<span>Connected to Bee Node</span> {!isOk && (
</div> <div>
<Error style={{ color: '#c9201f', marginRight: '7px' }} />
<span>Could not connect to Bee Node</span>
</div>
)}
</Typography>
{isOk && (
<>
<div style={{ marginBottom: '20px' }}>
<span style={{ marginRight: '20px' }}>Discovered Nodes: {nodeTopology?.population}</span>
<span style={{ marginRight: '20px' }}>
<span>Connected Peers: </span>
<Link to="/peers/">{nodeTopology?.connected}</Link>
</span>
</div>
<div>
<Typography component="div" variant="subtitle2" gutterBottom>
<span>AGENT: </span>
<a href="https://github.com/ethersphere/bee" rel="noreferrer" target="_blank">
Bee
</a>{' '}
<span>{userBeeVersion || '-'}</span>
{userBeeVersion && latestBeeVersion && userBeeVersion === latestBeeVersion ? (
<Chip
style={{ marginLeft: '7px', color: '#2145a0' }}
size="small"
label="latest"
className={classes.status}
/>
) : ( ) : (
<Typography variant="button">update</Typography>
)}
</Typography>
<Typography component="div" variant="subtitle2" gutterBottom>
<span>PUBLIC KEY: </span>
<span>{nodeAddresses?.public_key ? nodeAddresses.public_key : '-'}</span>
</Typography>
<Typography component="div" variant="subtitle2" gutterBottom>
<span>PSS PUBLIC KEY: </span>
<span>{nodeAddresses?.pss_public_key ? nodeAddresses.pss_public_key : '-'}</span>
</Typography>
<Typography component="div" variant="subtitle2" gutterBottom>
<span>OVERLAY ADDRESS (PEER ID): </span>
<span>{nodeAddresses?.overlay ? nodeAddresses.overlay : '-'}</span>
</Typography>
<Typography component="div" variant="subtitle2" gutterBottom>
<Typography component="div" onClick={() => setUnderlayAddresessVisible(!underlayAddressesVisible)}>
<Button color="primary" style={{ padding: 0, marginTop: '6px' }}>
{underlayAddressesVisible ? (
<ArrowDropUp style={{ fontSize: '12px' }} />
) : (
<ArrowRight style={{ fontSize: '12px' }} />
)}
<span>Underlay Addresses</span>
</Button>
</Typography>
{underlayAddressesVisible && (
<div> <div>
<Error style={{ color: '#c9201f', marginRight: '7px' }} /> {nodeAddresses?.underlay.map(item => (
<span>Could not connect to Bee Node</span> <li key={item}>{item}</li>
))}
</div> </div>
)} )}
<Button
variant="outlined"
color="primary"
size="small"
style={{ marginLeft: '12px' }}
onClick={() => props.setStatusChecksVisible(true)}
>
View Status Checks
</Button>
</Typography> </Typography>
<div style={{ marginBottom: '20px' }}> </div>
<span style={{ marginRight: '20px' }}>Discovered Nodes: {props.nodeTopology.population}</span> </>
<span style={{ marginRight: '20px' }}>
<span>Connected Peers: </span>
<Link to="/peers/">{props.nodeTopology.connected}</Link>
</span>
</div>
<div>
<Typography component="div" variant="subtitle2" gutterBottom>
<span>AGENT: </span>
<a href="https://github.com/ethersphere/bee" rel="noreferrer" target="_blank">
Bee
</a>
<span>{props.nodeHealth?.version ? ` v${props.nodeHealth.version}` : '-'}</span>
{
// FIXME: this should be broken up
/* eslint-disable no-nested-ternary */
props.beeRelease && props.beeRelease.name === `v${props.nodeHealth?.version?.split('-')[0]}` ? (
<Chip
style={{ marginLeft: '7px', color: '#2145a0' }}
size="small"
label="latest"
className={classes.status}
/>
) : props.loadingBeeRelease ? (
''
) : (
<Typography variant="button">update</Typography>
)
/* eslint-enable no-nested-ternary */
}
</Typography>
<Typography component="div" variant="subtitle2" gutterBottom>
<span>PUBLIC KEY: </span>
<span>{props.nodeAddresses.public_key ? props.nodeAddresses.public_key : '-'}</span>
</Typography>
<Typography component="div" variant="subtitle2" gutterBottom>
<span>PSS PUBLIC KEY: </span>
<span>{props.nodeAddresses.pss_public_key ? props.nodeAddresses.pss_public_key : '-'}</span>
</Typography>
<Typography component="div" variant="subtitle2" gutterBottom>
<Typography component="div" style={{ marginTop: '20px' }}>
<span>OVERLAY ADDRESS (PEER ID): </span>
<span>{props.nodeAddresses.overlay ? props.nodeAddresses.overlay : '-'}</span>
</Typography>
<Typography component="div" onClick={() => setUnderlayAddresessVisible(!underlayAddressesVisible)}>
<Button color="primary" style={{ padding: 0, marginTop: '6px' }}>
{underlayAddressesVisible ? (
<ArrowDropUp style={{ fontSize: '12px' }} />
) : (
<ArrowRight style={{ fontSize: '12px' }} />
)}
<span>Underlay Addresses</span>
</Button>
</Typography>
{underlayAddressesVisible ? (
<div>
{props.nodeAddresses.underlay
? props.nodeAddresses.underlay.map(item => <li key={item}>{item}</li>)
: '-'}
</div>
) : null}
</Typography>
</div>
</CardContent>
</div>
) : (
<div style={{ padding: '16px' }}>
<Skeleton width={650} height={32} animation="wave" />
<Skeleton width={650} height={24} animation="wave" />
<Skeleton width={650} height={24} animation="wave" />
</div>
)} )}
</Card> </CardContent>
</div> </Card>
) )
} }
+56 -124
View File
@@ -1,143 +1,75 @@
import { useState, useEffect, ReactElement } from 'react' import { ReactElement } from 'react'
import axios from 'axios'
import { Container, CircularProgress } from '@material-ui/core' import { Container, CircularProgress } from '@material-ui/core'
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
import NodeSetupWorkflow from './NodeSetupWorkflow' import NodeSetupWorkflow from './NodeSetupWorkflow'
import StatusCard from './StatusCard' import StatusCard from './StatusCard'
import EthereumAddressCard from '../../components/EthereumAddressCard' import EthereumAddressCard from '../../components/EthereumAddressCard'
import { import {
useApiHealth, useStatusEthereumConnection,
useDebugApiHealth, useStatusNodeVersion,
useApiNodeAddresses, useStatusDebugConnection,
useApiChequebookAddress, useStatusConnection,
useApiNodeTopology, useStatusTopology,
useApiChequebookBalance, useStatusChequebook,
} from '../../hooks/apiHooks' } from '../../hooks/status'
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
width: '100%',
display: 'grid',
rowGap: theme.spacing(3),
},
}),
)
export default function Status(): ReactElement { export default function Status(): ReactElement {
const [beeRelease, setBeeRelease] = useState<LatestBeeRelease | null>(null) const classes = useStyles()
const [isLoadingBeeRelease, setIsLoadingBeeRelease] = useState<boolean>(false)
const [apiHost, setApiHost] = useState('') const nodeVersion = useStatusNodeVersion()
const [debugApiHost, setDebugApiHost] = useState('') const ethereumConnection = useStatusEthereumConnection()
const debugApiConnection = useStatusDebugConnection()
const apiConnection = useStatusConnection()
const topology = useStatusTopology()
const chequebook = useStatusChequebook()
const [statusChecksVisible, setStatusChecksVisible] = useState<boolean>(false) const checks = [nodeVersion, ethereumConnection, debugApiConnection, apiConnection, topology, chequebook]
const { health, isLoadingHealth } = useApiHealth() // If any check data are still loading
const { nodeHealth, isLoadingNodeHealth } = useDebugApiHealth() if (!checks.every(c => !c.isLoading)) {
const { nodeAddresses, isLoadingNodeAddresses } = useApiNodeAddresses() return (
const { chequebookAddress, isLoadingChequebookAddress } = useApiChequebookAddress() <Container style={{ textAlign: 'center', padding: '50px' }}>
const { topology: nodeTopology, isLoading: isLoadingNodeTopology } = useApiNodeTopology() <CircularProgress />
const { chequebookBalance, isLoadingChequebookBalance } = useApiChequebookBalance() </Container>
)
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)
})
} }
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 ( return (
<div> <div className={classes.root}>
{nodeHealth?.status === 'ok' && <StatusCard
health && userBeeVersion={nodeVersion.userVersion}
beeRelease && latestBeeVersion={nodeVersion.latestVersion}
beeRelease.name === `v${nodeHealth?.version?.split('-')[0]}` && isOk={checks.every(c => c.isOk)}
nodeAddresses?.ethereum && nodeTopology={topology.topology}
chequebookAddress?.chequebookaddress && nodeAddresses={ethereumConnection.nodeAddresses}
chequebookBalance && />
chequebookBalance?.totalBalance > 0 && {ethereumConnection.nodeAddresses && chequebook.chequebookAddress && (
nodeTopology?.connected && <EthereumAddressCard
nodeTopology?.connected > 0 && nodeAddresses={ethereumConnection.nodeAddresses}
!statusChecksVisible ? ( isLoadingNodeAddresses={ethereumConnection.isLoading}
<div> chequebookAddress={chequebook.chequebookAddress}
<StatusCard isLoadingChequebookAddress={chequebook.isLoading}
nodeHealth={nodeHealth}
loadingNodeHealth={isLoadingNodeHealth}
beeRelease={beeRelease}
loadingBeeRelease={isLoadingBeeRelease}
nodeAddresses={nodeAddresses}
loadingNodeTopology={isLoadingNodeTopology}
nodeTopology={nodeTopology}
setStatusChecksVisible={setStatusChecksVisible}
/>
<EthereumAddressCard
nodeAddresses={nodeAddresses}
isLoadingNodeAddresses={isLoadingNodeAddresses}
chequebookAddress={chequebookAddress}
isLoadingChequebookAddress={isLoadingChequebookAddress}
/>
</div>
) : isLoadingNodeHealth ||
isLoadingHealth ||
isLoadingChequebookAddress ||
isLoadingNodeTopology ||
isLoadingBeeRelease ||
isLoadingNodeAddresses ||
isLoadingBeeRelease ||
isLoadingChequebookBalance ? (
<Container style={{ textAlign: 'center', padding: '50px' }}>
<CircularProgress />
</Container>
) : (
<NodeSetupWorkflow
beeRelease={beeRelease}
isLoadingBeeRelease={isLoadingBeeRelease}
nodeHealth={nodeHealth}
isLoadingNodeHealth={isLoadingNodeHealth}
nodeAddresses={nodeAddresses}
isLoadingNodeAddresses={isLoadingNodeAddresses}
nodeTopology={nodeTopology}
isLoadingNodeTopology={isLoadingNodeTopology}
nodeApiHealth={health}
isLoadingHealth={isLoadingHealth}
chequebookAddress={chequebookAddress}
isLoadingChequebookAddress={isLoadingChequebookAddress}
chequebookBalance={chequebookBalance}
isLoadingChequebookBalance={isLoadingChequebookBalance}
apiHost={apiHost}
debugApiHost={debugApiHost}
setStatusChecksVisible={setStatusChecksVisible}
/> />
)} )}
<NodeSetupWorkflow
nodeVersion={nodeVersion}
ethereumConnection={ethereumConnection}
debugApiConnection={debugApiConnection}
apiConnection={apiConnection}
topology={topology}
chequebook={chequebook}
/>
</div> </div>
) )
} }
+21
View File
@@ -4,3 +4,24 @@ interface LatestBeeRelease {
name: string name: string
html_url: 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
}