feat: optional status checks (e.g. connected peers > 0 or funded chequebook) (#331)

* feat: make some check optional (e.g. connected peers > 0 or funded chequebook)

* fix: alter setup step text to better describe what needs to be done

* refactor: rename isOk from boolean value to checkState enum

* fix: add checking for any error
This commit is contained in:
Vojtech Simetka
2022-04-13 18:09:30 +05:00
committed by GitHub
parent cd332c4dfd
commit 5d0fbf705d
16 changed files with 190 additions and 124 deletions
+2 -4
View File
@@ -66,11 +66,9 @@ export default function SideBarItem({ path }: Props): ReactElement {
disableRipple
>
<ListItemIcon style={{ marginLeft: '30px' }}>
<StatusIcon isOk={status.all} isLoading={isLoading} />
<StatusIcon checkState={status.all} isLoading={isLoading} />
</ListItemIcon>
<ListItemText
primary={<Typography className={classes.smallerText}>{`Node ${status.all ? 'OK' : 'Error'}`}</Typography>}
/>
<ListItemText primary={<Typography className={classes.smallerText}>{`Node ${status.all}`}</Typography>} />
<ListItemIcon className={classes.icon}>
{status.all ? null : <ArrowRight className={classes.iconSmall} />}
</ListItemIcon>
+20 -3
View File
@@ -1,23 +1,40 @@
import type { ReactElement } from 'react'
import { CircularProgress } from '@material-ui/core'
import { CheckState } from '../providers/Bee'
interface Props {
isOk: boolean
checkState: CheckState
isLoading?: boolean
size?: number | string
className?: string
}
export default function StatusIcon({ isOk, size, className, isLoading }: Props): ReactElement {
export default function StatusIcon({ checkState, size, className, isLoading }: Props): ReactElement {
const s = size || '1rem'
if (isLoading) return <CircularProgress size={s} className={className} />
let backgroundColor: string
switch (checkState) {
case CheckState.OK:
backgroundColor = '#1de600'
break
case CheckState.WARNING:
backgroundColor = 'orange'
break
case CheckState.ERROR:
backgroundColor = '#ff3a52'
break
default:
// Default is error
backgroundColor = '#ff3a52'
}
return (
<span
className={className}
style={{
backgroundColor: isOk ? '#1de600' : '#ff3a52',
backgroundColor,
height: s,
width: s,
borderRadius: '50%',
+2 -2
View File
@@ -2,7 +2,7 @@ import { ReactElement, useContext } from 'react'
import PeerBalances from './PeerBalances'
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
import { Context as BeeContext } from '../../providers/Bee'
import { CheckState, Context as BeeContext } from '../../providers/Bee'
import { Context as SettingsContext } from '../../providers/Settings'
import { useAccounting } from '../../hooks/accounting'
import ExpandableList from '../../components/ExpandableList'
@@ -19,7 +19,7 @@ export default function Accounting(): ReactElement {
const { accounting, totalUncashed, isLoadingUncashed } = useAccounting(beeDebugApi, settlements, peerBalances)
if (!status.all) return <TroubleshootConnectionCard />
if (status.all === CheckState.ERROR) return <TroubleshootConnectionCard />
return (
<div>
+2 -2
View File
@@ -8,7 +8,7 @@ import ExpandableListItemActions from '../../components/ExpandableListItemAction
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
import { SwarmButton } from '../../components/SwarmButton'
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
import { Context as BeeContext } from '../../providers/Bee'
import { CheckState, Context as BeeContext } from '../../providers/Bee'
import { Context as IdentityContext, Identity } from '../../providers/Feeds'
import { ROUTES } from '../../routes'
import { formatEnum } from '../../utils'
@@ -60,7 +60,7 @@ export default function Feeds(): ReactElement {
setShowDelete(true)
}
if (!status.all) return <TroubleshootConnectionCard />
if (status.all === CheckState.ERROR) return <TroubleshootConnectionCard />
return (
<div>
+2 -2
View File
@@ -6,7 +6,7 @@ import { DocumentationText } from '../../components/DocumentationText'
import { HistoryHeader } from '../../components/HistoryHeader'
import { ProgressIndicator } from '../../components/ProgressIndicator'
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
import { Context as BeeContext } from '../../providers/Bee'
import { CheckState, Context as BeeContext } from '../../providers/Bee'
import { Context as IdentityContext, Identity } from '../../providers/Feeds'
import { Context as FileContext } from '../../providers/File'
import { Context as SettingsContext } from '../../providers/Settings'
@@ -43,7 +43,7 @@ export function Upload(): ReactElement {
refresh()
}, []) // eslint-disable-line react-hooks/exhaustive-deps
if (!status.all) return <TroubleshootConnectionCard />
if (status.all === CheckState.ERROR) return <TroubleshootConnectionCard />
if (!files.length) {
setFiles([])
+2 -2
View File
@@ -2,7 +2,7 @@ import { ReactElement, useContext } from 'react'
import { Button } from '@material-ui/core'
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
import { Context as BeeContext } from '../../providers/Bee'
import { CheckState, Context as BeeContext } from '../../providers/Bee'
import ExpandableList from '../../components/ExpandableList'
import ExpandableListItem from '../../components/ExpandableListItem'
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
@@ -20,7 +20,7 @@ export default function Status(): ReactElement {
nodeInfo,
} = useContext(BeeContext)
if (!status.all) return <TroubleshootConnectionCard />
if (status.all === CheckState.ERROR) return <TroubleshootConnectionCard />
return (
<div>
+2 -2
View File
@@ -5,7 +5,7 @@ import { PlusSquare } from 'react-feather'
import { useNavigate } from 'react-router'
import { SwarmButton } from '../../components/SwarmButton'
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
import { Context as BeeContext } from '../../providers/Bee'
import { CheckState, Context as BeeContext } from '../../providers/Bee'
import { Context as StampsContext } from '../../providers/Stamps'
import { ROUTES } from '../../routes'
import StampsTable from './StampsTable'
@@ -41,7 +41,7 @@ export default function Stamp(): ReactElement {
return () => stop()
}, [status]) // eslint-disable-line react-hooks/exhaustive-deps
if (!status.all) return <TroubleshootConnectionCard />
if (status.all === CheckState.ERROR) return <TroubleshootConnectionCard />
function navigateToNewStamp() {
navigate(ROUTES.STAMPS_NEW)
@@ -1,41 +1,59 @@
import { useContext } from 'react'
import DepositModal from '../../../containers/DepositModal'
import type { ReactElement } from 'react'
import type { ReactElement, ReactNode } from 'react'
import ExpandableList from '../../../components/ExpandableList'
import ExpandableListItemKey from '../../../components/ExpandableListItemKey'
import ExpandableListItemActions from '../../../components/ExpandableListItemActions'
import ExpandableListItemNote from '../../../components/ExpandableListItemNote'
import StatusIcon from '../../../components/StatusIcon'
import { Context } from '../../../providers/Bee'
import { CheckState, Context } from '../../../providers/Bee'
const ChequebookDeployFund = (): ReactElement | null => {
const { status, isLoading, chequebookAddress } = useContext(Context)
const { isOk, isEnabled } = status.chequebook
const { checkState, isEnabled } = status.chequebook
if (!isEnabled) return null
let text: ReactNode
switch (checkState) {
case CheckState.OK:
text = 'Your chequebook is deployed and funded'
break
case CheckState.WARNING:
text = (
<>
Your chequebook is not funded. Please deposit some xBZZ to your chequebook address. You may need to aquire BZZ
(e.g. <a href="https://bzz.exchange/">bzz.exchange</a>) and bridge it to the xDai network through the{' '}
<a href="https://omni.xdaichain.com/bridge">omni bridge</a>. To pay the transaction fees, you will also need
xDAI token. You can purchase DAI on the network and bridge it to xDai network through the{' '}
<a href="https://bridge.xdaichain.com/">xDai Bridge</a>. See the{' '}
<a href="https://www.xdaichain.com/#xdai-stable-chain">official xDai website</a> for more information.
</>
)
break
default:
text = (
<>
Your chequebook is either not deployed nor funded. To run the node you will need xDAI and xBZZ on the xDai
network. You may need to aquire BZZ (e.g. <a href="https://bzz.exchange/">bzz.exchange</a>) and bridge it to
the xDai network through the <a href="https://omni.xdaichain.com/bridge">omni bridge</a>. To pay the
transaction fees, you will also need xDAI token. You can purchase DAI on the network and bridge it to xDai
network through the <a href="https://bridge.xdaichain.com/">xDai Bridge</a>. See the{' '}
<a href="https://www.xdaichain.com/#xdai-stable-chain">official xDai website</a> for more information.
</>
)
}
return (
<ExpandableList
label={
<>
<StatusIcon isOk={isOk} isLoading={isLoading} /> Chequebook Deployment & Funding
<StatusIcon checkState={checkState} isLoading={isLoading} /> Chequebook Deployment & Funding
</>
}
>
<ExpandableListItemNote>
{isOk ? (
'Your chequebook is deployed and funded'
) : (
<>
Your chequebook is either not deployed or funded. To run the node you will need xDAI and xBZZ on the xDai
network. You may need to aquire BZZ (e.g. <a href="https://bzz.exchange/">bzz.exchange</a>) and bridge it to
the xDai network through the <a href="https://omni.xdaichain.com/bridge">omni bridge</a>. To pay the
transaction fees, you will also need xDAI token. You can purchase DAI on the network and bridge it to xDai
network through the <a href="https://bridge.xdaichain.com/">xDai Bridge</a>. See the{' '}
<a href="https://www.xdaichain.com/#xdai-stable-chain">official xDai website</a> for more information.
</>
)}
</ExpandableListItemNote>
<ExpandableListItemNote>{text}</ExpandableListItemNote>
{chequebookAddress && (
<>
<ExpandableListItemKey label="Chequebook Address" value={chequebookAddress.chequebookAddress} />
@@ -6,13 +6,13 @@ import ExpandableListItem from '../../../components/ExpandableListItem'
import ExpandableListItemInput from '../../../components/ExpandableListItemInput'
import ExpandableListItemNote from '../../../components/ExpandableListItemNote'
import StatusIcon from '../../../components/StatusIcon'
import { Context } from '../../../providers/Bee'
import { CheckState, Context } from '../../../providers/Bee'
import { Context as SettingsContext } from '../../../providers/Settings'
export default function NodeConnectionCheck(): ReactElement | null {
const { status, isLoading } = useContext(Context)
const { setDebugApiUrl, apiDebugUrl } = useContext(SettingsContext)
const { isOk, isEnabled } = status.debugApiConnection
const { checkState, isEnabled } = status.debugApiConnection
if (!isEnabled) return null
@@ -20,18 +20,18 @@ export default function NodeConnectionCheck(): ReactElement | null {
<ExpandableList
label={
<>
<StatusIcon isOk={isOk} isLoading={isLoading} /> Connection to Bee Debug API
<StatusIcon checkState={checkState} isLoading={isLoading} /> Connection to Bee Debug API
</>
}
>
<ExpandableListItemNote>
{isOk
{checkState === CheckState.OK
? 'The connection to the Bee nodes debug API has been successful'
: 'We cannot connect to your nodes debug API. Please check the following to troubleshoot your issue.'}
</ExpandableListItemNote>
<ExpandableListItemInput label="Bee Debug API" value={apiDebugUrl} onConfirm={setDebugApiUrl} />
{!isOk && (
{checkState === CheckState.ERROR && (
<ExpandableList level={1} label="Troubleshoot">
<ExpandableListItem
label={
@@ -3,11 +3,11 @@ import ExpandableList from '../../../components/ExpandableList'
import ExpandableListItemKey from '../../../components/ExpandableListItemKey'
import ExpandableListItemNote from '../../../components/ExpandableListItemNote'
import StatusIcon from '../../../components/StatusIcon'
import { Context } from '../../../providers/Bee'
import { CheckState, Context } from '../../../providers/Bee'
export default function EthereumConnectionCheck(): ReactElement | null {
const { status, isLoading, nodeAddresses } = useContext(Context)
const { isOk, isEnabled } = status.blockchainConnection
const { checkState, isEnabled } = status.blockchainConnection
if (!isEnabled) return null
@@ -15,12 +15,12 @@ export default function EthereumConnectionCheck(): ReactElement | null {
<ExpandableList
label={
<>
<StatusIcon isOk={isOk} isLoading={isLoading} /> Connection to Blockchain
<StatusIcon checkState={checkState} isLoading={isLoading} /> Connection to Blockchain
</>
}
>
<ExpandableListItemNote>
{isOk ? (
{checkState === CheckState.OK ? (
'Your node is connected to the xDai blockchain'
) : (
<>
@@ -7,12 +7,12 @@ import ExpandableListItem from '../../../components/ExpandableListItem'
import ExpandableListItemNote from '../../../components/ExpandableListItemNote'
import ExpandableListItemInput from '../../../components/ExpandableListItemInput'
import StatusIcon from '../../../components/StatusIcon'
import { Context } from '../../../providers/Bee'
import { CheckState, Context } from '../../../providers/Bee'
export default function NodeConnectionCheck(): ReactElement | null {
const { setApiUrl, apiUrl } = useContext(SettingsContext)
const { status, isLoading } = useContext(Context)
const { isEnabled, isOk } = status.apiConnection
const { isEnabled, checkState } = status.apiConnection
if (!isEnabled) return null
@@ -20,17 +20,17 @@ export default function NodeConnectionCheck(): ReactElement | null {
<ExpandableList
label={
<>
<StatusIcon isOk={isOk} isLoading={isLoading} /> Connection to Bee API
<StatusIcon checkState={checkState} isLoading={isLoading} /> Connection to Bee API
</>
}
>
<ExpandableListItemNote>
{isOk
{checkState === CheckState.OK
? 'The connection to the Bee nodes API has been successful'
: 'Could not connect to your Bee nodes API. Please check the troubleshoot below on how you may resolve it.'}
</ExpandableListItemNote>
<ExpandableListItemInput label="Bee API" value={apiUrl} onConfirm={setApiUrl} />
{!isOk && (
{checkState === CheckState.ERROR && (
<ExpandableList level={1} label="Troubleshoot">
<ExpandableListItem
label={
+17 -9
View File
@@ -1,29 +1,37 @@
import { ReactElement, useContext } from 'react'
import { ReactElement, ReactNode, useContext } from 'react'
import ExpandableList from '../../../components/ExpandableList'
import ExpandableListItemNote from '../../../components/ExpandableListItemNote'
import TopologyStats from '../../../components/TopologyStats'
import StatusIcon from '../../../components/StatusIcon'
import { Context } from '../../../providers/Bee'
import { CheckState, Context } from '../../../providers/Bee'
export default function PeerConnection(): ReactElement | null {
const { status, isLoading, topology } = useContext(Context)
const { isEnabled, isOk } = status.topology
const { isEnabled, checkState } = status.topology
if (!isEnabled) return null
let text: ReactNode
switch (checkState) {
case CheckState.OK:
text = 'You are connected to other Bee nodes'
break
// Both error state and warning state
default:
text =
'Your node is not connected to any peers. Please wait a bit if you just started the node, otherwise review your configuration file.'
}
return (
<ExpandableList
label={
<>
<StatusIcon isOk={isOk} isLoading={isLoading} /> Connection to Peers
<StatusIcon checkState={checkState} isLoading={isLoading} /> Connection to Peers
</>
}
>
<ExpandableListItemNote>
{isOk
? 'You are connected to other Bee nodes'
: 'Your node is not connected to any peers. Please wait a bit if you just started the node, otherwise review your configuration file.'}
</ExpandableListItemNote>
<ExpandableListItemNote>{text}</ExpandableListItemNote>
<TopologyStats topology={topology} />
</ExpandableList>
+4 -4
View File
@@ -4,11 +4,11 @@ import ExpandableList from '../../../components/ExpandableList'
import ExpandableListItem from '../../../components/ExpandableListItem'
import ExpandableListItemNote from '../../../components/ExpandableListItemNote'
import StatusIcon from '../../../components/StatusIcon'
import { Context } from '../../../providers/Bee'
import { CheckState, Context } from '../../../providers/Bee'
export default function VersionCheck(): ReactElement | null {
const { status, isLoading, latestUserVersion, latestPublishedVersion, latestBeeVersionUrl } = useContext(Context)
const { isEnabled, isOk } = status.version
const { isEnabled, checkState } = status.version
if (!isEnabled) return null
@@ -16,12 +16,12 @@ export default function VersionCheck(): ReactElement | null {
<ExpandableList
label={
<>
<StatusIcon isOk={isOk} isLoading={isLoading} /> Bee Version
<StatusIcon checkState={checkState} isLoading={isLoading} /> Bee Version
</>
}
>
<ExpandableListItemNote>
{isOk ? (
{checkState === CheckState.OK ? (
'You are running the latest version of Bee.'
) : (
<>
+68 -45
View File
@@ -17,13 +17,19 @@ import { Token } from '../models/Token'
import type { Balance, ChequebookBalance, Settlements } from '../types'
import { Context as SettingsContext } from './Settings'
export enum CheckState {
OK = 'OK',
WARNING = 'Warning',
ERROR = 'Error',
}
interface StatusItem {
isEnabled: boolean
isOk: boolean
checkState: CheckState
}
interface Status {
all: boolean
all: CheckState
version: StatusItem
blockchainConnection: StatusItem
debugApiConnection: StatusItem
@@ -63,13 +69,13 @@ interface ContextInterface {
const initialValues: ContextInterface = {
status: {
all: false,
version: { isEnabled: false, isOk: false },
blockchainConnection: { isEnabled: false, isOk: false },
debugApiConnection: { isEnabled: false, isOk: false },
apiConnection: { isEnabled: false, isOk: false },
topology: { isEnabled: false, isOk: false },
chequebook: { isEnabled: false, isOk: false },
all: CheckState.ERROR,
version: { isEnabled: false, checkState: CheckState.ERROR },
blockchainConnection: { isEnabled: false, checkState: CheckState.ERROR },
debugApiConnection: { isEnabled: false, checkState: CheckState.ERROR },
apiConnection: { isEnabled: false, checkState: CheckState.ERROR },
topology: { isEnabled: false, checkState: CheckState.ERROR },
chequebook: { isEnabled: false, checkState: CheckState.ERROR },
},
latestPublishedVersion: undefined,
latestUserVersion: undefined,
@@ -115,45 +121,62 @@ function getStatus(
chequebookBalance: ChequebookBalance | null,
error: Error | null,
): Status {
const status = {
version: {
isEnabled: true,
isOk: Boolean(
debugApiHealth &&
semver.satisfies(debugApiHealth.version, engines.bee, {
includePrerelease: true,
}),
),
},
blockchainConnection: {
isEnabled: true,
isOk: Boolean(nodeAddresses?.ethereum),
},
debugApiConnection: {
isEnabled: true,
isOk: Boolean(debugApiHealth?.status === 'ok'),
},
apiConnection: {
isEnabled: true,
isOk: apiHealth,
},
topology: {
isEnabled: Boolean(nodeInfo && [BeeModes.FULL, BeeModes.LIGHT, BeeModes.ULTRA_LIGHT].includes(nodeInfo.beeMode)),
isOk: Boolean(topology?.connected && topology?.connected > 0),
},
chequebook: {
isEnabled: Boolean(nodeInfo && [BeeModes.FULL, BeeModes.LIGHT].includes(nodeInfo.beeMode)),
isOk:
Boolean(chequebookAddress?.chequebookAddress) &&
chequebookBalance !== null &&
chequebookBalance?.totalBalance.toBigNumber.isGreaterThan(0),
},
const status: Status = { ...initialValues.status }
// Version check
status.version.isEnabled = true
status.version.checkState =
debugApiHealth &&
semver.satisfies(debugApiHealth.version, engines.bee, {
includePrerelease: true,
})
? CheckState.OK
: CheckState.ERROR
// Blockchain connection check
status.blockchainConnection.isEnabled = true
status.blockchainConnection.checkState = Boolean(debugApiHealth?.status === 'ok') ? CheckState.OK : CheckState.ERROR
// Debug API connection check
status.debugApiConnection.isEnabled = true
status.debugApiConnection.checkState = Boolean(debugApiHealth?.status === 'ok') ? CheckState.OK : CheckState.ERROR
// API connection check
status.apiConnection.isEnabled = true
status.apiConnection.checkState = apiHealth ? CheckState.OK : CheckState.ERROR
// Topology check
if (nodeInfo && [BeeModes.FULL, BeeModes.LIGHT, BeeModes.ULTRA_LIGHT].includes(nodeInfo.beeMode)) {
status.topology.isEnabled = true
status.topology.checkState = topology?.connected && topology?.connected > 0 ? CheckState.OK : CheckState.WARNING
}
return {
...status,
all: !error && Object.values(status).every(({ isEnabled, isOk }) => !isEnabled || (isEnabled && isOk)),
// Chequebook check
if (error || (nodeInfo && [BeeModes.FULL, BeeModes.LIGHT].includes(nodeInfo.beeMode))) {
status.chequebook.isEnabled = true
if (
chequebookAddress?.chequebookAddress &&
chequebookBalance !== null &&
chequebookBalance?.totalBalance.toBigNumber.isGreaterThan(0)
) {
status.chequebook.checkState = CheckState.OK
} else if (chequebookAddress?.chequebookAddress) status.chequebook.checkState = CheckState.WARNING
else status.chequebook.checkState = CheckState.OK
}
// Determine overall status
if (Object.values(status).some(({ isEnabled, checkState }) => isEnabled && checkState === CheckState.ERROR)) {
status.all = CheckState.ERROR
} else if (
Object.values(status).some(({ isEnabled, checkState }) => isEnabled && checkState === CheckState.WARNING)
) {
status.all = CheckState.WARNING
} else {
status.all = CheckState.OK
}
return status
}
export function Provider({ children }: Props): ReactElement {
-17
View File
@@ -5,23 +5,6 @@ interface LatestBeeRelease {
html_url: string
}
interface StatusHookCommon {
isOk: boolean
}
interface StatusNodeVersionHook extends StatusHookCommon {
userVersion?: string
latestVersion?: string
latestUrl: string
isLatestBeeVersion: boolean
}
interface StatusEthereumConnectionHook extends StatusHookCommon {
nodeAddresses: NodeAddresses | null
}
interface StatusTopologyHook extends StatusHookCommon {
topology: Topology | null
}
interface SwarmMetadata {
size: number
name: string
+19
View File
@@ -1,4 +1,23 @@
import type { NodeAddresses, Topology } from '@ethersphere/bee-js'
import type { Token } from './models/Token'
import { CheckState } from './providers/Bee'
export interface StatusHookCommon {
checkState: CheckState
}
export interface StatusNodeVersionHook extends StatusHookCommon {
userVersion?: string
latestVersion?: string
latestUrl: string
isLatestBeeVersion: boolean
}
export interface StatusEthereumConnectionHook extends StatusHookCommon {
nodeAddresses: NodeAddresses | null
}
export interface StatusTopologyHook extends StatusHookCommon {
topology: Topology | null
}
export interface ChequebookBalance {
totalBalance: Token