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({
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 (
<div>
<Card className={classes.root}>
{props.isLoadingNodeAddresses ? (
<div style={{ padding: '16px' }}>
<Skeleton width={300} height={30} animation="wave" />
<Skeleton width={300} height={50} animation="wave" />
</div>
) : (
<div className={classes.details}>
<CardContent className={classes.content}>
<Typography variant="subtitle1" gutterBottom>
Ethereum Address
</Typography>
<EthereumAddress address={props.nodeAddresses?.ethereum} network={'goerli'} />
</CardContent>
</div>
)}
{props.isLoadingChequebookAddress ? (
<div style={{ padding: '16px' }}>
<Skeleton width={300} height={30} animation="wave" />
<Skeleton width={300} height={50} animation="wave" />
</div>
) : (
<div className={classes.details}>
<CardContent className={classes.content}>
<Typography variant="subtitle1" gutterBottom>
Chequebook Contract Address
</Typography>
<EthereumAddress address={props.chequebookAddress?.chequebookaddress} network={'goerli'} />
</CardContent>
</div>
)}
</Card>
</div>
<Card className={classes.root}>
{props.isLoadingNodeAddresses ? (
<div style={{ padding: '16px' }}>
<Skeleton width={300} height={30} animation="wave" />
<Skeleton width={300} height={50} animation="wave" />
</div>
) : (
<div className={classes.details}>
<CardContent className={classes.content}>
<Typography variant="subtitle1" gutterBottom>
Ethereum Address
</Typography>
<EthereumAddress address={props.nodeAddresses?.ethereum} network={'goerli'} />
</CardContent>
</div>
)}
{props.isLoadingChequebookAddress ? (
<div style={{ padding: '16px' }}>
<Skeleton width={300} height={30} animation="wave" />
<Skeleton width={300} height={50} animation="wave" />
</div>
) : (
<div className={classes.details}>
<CardContent className={classes.content}>
<Typography variant="subtitle1" gutterBottom>
Chequebook Contract Address
</Typography>
<EthereumAddress address={props.chequebookAddress?.chequebookaddress} network={'goerli'} />
</CardContent>
</div>
)}
</Card>
)
}
+29
View File
@@ -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<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 { 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<unknown>, newValue: number) => {
setValue(newValue)
@@ -115,7 +126,6 @@ export default function Accounting(): ReactElement {
color: '#3f51b5',
},
},
selected: {},
}),
)((props: StyledTabProps) => <Tab disableRipple {...props} />)
@@ -125,7 +135,7 @@ export default function Accounting(): ReactElement {
// FIXME: this should be broken up
/* eslint-disable no-nested-ternary */
nodeHealth?.status === 'ok' && health ? (
<div>
<div className={classes.root}>
<AccountCard
chequebookAddress={chequebookAddress}
isLoadingChequebookAddress={isLoadingChequebookAddress}
@@ -140,20 +150,22 @@ export default function Accounting(): ReactElement {
chequebookAddress={chequebookAddress}
isLoadingChequebookAddress={isLoadingChequebookAddress}
/>
<AntTabs style={{ marginTop: '12px' }} value={value} onChange={handleChange} aria-label="ant example">
<AntTab label="Balances" {...a11yProps(0)} />
<AntTab label="Chequebook" {...a11yProps(1)} />
<AntTab label="Settlements" {...a11yProps(2)} />
</AntTabs>
<TabPanel value={value} index={0}>
<BalancesTable peerBalances={peerBalances} loading={isLoadingPeerBalances} />
</TabPanel>
<TabPanel value={value} index={1}>
<ChequebookTable peerCheques={peerCheques} loading={isLoadingPeerCheques} />
</TabPanel>
<TabPanel value={value} index={2}>
<SettlementsTable nodeSettlements={settlements} loading={isLoadingSettlements} />
</TabPanel>
<div>
<AntTabs style={{ marginTop: '12px' }} value={value} onChange={handleChange} aria-label="ant example">
<AntTab label="Balances" {...a11yProps(0)} />
<AntTab label="Chequebook" {...a11yProps(1)} />
<AntTab label="Settlements" {...a11yProps(2)} />
</AntTabs>
<TabPanel value={value} index={0}>
<BalancesTable peerBalances={peerBalances} loading={isLoadingPeerBalances} />
</TabPanel>
<TabPanel value={value} index={1}>
<ChequebookTable peerCheques={peerCheques} loading={isLoadingPeerCheques} />
</TabPanel>
<TabPanel value={value} index={2}>
<SettlementsTable nodeSettlements={settlements} loading={isLoadingSettlements} />
</TabPanel>
</div>
</div>
) : isLoadingHealth || isLoadingNodeHealth ? (
<Container style={{ textAlign: 'center', padding: '50px' }}>
+85 -163
View File
@@ -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 <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
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: <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(() => {
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 (
<div className={classes.root}>
<Typography variant="h4" gutterBottom>
<Paper className={classes.root}>
<Typography variant="h5" gutterBottom>
Node Setup
<span style={{ marginLeft: '25px' }}>
<Button variant="outlined" size="small" onClick={() => window.location.reload()}>
@@ -195,20 +126,22 @@ export default function NodeSetupWorkflow(props: Props): ReactElement {
</span>
</Typography>
<Stepper nonLinear activeStep={activeStep} orientation="vertical">
{steps.map((label, index) => (
{steps.map(({ label, condition, component, isLoading }, index) => (
<Step key={label}>
<StepLabel
onClick={() => setActiveStep(index === activeStep ? 6 : index)}
disabled={isLoading}
onClick={() => setActiveStep(index === activeStep ? steps.length : index)}
StepIconComponent={() => {
if (completed[index]) {
return <CheckCircle style={{ color: '#32c48d', height: '25px', cursor: 'pointer' }} />
} else {
return <Error style={{ color: '#c9201f', height: '25px', cursor: 'pointer' }} />
}
if (isLoading) return <Autorenew style={{ height: '25px', cursor: 'pointer' }} />
if (condition) return <CheckCircle style={{ color: '#32c48d', height: '25px', cursor: 'pointer' }} />
return <Error style={{ color: '#c9201f', height: '25px', cursor: 'pointer' }} />
}}
>
<StepButton
onClick={() => setActiveStep(index === activeStep ? 6 : index)}
disabled={isLoading}
onClick={() => setActiveStep(index === activeStep ? steps.length : index)}
style={{ justifyContent: 'space-between' }}
>
<div style={{ display: 'flex' }}>
@@ -220,14 +153,14 @@ export default function NodeSetupWorkflow(props: Props): ReactElement {
</StepButton>
</StepLabel>
<StepContent>
<Typography component="div">{getStepContent(index, props)}</Typography>
<Typography component="div">{component}</Typography>
<div className={classes.actionsContainer}>
<div>
<Button disabled={activeStep === 0} onClick={handleBack} className={classes.button}>
Back
</Button>
<Button variant="contained" color="primary" onClick={handleNext} className={classes.button}>
Next
{index < steps.length - 1 ? 'Next' : 'Finish'}
</Button>
</div>
</div>
@@ -235,17 +168,6 @@ export default function NodeSetupWorkflow(props: Props): ReactElement {
</Step>
))}
</Stepper>
{Object.values(completed).filter(value => value).length === 6 ? (
<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>
</Paper>
)
}
@@ -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 => (
<div>
<p style={{ marginBottom: '20px', display: 'flex' }}>
<span style={{ marginRight: '40px' }}>Deploy chequebook and fund with BZZ</span>
{props.chequebookAddress?.chequebookaddress ? <DepositModal /> : null}
</p>
<div style={{ marginBottom: '10px' }}>
{
// 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 (
<div>
<p style={{ marginBottom: '20px', display: 'flex' }}>
{chequebookAddress?.chequebookaddress && <DepositModal />}
</p>
<div style={{ marginBottom: '10px' }}>
{!(chequebookAddress?.chequebookaddress && chequebookBalance && chequebookBalance?.totalBalance > 0) && (
<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>
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 => (
</span>
<CodeBlockTabs showLineNumbers linux={`bee-get-addr`} mac={`bee-get-addr`} />
</div>
)
/* eslint-enable no-nested-ternary */
}
)}
</div>
<Typography variant="subtitle1" gutterBottom>
Chequebook Address
</Typography>
<EthereumAddress address={chequebookAddress?.chequebookaddress} network={'goerli'} />
</div>
<Typography variant="subtitle1" gutterBottom>
Chequebook Address
</Typography>
<EthereumAddress address={props.chequebookAddress?.chequebookaddress} network={'goerli'} />
</div>
)
)
}
export default ChequebookDeployFund
@@ -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 = (
<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 (
<div>
<p>Connect to Bee Node Debug API</p>
{changeDebugApiUrl}
<div>
<div style={{ display: 'flex', marginBottom: '25px' }}>
{props.nodeHealth?.status === 'ok' ? (
<CheckCircle style={{ color: '#32c48d', marginRight: '7px', height: '18px' }} />
) : (
<Error style={{ color: '#c9201f', marginRight: '7px', height: '18px' }} />
)}
<span style={{ marginRight: '15px' }}>
Debug API (<Typography variant="button">{props.debugApiHost}</Typography>)
</span>
<ConnectToHost hostName={'debug_api_host'} defaultHost={props.debugApiHost} />
</div>
<div>
{props.nodeHealth?.status !== 'ok' ? (
<Typography component="div" variant="body2" gutterBottom style={{ margin: '15px' }}>
We cannot connect to your nodes debug API at{' '}
<Typography variant="button">{props.debugApiHost}</Typography>. Please check the following to troubleshoot
your issue.
<Accordion style={{ marginTop: '20px' }}>
<AccordionSummary expandIcon={<ExpandMoreSharp />} aria-controls="panel1a-content" id="panel1a-header">
<Typography>Troubleshoot</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography component="div">
<ol>
<li>
Check the status of your node by running the below command to see if your node is running.
</li>
<CodeBlockTabs
showLineNumbers
linux={`sudo systemctl status bee`}
mac={`brew services status swarm-bee`}
/>
<li>
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
</li>
<MuiAlert
style={{ marginTop: '10px', marginBottom: '10px' }}
elevation={6}
variant="filled"
severity="error"
>
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 &quot;what is my ip&quot; will show you
your computers public IP address to allow.
</MuiAlert>
<CodeBlockTabs
showLineNumbers
linux={`sudo systemctl start bee`}
mac={`brew services start swarm-bee`}
/>
<li>Run the commands to validate your node is running and see the log output.</li>
<CodeBlockTabs
showLineNumbers
linux={`sudo systemctl status bee \njournalctl --lines=100 --follow --unit bee`}
mac={`brew services status swarm-bee \ntail -f /usr/local/var/log/swarm-bee/bee.log`}
/>
<li>
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>
<Typography component="div" variant="body2" gutterBottom style={{ margin: '15px' }}>
We cannot connect to your nodes debug API at <Typography variant="button">{debugApiHost}</Typography>. Please
check the following to troubleshoot your issue.
<Accordion style={{ marginTop: '20px' }}>
<AccordionSummary expandIcon={<ExpandMoreSharp />} aria-controls="panel1a-content" id="panel1a-header">
<Typography>Troubleshoot</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography component="div">
<ol>
<li>Check the status of your node by running the below command to see if your node is running.</li>
<CodeBlockTabs
showLineNumbers
linux={`sudo systemctl status bee`}
mac={`brew services status swarm-bee`}
/>
<li>
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
</li>
<MuiAlert
style={{ marginTop: '10px', marginBottom: '10px' }}
elevation={6}
variant="filled"
severity="error"
>
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 &quot;what is my ip&quot; will show you your
computers public IP address to allow.
</MuiAlert>
<CodeBlockTabs
showLineNumbers
linux={`sudo systemctl start bee`}
mac={`brew services start swarm-bee`}
/>
<li>Run the commands to validate your node is running and see the log output.</li>
<CodeBlockTabs
showLineNumbers
linux={`sudo systemctl status bee \njournalctl --lines=100 --follow --unit bee`}
mac={`brew services status swarm-bee \ntail -f /usr/local/var/log/swarm-bee/bee.log`}
/>
<li>
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>
</div>
</div>
)
@@ -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 (
<div>
<p>Connect to the ethereum blockchain.</p>
<div style={{ marginBottom: '10px' }}>
{
// FIXME: this should be broken up
/* eslint-disable no-nested-ternary */
props.nodeAddresses?.ethereum ? (
<div>
<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 */
}
export default function EthereumConnectionCheck({ isLoading, isOk, nodeAddresses }: Props): ReactElement | null {
if (isLoading) return null
if (isOk) {
return (
<div>
<Typography variant="subtitle1" gutterBottom>
Node Address
</Typography>
<EthereumAddress address={nodeAddresses?.ethereum} network={'goerli'} />
</div>
<Typography variant="subtitle1" gutterBottom>
Node Address
</Typography>
<EthereumAddress address={props.nodeAddresses?.ethereum} network={'goerli'} />
</div>
)
}
return (
<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 { 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 (
<div>
<p>Connect to Bee Node API</p>
<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' }}>
Node API (<Typography variant="button">{props.apiHost}</Typography>)
Node API (<Typography variant="button">{apiHost}</Typography>)
</span>
<ConnectToHost hostName="api_host" defaultHost={props.apiHost} />
<ConnectToHost hostName="api_host" defaultHost={apiHost} />
</div>
<div>
{!props.nodeApiHealth ? (
{!isOk && (
<Typography component="div" variant="body2" gutterBottom style={{ margin: '15px' }}>
We cannot connect to your nodes API at <Typography variant="button">{props.apiHost}</Typography>. Please
check the following to troubleshoot your issue.
We cannot connect to your nodes API at <Typography variant="button">{apiHost}</Typography>. Please check the
following to troubleshoot your issue.
<Accordion style={{ marginTop: '20px' }}>
<AccordionSummary expandIcon={<ExpandMoreSharp />} aria-controls="panel1a-content" id="panel1a-header">
<Typography>Troubleshoot</Typography>
@@ -64,7 +58,7 @@ export default function NodeConnectionCheck(props: Props): ReactElement {
</AccordionDetails>
</Accordion>
</Typography>
) : null}
)}
</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 { 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 (
<div>
<p>Connect to Peers</p>
<div style={{ marginBottom: '10px' }}>
html_url
{
// FIXME: this should be broken up
/* eslint-disable no-nested-ternary */
props.nodeTopology?.connected && props.nodeTopology?.connected > 0 ? (
<div>
<CheckCircle style={{ color: '#32c48d', marginRight: '7px', height: '18px' }} />
<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 */
}
export default function PeerConnection({ isLoading, isOk, topology }: Props): ReactElement | null {
if (isLoading) return null
const peers = (
<div style={{ display: 'flex', marginTop: '15px' }}>
<div style={{ marginRight: '30px' }}>
<Typography component="div" variant="subtitle1" gutterBottom color="textSecondary">
<span>Connected Peers</span>
</Typography>
<Typography component="h2" variant="h5">
{topology?.connected ? topology.connected : '-'}
</Typography>
</div>
<div style={{ display: 'flex' }}>
<div style={{ marginRight: '30px' }}>
<Typography component="div" variant="subtitle1" gutterBottom color="textSecondary">
<span>Connected Peers</span>
</Typography>
<Typography component="h2" variant="h5">
{props.nodeTopology?.connected}
</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>
<Typography component="div" variant="subtitle1" gutterBottom color="textSecondary">
<span>Discovered Nodes</span>
</Typography>
<Typography component="h2" variant="h5">
{topology?.population ? topology.population : '-'}
</Typography>
</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 { 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 (
<div>
<p>
Check to make sure the latest version of{' '}
<a href="https://github.com/ethersphere/bee" rel="noreferrer" target="_blank">
Bee
</a>{' '}
is running
</p>
{
// FIXME: this should be broken up
/* eslint-disable no-nested-ternary */
props.beeRelease && props.beeRelease.name === `v${props.nodeHealth?.version?.split('-')[0]}` ? (
<div>
<CheckCircle style={{ color: '#32c48d', marginRight: '7px', height: '18px' }} />
<span>Your running the latest version of Bee</span>
</div>
) : props.isLoadingBeeRelease ? null : (
<div>
<Warning style={{ color: '#ff9800', marginRight: '7px', height: '18px' }} />
<span>
Your Bee version is out of date. Please update to the{' '}
<a href={props.beeRelease?.html_url} 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/${
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>
export default function VersionCheck({
isLoading,
isOk,
userVersion,
latestVersion,
latestUrl,
}: Props): ReactElement | null {
if (isLoading) return null
const version = (
<div style={{ display: 'flex' }}>
<div style={{ marginRight: '30px' }}>
<p>
<span>User Version</span>
</p>
<Typography component="h5" variant="h5">
<span>{userVersion}</span>
</Typography>
</div>
<div>
<p>
<span>Latest Version</span>
</p>
<Typography component="h5" variant="h5">
<span>{latestVersion}</span>
</Typography>
</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 { 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<boolean>(false)
return (
<div>
<Card className={classes.root}>
{!props.loadingNodeHealth && props.nodeHealth ? (
<div className={classes.details}>
<CardContent className={classes.content}>
<Typography component="h5" variant="h5" style={{ display: 'flex', justifyContent: 'space-between' }}>
{props.nodeHealth.status === 'ok' ? (
<div>
<CheckCircle style={{ color: '#32c48d', marginRight: '7px' }} />
<span>Connected to Bee Node</span>
</div>
<Card>
<CardContent className={classes.root}>
<Typography component="h5" variant="h5" style={{ display: 'flex', justifyContent: 'space-between' }}>
{isOk && (
<div>
<CheckCircle style={{ color: '#32c48d', marginRight: '7px' }} />
<span>Your Bee node is running as expected</span>
</div>
)}
{!isOk && (
<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>
<Error style={{ color: '#c9201f', marginRight: '7px' }} />
<span>Could not connect to Bee Node</span>
{nodeAddresses?.underlay.map(item => (
<li key={item}>{item}</li>
))}
</div>
)}
<Button
variant="outlined"
color="primary"
size="small"
style={{ marginLeft: '12px' }}
onClick={() => props.setStatusChecksVisible(true)}
>
View Status Checks
</Button>
</Typography>
<div style={{ marginBottom: '20px' }}>
<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>
</div>
</>
)}
</Card>
</div>
</CardContent>
</Card>
)
}
+56 -124
View File
@@ -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<LatestBeeRelease | null>(null)
const [isLoadingBeeRelease, setIsLoadingBeeRelease] = useState<boolean>(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<boolean>(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 (
<Container style={{ textAlign: 'center', padding: '50px' }}>
<CircularProgress />
</Container>
)
}
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 (
<div>
{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 ? (
<div>
<StatusCard
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}
<div className={classes.root}>
<StatusCard
userBeeVersion={nodeVersion.userVersion}
latestBeeVersion={nodeVersion.latestVersion}
isOk={checks.every(c => c.isOk)}
nodeTopology={topology.topology}
nodeAddresses={ethereumConnection.nodeAddresses}
/>
{ethereumConnection.nodeAddresses && chequebook.chequebookAddress && (
<EthereumAddressCard
nodeAddresses={ethereumConnection.nodeAddresses}
isLoadingNodeAddresses={ethereumConnection.isLoading}
chequebookAddress={chequebook.chequebookAddress}
isLoadingChequebookAddress={chequebook.isLoading}
/>
)}
<NodeSetupWorkflow
nodeVersion={nodeVersion}
ethereumConnection={ethereumConnection}
debugApiConnection={debugApiConnection}
apiConnection={apiConnection}
topology={topology}
chequebook={chequebook}
/>
</div>
)
}
+21
View File
@@ -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
}