mirror of
https://github.com/stake-house/wagyu-installer.git
synced 2022-05-06 20:09:49 +03:00
[WAGYU-43] Status enum (#52)
* add vscode workspace dir to gitignore * update prettierrc * add types file for statuses * remove unused import * create utils file for Status * update imports * add new NodeStatus * prettier formatting updates * use new Status enum * use Status enum in RocketPool * move pages to components, run formatter, clean up imports, remove default export * format footer * format SystemCheck, clean up imports, consolidate a few render functions * update imports in App
This commit is contained in:
committed by
GitHub
parent
24683cd39f
commit
e221ab7432
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,6 @@
|
||||
node_modules/
|
||||
dist/
|
||||
.idea
|
||||
|
||||
# Ignore Visual Studio Code IDE workspace settings
|
||||
.vscode/
|
||||
13
.prettierrc
13
.prettierrc
@@ -1,6 +1,9 @@
|
||||
{
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 4,
|
||||
"semi": false,
|
||||
"singleQuote": true
|
||||
}
|
||||
"bracketSpacing": true,
|
||||
"jsxBracketSameLine": false,
|
||||
"trailingComma": "all",
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"useTabs": false
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { HashRouter, Route, Switch } from "react-router-dom";
|
||||
|
||||
import { Background } from "./colors";
|
||||
import Deposit from "./pages/Deposit";
|
||||
import Home from "./pages/Home";
|
||||
import InstallFailed from "./pages/InstallFailed";
|
||||
import Installing from "./pages/Installing";
|
||||
import React from "react";
|
||||
import Status from "./pages/Status";
|
||||
import SystemCheck from "./pages/SystemCheck";
|
||||
import styled from "styled-components";
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { HashRouter, Route, Switch } from 'react-router-dom';
|
||||
import { Background } from './colors';
|
||||
import { Deposit } from './components/Deposit';
|
||||
import { Home } from './components/Home';
|
||||
import { InstallFailed } from './components/InstallFailed';
|
||||
import Installing from './components/Installing';
|
||||
import Status from './components/Status';
|
||||
import { SystemCheck } from './components/SystemCheck';
|
||||
|
||||
const Container = styled.main`
|
||||
font-family: 'PT Mono', monospace;
|
||||
|
||||
@@ -3,13 +3,13 @@ import {
|
||||
executeCommandAsync,
|
||||
executeCommandInNewTerminal,
|
||||
executeCommandStream,
|
||||
executeCommandSync,
|
||||
executeCommandSyncReturnStdout,
|
||||
executeCommandWithPromptsAsync,
|
||||
} from './ExecuteCommand'
|
||||
|
||||
import fs from "fs";
|
||||
import yaml from "js-yaml";
|
||||
import { Status } from "../types";
|
||||
|
||||
const ASKPASS_PATH = "src/scripts/askpass.sh";
|
||||
|
||||
@@ -26,7 +26,7 @@ const GETH_PEERS_DOCKER_CMD = "docker exec rocketpool_eth1 geth --exec 'admin.pe
|
||||
// TODO: better error handling/logging
|
||||
|
||||
type Callback = (success: boolean) => void;
|
||||
type NodeStatusCallback = (status: number) => void;
|
||||
type NodeStatusCallback = (status: Status) => void;
|
||||
type StdoutCallback = (text: string[]) => void;
|
||||
|
||||
const wrapCommandInDockerGroup = (command: string) => {
|
||||
@@ -184,9 +184,9 @@ const dockerContainerStatus = async (containerName: string, nodeStatusCallback:
|
||||
const containerId = executeCommandSyncReturnStdout(wrapCommandInDockerGroup("docker ps -q -f name=" + containerName));
|
||||
|
||||
if (containerId.trim()) {
|
||||
nodeStatusCallback(0); // online
|
||||
nodeStatusCallback(Status.Online);
|
||||
} else {
|
||||
nodeStatusCallback(2); // offline
|
||||
nodeStatusCallback(Status.Offline);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
import {
|
||||
Black,
|
||||
DisabledButton,
|
||||
Heading,
|
||||
MainContent
|
||||
} from "../colors";
|
||||
|
||||
import Footer from "../components/Footer";
|
||||
import React from "react";
|
||||
import { shell } from "electron";
|
||||
import styled from "styled-components";
|
||||
import React from 'react';
|
||||
import { shell } from 'electron';
|
||||
import styled from 'styled-components';
|
||||
import { Black, DisabledButton, Heading, MainContent } from '../colors';
|
||||
import Footer from './Footer';
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height:100vh;
|
||||
min-height: 100vh;
|
||||
`;
|
||||
|
||||
const LandingHeader = styled.div`
|
||||
@@ -24,7 +18,7 @@ const LandingHeader = styled.div`
|
||||
margin-top: 50;
|
||||
color: ${Heading};
|
||||
max-width: 550;
|
||||
flex-grow:1;
|
||||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
const Content = styled.div`
|
||||
@@ -62,17 +56,19 @@ const ImportKeysButton = styled.div`
|
||||
`;
|
||||
|
||||
const sendToLaunchpad = () => {
|
||||
shell.openExternal("https://pyrmont.launchpad.ethereum.org/");
|
||||
}
|
||||
shell.openExternal('https://pyrmont.launchpad.ethereum.org/');
|
||||
};
|
||||
|
||||
const Deposit = () => {
|
||||
// TODO: add browse
|
||||
export const Deposit = () => {
|
||||
// TODO: add browse
|
||||
// TODO: run validator then go to status page on Run click
|
||||
return (
|
||||
<Container>
|
||||
<LandingHeader>Deposit</LandingHeader>
|
||||
<Content>
|
||||
1) Head to the <StyledLink onClick={sendToLaunchpad}>launchpad</StyledLink> to deposit 32 Goerli ETH.
|
||||
1) Head to the
|
||||
<StyledLink onClick={sendToLaunchpad}>launchpad</StyledLink> to deposit
|
||||
32 Goerli ETH.
|
||||
<br />
|
||||
<br />
|
||||
<em>Note: Your nodes are set up already, so ignore those steps.</em>
|
||||
@@ -86,9 +82,9 @@ const Deposit = () => {
|
||||
<br />
|
||||
<ButtonContainer>
|
||||
<ImportKeysButton>
|
||||
Import Validator Keys `keystore-*.json` file
|
||||
<br />
|
||||
(still in development)
|
||||
Import Validator Keys `keystore-*.json` file
|
||||
<br />
|
||||
(still in development)
|
||||
</ImportKeysButton>
|
||||
</ButtonContainer>
|
||||
<br />
|
||||
@@ -96,9 +92,12 @@ const Deposit = () => {
|
||||
<br />
|
||||
3) Click Run.
|
||||
</Content>
|
||||
<Footer backLink={"/status"} backLabel={"Back"} nextLink={"/status"} nextLabel={"Run"} />
|
||||
<Footer
|
||||
backLink={'/status'}
|
||||
backLabel={'Back'}
|
||||
nextLink={'/status'}
|
||||
nextLabel={'Run'}
|
||||
/>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
export default Deposit;
|
||||
);
|
||||
};
|
||||
@@ -1,15 +1,14 @@
|
||||
import { Black, Button, ButtonHover } from "../colors";
|
||||
|
||||
import { Link } from "react-router-dom";
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Black, Button, ButtonHover } from '../colors';
|
||||
|
||||
type FooterProps = {
|
||||
backLink: string;
|
||||
backLabel: string;
|
||||
nextLink: string;
|
||||
nextLabel: string;
|
||||
}
|
||||
};
|
||||
|
||||
const FooterContainer = styled.div`
|
||||
display: flex;
|
||||
@@ -18,8 +17,8 @@ const FooterContainer = styled.div`
|
||||
align-items: center;
|
||||
align-self: flex-end;
|
||||
height: 70;
|
||||
flex-grow:1;
|
||||
min-width:100vw;
|
||||
flex-grow: 1;
|
||||
min-width: 100vw;
|
||||
`;
|
||||
|
||||
const StyledButton = styled(Link)`
|
||||
@@ -28,7 +27,7 @@ const StyledButton = styled(Link)`
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-self:flex-end;
|
||||
align-self: flex-end;
|
||||
height: 24;
|
||||
background-color: ${Button};
|
||||
padding: 16 24;
|
||||
@@ -44,15 +43,21 @@ const StyledButton = styled(Link)`
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
const Footer = (props: FooterProps) => {
|
||||
return(
|
||||
return (
|
||||
<FooterContainer>
|
||||
{ props.backLink ? <StyledButton to={props.backLink}>{props.backLabel}</StyledButton> : <div></div> }
|
||||
{ props.nextLink ? <StyledButton to={props.nextLink}>{props.nextLabel}</StyledButton> : <div></div> }
|
||||
{props.backLink ? (
|
||||
<StyledButton to={props.backLink}>{props.backLabel}</StyledButton>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
{props.nextLink ? (
|
||||
<StyledButton to={props.nextLink}>{props.nextLabel}</StyledButton>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
</FooterContainer>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
export default Footer;
|
||||
export default Footer;
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { shell } from 'electron';
|
||||
import { Link, withRouter } from 'react-router-dom';
|
||||
import { History } from 'history';
|
||||
import { isRocketPoolInstalled } from '../commands/RocketPool';
|
||||
import {
|
||||
Black,
|
||||
Button,
|
||||
ButtonHover,
|
||||
Heading,
|
||||
MainContent,
|
||||
Red
|
||||
} from "../colors";
|
||||
import { Link, withRouter } from "react-router-dom";
|
||||
|
||||
import { History } from "history";
|
||||
import React from "react";
|
||||
import { isRocketPoolInstalled } from "../commands/RocketPool";
|
||||
import { shell } from "electron";
|
||||
import styled from "styled-components";
|
||||
Red,
|
||||
} from '../colors';
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
@@ -63,17 +62,17 @@ const StyledLink = styled.em`
|
||||
`;
|
||||
|
||||
const Testnet = styled.b`
|
||||
color: ${Red}
|
||||
color: ${Red};
|
||||
`;
|
||||
|
||||
const Home = ({ history }: {history: History}) => {
|
||||
export const Home = withRouter(({ history }: { history: History }) => {
|
||||
if (isRocketPoolInstalled()) {
|
||||
history.push('/status');
|
||||
}
|
||||
|
||||
const sendToRocketpool = () => {
|
||||
shell.openExternal("https://www.rocketpool.net/");
|
||||
}
|
||||
shell.openExternal('https://www.rocketpool.net/');
|
||||
};
|
||||
|
||||
return (
|
||||
<Container>
|
||||
@@ -81,24 +80,25 @@ const Home = ({ history }: {history: History}) => {
|
||||
<Content>
|
||||
This is your portal into the Eth2 world - welcome.
|
||||
<br />
|
||||
<br/>
|
||||
<br />
|
||||
A one-click staking installer for the <Testnet>pyrmont testnet</Testnet>.
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
<br />A one-click staking installer for the
|
||||
<Testnet>pyrmont testnet</Testnet>.
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
It will configure the following for you*:
|
||||
<ul>
|
||||
<li>Eth 1 Node</li>
|
||||
<li>Eth 2 Beacon Node</li>
|
||||
<li>Eth 2 Validator</li>
|
||||
</ul>
|
||||
<br/>
|
||||
<br/>
|
||||
*Note: we use the Rocket Pool install infrastructure which runs everything in docker, more info <StyledLink onClick={sendToRocketpool}>here</StyledLink>
|
||||
<br />
|
||||
<br />
|
||||
*Note: we use the Rocket Pool install infrastructure which runs
|
||||
everything in docker, more info
|
||||
<StyledLink onClick={sendToRocketpool}>here</StyledLink>
|
||||
</Content>
|
||||
<StartButton to="/systemcheck">Enter</StartButton>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
export default withRouter(Home);
|
||||
});
|
||||
@@ -1,15 +1,14 @@
|
||||
import { Heading, MainContent } from '../colors';
|
||||
|
||||
import Footer from '../components/Footer';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Heading, MainContent } from '../colors';
|
||||
import Footer from './Footer';
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height:100vh;
|
||||
min-height: 100vh;
|
||||
`;
|
||||
|
||||
const LandingHeader = styled.div`
|
||||
@@ -18,10 +17,9 @@ const LandingHeader = styled.div`
|
||||
margin-top: 50;
|
||||
color: ${Heading};
|
||||
max-width: 550;
|
||||
flex-grow:1;
|
||||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
|
||||
const Content = styled.div`
|
||||
color: ${MainContent};
|
||||
margin-top: 20;
|
||||
@@ -29,19 +27,19 @@ const Content = styled.div`
|
||||
flex-grow: 6;
|
||||
`;
|
||||
|
||||
const InstallFailed = () => {
|
||||
export const InstallFailed = () => {
|
||||
return (
|
||||
<Container>
|
||||
<LandingHeader>Install Failed</LandingHeader>
|
||||
<Content>
|
||||
Unfortunately your install failed. At this time we cannot provide any additonal info.
|
||||
Unfortunately your install failed. At this time we cannot provide any
|
||||
additonal info.
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
Please reach out to the ethstaker community for help.
|
||||
</Content>
|
||||
<Footer backLink={"/"} backLabel={"Home"} nextLink={""} nextLabel={""} />
|
||||
<Footer backLink={'/'} backLabel={'Home'} nextLink={''} nextLabel={''} />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
export default InstallFailed;
|
||||
};
|
||||
@@ -1,25 +1,16 @@
|
||||
import {
|
||||
Black,
|
||||
Button,
|
||||
ButtonHover,
|
||||
Heading,
|
||||
MainContent
|
||||
} from "../colors";
|
||||
import { Link, withRouter } from "react-router-dom";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import styled, { keyframes } from "styled-components";
|
||||
|
||||
import { History } from "history";
|
||||
import { installAndStartRocketPool } from "../commands/RocketPool";
|
||||
|
||||
const ENTER_KEYCODE = 13;
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import styled, { keyframes } from 'styled-components';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { History } from 'history';
|
||||
import { Heading, MainContent } from '../colors';
|
||||
import { installAndStartRocketPool } from '../commands/RocketPool';
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height:100vh;
|
||||
min-height: 100vh;
|
||||
`;
|
||||
|
||||
const LandingHeader = styled.div`
|
||||
@@ -28,7 +19,7 @@ const LandingHeader = styled.div`
|
||||
margin-top: 50;
|
||||
color: ${Heading};
|
||||
max-width: 550;
|
||||
flex-grow:1;
|
||||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
const Content = styled.div`
|
||||
@@ -81,13 +72,12 @@ const LogsListItem = styled.li`
|
||||
text-overflow: ellipsis;
|
||||
`;
|
||||
|
||||
const LogsContainerAnchor = styled.div`
|
||||
`;
|
||||
const LogsContainerAnchor = styled.div``;
|
||||
|
||||
const Installing = ({ history }: {history: History}) => {
|
||||
const anchorRef = useRef(document.createElement("div"));
|
||||
const Installing = ({ history }: { history: History }) => {
|
||||
const anchorRef = useRef(document.createElement('div'));
|
||||
|
||||
const [stdoutText, setStdoutText] = useState([""]);
|
||||
const [stdoutText, setStdoutText] = useState(['']);
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
@@ -100,55 +90,55 @@ const Installing = ({ history }: {history: History}) => {
|
||||
}, [stdoutText]);
|
||||
|
||||
const stdoutCallback = (text: string[]) => {
|
||||
console.log("installing cb with " + text.join());
|
||||
console.log('installing cb with ' + text.join());
|
||||
setStdoutText(stdoutText.concat(text));
|
||||
}
|
||||
};
|
||||
|
||||
const installCallback = (success: boolean) => {
|
||||
if (success) {
|
||||
console.log("install succeeded")
|
||||
|
||||
console.log('install succeeded');
|
||||
|
||||
// wait 5 seconds before redirecting to make sure everythings up
|
||||
setTimeout(() => {
|
||||
history.push('/status');
|
||||
}, 5000);
|
||||
} else {
|
||||
console.log("install failed");
|
||||
history.push("/installfailed");
|
||||
console.log('install failed');
|
||||
history.push('/installfailed');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<LandingHeader>Install</LandingHeader>
|
||||
<Content>
|
||||
Installing...
|
||||
<br />
|
||||
<br />
|
||||
May take 2-4 minutes depending on internet speed.
|
||||
<SpinnerContainer>
|
||||
<LoadingSpinner />
|
||||
</SpinnerContainer>
|
||||
<br/>
|
||||
{ // Only show logs container if there are some
|
||||
stdoutText.length > 1
|
||||
&&
|
||||
<Content>
|
||||
Installing...
|
||||
<br />
|
||||
<br />
|
||||
May take 2-4 minutes depending on internet speed.
|
||||
<SpinnerContainer>
|
||||
<LoadingSpinner />
|
||||
</SpinnerContainer>
|
||||
<br />
|
||||
{
|
||||
// Only show logs container if there are some
|
||||
stdoutText.length > 1 && (
|
||||
<div>
|
||||
Install logs:
|
||||
<LogsContainer>
|
||||
<LogsList>
|
||||
{stdoutText.map((line, i) => {
|
||||
return (<LogsListItem key={i}>{line}</LogsListItem>)
|
||||
return <LogsListItem key={i}>{line}</LogsListItem>;
|
||||
})}
|
||||
</LogsList>
|
||||
<LogsContainerAnchor ref={anchorRef}></LogsContainerAnchor>
|
||||
</LogsContainer>
|
||||
</div>
|
||||
}
|
||||
|
||||
</Content>
|
||||
)
|
||||
}
|
||||
</Content>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default withRouter(Installing);
|
||||
382
src/react/components/Status.tsx
Normal file
382
src/react/components/Status.tsx
Normal file
@@ -0,0 +1,382 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
Black,
|
||||
Button,
|
||||
ButtonHover,
|
||||
DarkGray,
|
||||
Gray4,
|
||||
Heading,
|
||||
MainContent,
|
||||
} from '../colors';
|
||||
import { Status, AllStatuses, NodeStatuses } from '../types';
|
||||
import {
|
||||
sendToEthStakerDiscord,
|
||||
sendToEthStakerSubreddit,
|
||||
sendToGoerliEtherscan,
|
||||
sendToPyrmontBeaconchain,
|
||||
sendToGetGoerliEth,
|
||||
sendToEthereumStudymaster,
|
||||
} from '../utils';
|
||||
import {
|
||||
getEth2ClientName,
|
||||
openEth1Logs,
|
||||
openEth2BeaconLogs,
|
||||
openEth2ValidatorLogs,
|
||||
queryEth1PeerCount,
|
||||
queryEth1Status,
|
||||
queryEth1Syncing,
|
||||
queryEth2BeaconStatus,
|
||||
queryEth2ValidatorStatus,
|
||||
startNodes,
|
||||
stopNodes,
|
||||
} from '../commands/RocketPool';
|
||||
import Footer from './Footer';
|
||||
|
||||
const NodeStatus: NodeStatuses = {
|
||||
Online: { code: 0, text: 'Online', character: '\u2B24', color: 'green' },
|
||||
Syncing: { code: 1, text: 'Syncing', character: '\u2B24', color: 'yellow' },
|
||||
Offline: { code: 2, text: 'Offline', character: '\u2B24', color: 'red' },
|
||||
Loading: { code: 3, text: 'Loading...', character: '', color: '' },
|
||||
};
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
`;
|
||||
|
||||
const LandingHeader = styled.div`
|
||||
font-weight: 700;
|
||||
font-size: 35;
|
||||
margin-top: 50;
|
||||
color: ${Heading};
|
||||
max-width: 550;
|
||||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
const Content = styled.div`
|
||||
color: ${MainContent};
|
||||
margin-top: 20;
|
||||
width: 650;
|
||||
flex-grow: 6;
|
||||
`;
|
||||
|
||||
const ResultsTable = styled.table`
|
||||
border: 2px solid gray;
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
text-align: left;
|
||||
color: white;
|
||||
`;
|
||||
|
||||
const StyledLink = styled.span`
|
||||
color: ${Heading};
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const LogsButton = styled.button`
|
||||
color: ${Black};
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: ${Button};
|
||||
border-radius: 10px;
|
||||
text-decoration: none;
|
||||
|
||||
transition: 250ms background-color ease;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: ${ButtonHover};
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
cursor: default;
|
||||
color: ${DarkGray};
|
||||
background-color: ${Gray4};
|
||||
}
|
||||
`;
|
||||
|
||||
// TODO: right after install, while nodes are starting up, this page says everything is "online"
|
||||
// while things are looking for peers. Need to improve that logic.
|
||||
|
||||
const StatusPage = () => {
|
||||
const [eth1ContainerStatus, setEth1ContainerStatus] = useState<Status>(
|
||||
Status.Loading,
|
||||
);
|
||||
const [eth1PeerCount, setEth1PeerCount] = useState(0);
|
||||
const [eth1Syncing, setEth1Syncing] = useState(false);
|
||||
const [eth2ClientName, setEth2ClientName] = useState('');
|
||||
const [eth2BeaconContainerStatus, setEth2BeaconContainerStatus] =
|
||||
useState<Status>(Status.Loading);
|
||||
const [eth2ValidatorContainerStatus, setEth2ValidatorContainerStatus] =
|
||||
useState<Status>(Status.Loading);
|
||||
const [ProcessingTotalStatus, setProcessingTotalStatus] = useState<Status>(
|
||||
Status.Online,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
queryStatuses();
|
||||
setEth2ClientName(getEth2ClientName());
|
||||
setInterval(queryStatuses, 5000);
|
||||
}, 500);
|
||||
}, []);
|
||||
|
||||
const queryStatuses = () => {
|
||||
queryEth1Status(setEth1ContainerStatus);
|
||||
setEth1Syncing(queryEth1Syncing());
|
||||
setEth1PeerCount(queryEth1PeerCount());
|
||||
queryEth2BeaconStatus(setEth2BeaconContainerStatus);
|
||||
queryEth2ValidatorStatus(setEth2ValidatorContainerStatus);
|
||||
};
|
||||
|
||||
const formatStatusIcon = (status: Status) => {
|
||||
return (
|
||||
<span style={{ color: NodeStatus[status].color }}>
|
||||
{NodeStatus[status].character}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
const formatTotalStatusButton = (running: boolean) => {
|
||||
const toggleTotalStatus = async () => {
|
||||
if (!running) {
|
||||
startNodes();
|
||||
setProcessingTotalStatus(Status.Syncing);
|
||||
} else {
|
||||
stopNodes();
|
||||
setProcessingTotalStatus(Status.Offline);
|
||||
}
|
||||
};
|
||||
let text = running ? 'Stop All' : 'Start All';
|
||||
if (ProcessingTotalStatus) text = 'Processing...';
|
||||
let totalStatusChanged =
|
||||
(ProcessingTotalStatus === Status.Syncing && running) ||
|
||||
(ProcessingTotalStatus === Status.Offline && !running);
|
||||
if (totalStatusChanged) setProcessingTotalStatus(Status.Online);
|
||||
return (
|
||||
<LogsButton
|
||||
onClick={toggleTotalStatus}
|
||||
disabled={ProcessingTotalStatus !== Status.Online}
|
||||
>
|
||||
{text}
|
||||
</LogsButton>
|
||||
);
|
||||
};
|
||||
|
||||
const eth1eth2synced = () => {
|
||||
return (
|
||||
computeEth1Status() === Status.Loading &&
|
||||
eth2BeaconContainerStatus === Status.Online
|
||||
);
|
||||
};
|
||||
|
||||
const computeEth1Status = (): AllStatuses => {
|
||||
if (eth1ContainerStatus === Status.Loading) {
|
||||
return Status.Loading;
|
||||
} else if (eth1ContainerStatus === Status.Offline) {
|
||||
return Status.Offline;
|
||||
} else if (eth1Syncing) {
|
||||
return Status.Syncing;
|
||||
} else {
|
||||
return Status.Online;
|
||||
}
|
||||
};
|
||||
|
||||
const computeTotalStatus = () => {
|
||||
return !(
|
||||
computeEth1Status() === Status.Offline &&
|
||||
eth2BeaconContainerStatus === Status.Offline &&
|
||||
eth2ValidatorContainerStatus === Status.Offline
|
||||
);
|
||||
};
|
||||
|
||||
const renderNodeStatusTable = () => {
|
||||
return (
|
||||
<ResultsTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colSpan={3} />
|
||||
<th style={{ width: '100px' }}>
|
||||
{formatTotalStatusButton(computeTotalStatus())}
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Application</th>
|
||||
<th>Status*</th>
|
||||
<th># Peers</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Eth1 Node - geth</td>
|
||||
<td>
|
||||
{formatStatusIcon(computeEth1Status())}
|
||||
{NodeStatus[computeEth1Status()].text}
|
||||
</td>
|
||||
<td>{eth1PeerCount}</td>
|
||||
<td>
|
||||
<LogsButton
|
||||
onClick={openEth1Logs}
|
||||
disabled={eth1ContainerStatus === Status.Offline}
|
||||
>
|
||||
View Logs
|
||||
</LogsButton>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Eth2 Beacon Node - {eth2ClientName}</td>
|
||||
<td>
|
||||
{formatStatusIcon(eth2BeaconContainerStatus)}
|
||||
{NodeStatus[eth2BeaconContainerStatus].text}
|
||||
</td>
|
||||
<td>-</td>
|
||||
<td>
|
||||
<LogsButton
|
||||
onClick={openEth2BeaconLogs}
|
||||
disabled={eth2BeaconContainerStatus === Status.Offline}
|
||||
>
|
||||
View Logs
|
||||
</LogsButton>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Eth2 Validator - {eth2ClientName}</td>
|
||||
<td>
|
||||
{formatStatusIcon(eth2ValidatorContainerStatus)}
|
||||
{NodeStatus[eth2ValidatorContainerStatus].text}
|
||||
</td>
|
||||
<td>-</td>
|
||||
<td>
|
||||
<LogsButton
|
||||
onClick={openEth2ValidatorLogs}
|
||||
disabled={eth2ValidatorContainerStatus === Status.Offline}
|
||||
>
|
||||
View Logs
|
||||
</LogsButton>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</ResultsTable>
|
||||
);
|
||||
};
|
||||
|
||||
const renderResources = () => {
|
||||
return (
|
||||
<ul>
|
||||
<li>
|
||||
Join the EthStaker
|
||||
<StyledLink onClick={sendToEthStakerDiscord}>discord</StyledLink>
|
||||
</li>
|
||||
<li>
|
||||
Check out the EthStaker
|
||||
<StyledLink onClick={sendToEthStakerSubreddit}>subreddit</StyledLink>
|
||||
</li>
|
||||
<li>
|
||||
Join the
|
||||
<StyledLink onClick={sendToEthereumStudymaster}>
|
||||
Ethereum Studymaster
|
||||
</StyledLink>
|
||||
program
|
||||
</li>
|
||||
<li>
|
||||
Grab some
|
||||
<StyledLink onClick={sendToGetGoerliEth}>Goerli ETH</StyledLink>
|
||||
</li>
|
||||
<li>
|
||||
Familiarize yourself with
|
||||
<StyledLink onClick={sendToGoerliEtherscan}>etherscan</StyledLink> and
|
||||
<StyledLink onClick={sendToPyrmontBeaconchain}>
|
||||
beaconcha.in
|
||||
</StyledLink>
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
||||
const renderSubText = () => {
|
||||
if (eth1eth2synced()) {
|
||||
return (
|
||||
<div>
|
||||
Resources:
|
||||
{renderResources()}
|
||||
If you have not deposited your Goerli eth yet, click Deposit.
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
Syncing may take a while.. here are a few things to do:
|
||||
{renderResources()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const renderContent = () => {
|
||||
return (
|
||||
<Content>
|
||||
{renderNodeStatusTable()}
|
||||
<br />
|
||||
<br />
|
||||
*Note: "Syncing" state is only supported for Eth1. Eth1 Beacon/Validator
|
||||
statuses are set based on docker status.
|
||||
<br />
|
||||
Supporting "Syncing" state for Eth2 Becon high priority feature to
|
||||
build.
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
{renderSubText()}
|
||||
{
|
||||
// TODO: file size for proxy of progress?
|
||||
// TODO: blinking dot for syncing and running
|
||||
// TODO: load right to status if rp is installed
|
||||
// TODO: additional data, maybe even pull some from beaconcha.in
|
||||
// TODO: add buttons to stop/start/update nodes
|
||||
// TODO: add button for logs
|
||||
// TODO: direct user to beaconcha.in to track their validator
|
||||
// TODO: button to configure alerts from beaconcha.in?
|
||||
}
|
||||
</Content>
|
||||
);
|
||||
};
|
||||
|
||||
const renderFooter = () => {
|
||||
if (eth1eth2synced()) {
|
||||
return (
|
||||
<Footer
|
||||
backLink={'/systemcheck'}
|
||||
backLabel={'Back'}
|
||||
nextLink={'/deposit'}
|
||||
nextLabel={'Deposit'}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Footer
|
||||
backLink={'/systemcheck'}
|
||||
backLabel={'Back'}
|
||||
nextLink={''}
|
||||
nextLabel={''}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<LandingHeader>Status</LandingHeader>
|
||||
{renderContent()}
|
||||
{renderFooter()}
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default StatusPage;
|
||||
@@ -1,20 +1,15 @@
|
||||
import {
|
||||
DarkBlue,
|
||||
Heading,
|
||||
MainContent
|
||||
} from "../colors";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import Footer from "../components/Footer";
|
||||
import { isRocketPoolInstalled } from "../commands/RocketPool";
|
||||
import styled from "styled-components";
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { DarkBlue, Heading, MainContent } from '../colors';
|
||||
import Footer from './Footer';
|
||||
import { isRocketPoolInstalled } from '../commands/RocketPool';
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height:100vh;
|
||||
min-height: 100vh;
|
||||
`;
|
||||
|
||||
const LandingHeader = styled.div`
|
||||
@@ -23,7 +18,7 @@ const LandingHeader = styled.div`
|
||||
margin-top: 50;
|
||||
color: ${Heading};
|
||||
max-width: 550;
|
||||
flex-grow:1;
|
||||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
const Content = styled.div`
|
||||
@@ -34,7 +29,7 @@ const Content = styled.div`
|
||||
`;
|
||||
|
||||
const Advanced = styled.div`
|
||||
font-size:15;
|
||||
font-size: 15;
|
||||
color: ${DarkBlue};
|
||||
max-width: 550;
|
||||
cursor: pointer;
|
||||
@@ -48,22 +43,18 @@ const ResultsTable = styled.table`
|
||||
color: white;
|
||||
`;
|
||||
|
||||
const SystemCheck = () => {
|
||||
export const SystemCheck = () => {
|
||||
const [showAdvanced, setShowAdvanced] = useState(false);
|
||||
|
||||
const [arbitraryTest, setArbitraryTest] = useState(true);
|
||||
const [rocketPoolInstalled, setRocketPoolInstalled] = useState(false);
|
||||
const [systemStatus, setSystemStatus] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
runSystemCheck();
|
||||
}, [])
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setSystemStatus(
|
||||
arbitraryTest &&
|
||||
!rocketPoolInstalled
|
||||
);
|
||||
setSystemStatus(arbitraryTest && !rocketPoolInstalled);
|
||||
}, [arbitraryTest, rocketPoolInstalled]);
|
||||
|
||||
const runSystemCheck = () => {
|
||||
@@ -77,24 +68,14 @@ const SystemCheck = () => {
|
||||
|
||||
// TODO: add instructions/links for install/fix if a test fails
|
||||
// TODO: create a loading state and only render once the test is finished
|
||||
}
|
||||
};
|
||||
|
||||
const resultToIcon = (result: boolean): string => {
|
||||
// TODO: make these icons - green check and red X
|
||||
return result ? "Pass" : "Fail";
|
||||
}
|
||||
return result ? 'Pass' : 'Fail';
|
||||
};
|
||||
|
||||
const toggleShowAdvanced = () => {
|
||||
setShowAdvanced(!showAdvanced);
|
||||
}
|
||||
|
||||
const renderRocketPoolInstalledContent = () => {
|
||||
return (
|
||||
<Content>
|
||||
It looks like you already have Rocket Pool installed. Let's hop over to check the status of your node.
|
||||
</Content>
|
||||
);
|
||||
}
|
||||
const toggleShowAdvanced = () => setShowAdvanced(!showAdvanced);
|
||||
|
||||
const renderSystemCheckResultsTable = () => {
|
||||
// TODO: make this dynamic
|
||||
@@ -108,7 +89,9 @@ const SystemCheck = () => {
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Rocket Pool <i>not</i> installed</td>
|
||||
<td>
|
||||
Rocket Pool <i>not</i> installed
|
||||
</td>
|
||||
<td>{resultToIcon(!rocketPoolInstalled)}</td>
|
||||
</tr>
|
||||
{/* This test is used for development purposes, so we can toggle different states easily */}
|
||||
@@ -118,39 +101,8 @@ const SystemCheck = () => {
|
||||
</tr> */}
|
||||
</tbody>
|
||||
</ResultsTable>
|
||||
)
|
||||
}
|
||||
|
||||
const renderSystemReady = () => {
|
||||
return (
|
||||
<div>
|
||||
We are good to go. We will pick a random eth1 and eth2 client to install in order to promote client diversity.
|
||||
<br/>
|
||||
<br/>
|
||||
<Advanced onClick={toggleShowAdvanced}>Advanced</Advanced>
|
||||
{ // TODO: fix jump when this is clicked, or redisgn this alltogether
|
||||
// TODO: add pivoting dropdown arrow to signify menu opening and closing?
|
||||
showAdvanced ?
|
||||
<div>
|
||||
<br />
|
||||
<em>This is where we will allow users to pick their client, customize params, etc, however it is not yet implemented.</em>
|
||||
<br /><br />
|
||||
Come join the team and help out :)
|
||||
</div>
|
||||
:
|
||||
<div></div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const renderCantProceed = () => {
|
||||
return (
|
||||
<div>
|
||||
Unfortunately your system does not meet the requirements so we cannot proceed :(
|
||||
</div>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
const renderSystemCheckContent = () => {
|
||||
return (
|
||||
@@ -158,37 +110,89 @@ const SystemCheck = () => {
|
||||
We did some system checks, here are the results:
|
||||
<br />
|
||||
<br />
|
||||
{ renderSystemCheckResultsTable() }
|
||||
{renderSystemCheckResultsTable()}
|
||||
<br />
|
||||
<br />
|
||||
{ systemStatus ? renderSystemReady() : renderCantProceed() }
|
||||
{systemStatus ? (
|
||||
<div>
|
||||
We are good to go. We will pick a random eth1 and eth2 client to
|
||||
install in order to promote client diversity.
|
||||
<br />
|
||||
<br />
|
||||
<Advanced onClick={toggleShowAdvanced}>Advanced</Advanced>
|
||||
{
|
||||
// TODO: fix jump when this is clicked, or redesign this altogether
|
||||
// TODO: add pivoting dropdown arrow to signify menu opening and closing?
|
||||
|
||||
showAdvanced ? (
|
||||
<div>
|
||||
<br />
|
||||
<em>
|
||||
This is where we will allow users to pick their client,
|
||||
customize params, etc, however it is not yet implemented.
|
||||
</em>
|
||||
<br />
|
||||
<br />
|
||||
Come join the team and help out :)
|
||||
</div>
|
||||
) : (
|
||||
<div />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
Unfortunately your system does not meet the requirements so we
|
||||
cannot proceed :(
|
||||
</div>
|
||||
)}
|
||||
</Content>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const renderContent = () => {
|
||||
if (rocketPoolInstalled) {
|
||||
return renderRocketPoolInstalledContent();
|
||||
return (
|
||||
<Content>
|
||||
It looks like you already have Rocket Pool installed. Let's hop over
|
||||
to check the status of your node.
|
||||
</Content>
|
||||
);
|
||||
} else {
|
||||
return renderSystemCheckContent();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const renderFooter = () => {
|
||||
if (systemStatus) {
|
||||
return (
|
||||
<Footer backLink={"/"} backLabel={"Back"} nextLink={"/installing"} nextLabel={"Install"} />
|
||||
)
|
||||
<Footer
|
||||
backLink={'/'}
|
||||
backLabel={'Back'}
|
||||
nextLink={'/installing'}
|
||||
nextLabel={'Install'}
|
||||
/>
|
||||
);
|
||||
} else if (rocketPoolInstalled) {
|
||||
return (
|
||||
<Footer backLink={"/"} backLabel={"Back"} nextLink={"/status"} nextLabel={"Status"} />
|
||||
)
|
||||
<Footer
|
||||
backLink={'/'}
|
||||
backLabel={'Back'}
|
||||
nextLink={'/status'}
|
||||
nextLabel={'Status'}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Footer backLink={"/"} backLabel={"Back"} nextLink={""} nextLabel={""} />
|
||||
)
|
||||
<Footer
|
||||
backLink={'/'}
|
||||
backLabel={'Back'}
|
||||
nextLink={''}
|
||||
nextLabel={''}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Container>
|
||||
@@ -196,7 +200,5 @@ const SystemCheck = () => {
|
||||
{renderContent()}
|
||||
{renderFooter()}
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
export default SystemCheck;
|
||||
);
|
||||
};
|
||||
@@ -1,328 +0,0 @@
|
||||
import {
|
||||
Black,
|
||||
Button,
|
||||
ButtonHover,
|
||||
DarkGray,
|
||||
Gray4,
|
||||
Heading,
|
||||
MainContent
|
||||
} from "../colors";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import {
|
||||
getEth2ClientName,
|
||||
openEth1Logs,
|
||||
openEth2BeaconLogs,
|
||||
openEth2ValidatorLogs,
|
||||
queryEth1PeerCount,
|
||||
queryEth1Status,
|
||||
queryEth1Syncing,
|
||||
queryEth2BeaconStatus,
|
||||
queryEth2ValidatorStatus,
|
||||
startNodes,
|
||||
stopNodes,
|
||||
} from '../commands/RocketPool'
|
||||
|
||||
import Footer from "../components/Footer";
|
||||
import { shell } from "electron";
|
||||
import styled from "styled-components";
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height:100vh;
|
||||
`;
|
||||
|
||||
const LandingHeader = styled.div`
|
||||
font-weight: 700;
|
||||
font-size: 35;
|
||||
margin-top: 50;
|
||||
color: ${Heading};
|
||||
max-width: 550;
|
||||
flex-grow:1;
|
||||
`;
|
||||
|
||||
|
||||
const Content = styled.div`
|
||||
color: ${MainContent};
|
||||
margin-top: 20;
|
||||
width: 650;
|
||||
flex-grow: 6;
|
||||
`;
|
||||
|
||||
const ResultsTable = styled.table`
|
||||
border: 2px solid gray;
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
text-align: left;
|
||||
color: white;
|
||||
`;
|
||||
|
||||
const StyledLink = styled.span`
|
||||
color: ${Heading};
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const LogsButton = styled.button`
|
||||
color: ${Black};
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: ${Button};
|
||||
border-radius: 10px;
|
||||
text-decoration: none;
|
||||
|
||||
transition: 250ms background-color ease;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: ${ButtonHover};
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
cursor: default;
|
||||
color: ${DarkGray};
|
||||
background-color: ${Gray4};
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
// TODO: turn this into an enum?
|
||||
const NodeStatus: [string, string, string][] = [
|
||||
["Online", "\u2B24", "green"], // 0
|
||||
["Syncing", "\u2B24", "yellow"], // 1
|
||||
["Offline", "\u2B24", "red"], // 2
|
||||
["Loading...", "", ""] // 3
|
||||
]
|
||||
|
||||
// TODO: right after install, while nodes are starting up, this page says everything is "online"
|
||||
// while things are looking for peers. Need to improve that logic.
|
||||
|
||||
const Status = () => {
|
||||
const [eth1ContainerStatus, setEth1ContainerStatus] = useState(3);
|
||||
const [eth1PeerCount, setEth1PeerCount] = useState(0);
|
||||
const [eth1Syncing, setEth1Syncing] = useState(false);
|
||||
const [eth2ClientName, setEth2ClientName] = useState("");
|
||||
const [eth2BeaconContainerStatus, setEth2BeaconContainerStatus] = useState(3);
|
||||
const [eth2ValidatorContainerStatus, setEth2ValidatorContainerStatus] = useState(3);
|
||||
const [ProcessingTotalStatus, setProcessingTotalStatus] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
queryStatuses();
|
||||
setEth2ClientName(getEth2ClientName());
|
||||
setInterval(queryStatuses, 5000);
|
||||
}, 500)
|
||||
}, []);
|
||||
|
||||
const queryStatuses = () => {
|
||||
queryEth1Status(setEth1ContainerStatus);
|
||||
setEth1Syncing(queryEth1Syncing());
|
||||
setEth1PeerCount(queryEth1PeerCount());
|
||||
queryEth2BeaconStatus(setEth2BeaconContainerStatus);
|
||||
queryEth2ValidatorStatus(setEth2ValidatorContainerStatus);
|
||||
}
|
||||
|
||||
const formatStatusIcon = (status: number) => {
|
||||
return (
|
||||
<span style={{ color: NodeStatus[status][2]}}>{NodeStatus[status][1]}</span>
|
||||
)
|
||||
}
|
||||
|
||||
const formatTotalStatusButton = (running: boolean) => {
|
||||
const toggleTotalStatus = async () => {
|
||||
if (!running)
|
||||
{
|
||||
startNodes()
|
||||
setProcessingTotalStatus(1);
|
||||
}
|
||||
else {
|
||||
stopNodes()
|
||||
setProcessingTotalStatus(2);
|
||||
}
|
||||
}
|
||||
let text = running ? "Stop All" : "Start All";
|
||||
if (ProcessingTotalStatus)
|
||||
text = "Processing...";
|
||||
let totalStatusChanged = ProcessingTotalStatus == 1 && running ||
|
||||
ProcessingTotalStatus == 2 && !running;
|
||||
if (totalStatusChanged)
|
||||
setProcessingTotalStatus(0);
|
||||
return (
|
||||
<LogsButton onClick={toggleTotalStatus} disabled={ProcessingTotalStatus != 0}>{text}</LogsButton>
|
||||
)
|
||||
}
|
||||
|
||||
const eth1eth2synced = () => {
|
||||
return computeEth1Status() == 0 && computeEth2BeaconStatus() == 0;
|
||||
}
|
||||
|
||||
const computeEth1Status = (): number => {
|
||||
if (eth1ContainerStatus == 3) {
|
||||
return 3;
|
||||
} else if (eth1ContainerStatus == 2) {
|
||||
return 2;
|
||||
} else if (eth1Syncing) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const computeEth2BeaconStatus = () => {
|
||||
return eth2BeaconContainerStatus;
|
||||
}
|
||||
|
||||
const computeEth2ValidatorStatus = () => {
|
||||
return eth2ValidatorContainerStatus;
|
||||
}
|
||||
|
||||
const computeTotalStatus = () => {
|
||||
return !(computeEth1Status() == 2 && eth2BeaconContainerStatus == 2 && eth2ValidatorContainerStatus == 2);
|
||||
}
|
||||
|
||||
const renderNodeStatusTable = () => {
|
||||
return (
|
||||
<ResultsTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colSpan={3}/>
|
||||
<th style={{width: "100px"}}>{formatTotalStatusButton(computeTotalStatus())}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Application</th>
|
||||
<th>Status*</th>
|
||||
<th># Peers</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Eth1 Node - geth</td>
|
||||
<td>{formatStatusIcon(computeEth1Status())} {NodeStatus[computeEth1Status()][0]}</td>
|
||||
<td>{eth1PeerCount}</td>
|
||||
<td><LogsButton onClick={openEth1Logs} disabled={eth1ContainerStatus == 2}>View Logs</LogsButton></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Eth2 Beacon Node - {eth2ClientName}</td>
|
||||
<td>{formatStatusIcon(computeEth2BeaconStatus())} {NodeStatus[computeEth2BeaconStatus()][0]}</td>
|
||||
<td>-</td>
|
||||
<td><LogsButton onClick={openEth2BeaconLogs} disabled={eth2BeaconContainerStatus == 2}>View Logs</LogsButton></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Eth2 Validator - {eth2ClientName}</td>
|
||||
<td>{formatStatusIcon(computeEth2ValidatorStatus())} {NodeStatus[computeEth2ValidatorStatus()][0]}</td>
|
||||
<td>-</td>
|
||||
<td><LogsButton onClick={openEth2ValidatorLogs} disabled={eth2ValidatorContainerStatus == 2}>View Logs</LogsButton></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</ResultsTable>
|
||||
)
|
||||
}
|
||||
|
||||
const sendToEthStakerDiscord = () => {
|
||||
shell.openExternal("http://invite.gg/ethstaker");
|
||||
}
|
||||
|
||||
const sendToEthStakerSubreddit = () => {
|
||||
shell.openExternal("https://www.reddit.com/r/ethstaker/");
|
||||
}
|
||||
|
||||
const sendToGoerliEtherscan = () => {
|
||||
shell.openExternal("http://goerli.etherscan.io/");
|
||||
}
|
||||
|
||||
const sendToPyrmontBeaconchain = () => {
|
||||
shell.openExternal("https://pyrmont.beaconcha.in/");
|
||||
}
|
||||
|
||||
const sendToGetGoerliEth = () => {
|
||||
shell.openExternal("https://www.reddit.com/r/ethstaker/comments/ij56ox/best_way_to_get_goerli_ether/");
|
||||
}
|
||||
|
||||
const sendToEthereumStudymaster = () => {
|
||||
shell.openExternal("https://ethereumstudymaster.com/");
|
||||
}
|
||||
|
||||
const renderResources = () => {
|
||||
return (
|
||||
<ul>
|
||||
<li>Join the EthStaker <StyledLink onClick={sendToEthStakerDiscord}>discord</StyledLink></li>
|
||||
<li>Check out the EthStaker <StyledLink onClick={sendToEthStakerSubreddit}>subreddit</StyledLink></li>
|
||||
<li>Join the <StyledLink onClick={sendToEthereumStudymaster}>Ethereum Studymaster</StyledLink> program</li>
|
||||
<li>Grab some <StyledLink onClick={sendToGetGoerliEth}>Goerli ETH</StyledLink></li>
|
||||
<li>Familiarize yourself with <StyledLink onClick={sendToGoerliEtherscan}>etherscan</StyledLink> and <StyledLink onClick={sendToPyrmontBeaconchain}>beaconcha.in</StyledLink> </li>
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
const renderSubText = () => {
|
||||
if (eth1eth2synced()) {
|
||||
return (
|
||||
<div>
|
||||
Resources:
|
||||
{ renderResources() }
|
||||
If you have not deposited your Goerli eth yet, click Deposit.
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
Syncing may take a while.. here are a few things to do:
|
||||
{ renderResources() }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const renderContent = () => {
|
||||
return(
|
||||
<Content>
|
||||
{ renderNodeStatusTable() }
|
||||
<br />
|
||||
<br />
|
||||
*Note: "Syncing" state is only supported for Eth1. Eth1 Beacon/Validator statuses are set based on docker status.
|
||||
<br />
|
||||
Supporting "Syncing" state for Eth2 Becon high priority feature to build.
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
{ renderSubText() }
|
||||
{
|
||||
// TODO: file size for proxy of progress?
|
||||
// TODO: blinking dot for syncing and running
|
||||
// TODO: load right to status if rp is installed
|
||||
// TODO: additional data, maybe even pull some from beaconcha.in
|
||||
// TODO: add buttons to stop/start/update nodes
|
||||
// TODO: add button for logs
|
||||
// TODO: direct user to beaconcha.in to track their validator
|
||||
// TODO: button to configure alerts from beaconcha.in?
|
||||
}
|
||||
</Content>
|
||||
);
|
||||
}
|
||||
|
||||
const renderFooter = () => {
|
||||
if (eth1eth2synced()) {
|
||||
return (
|
||||
<Footer backLink={"/systemcheck"} backLabel={"Back"} nextLink={"/deposit"} nextLabel={"Deposit"} />
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<Footer backLink={"/systemcheck"} backLabel={"Back"} nextLink={""} nextLabel={""} />
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<LandingHeader>Status</LandingHeader>
|
||||
{renderContent()}
|
||||
{renderFooter()}
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
export default Status;
|
||||
23
src/react/types.ts
Normal file
23
src/react/types.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
type StatusDetails = {
|
||||
text: string;
|
||||
code: number;
|
||||
character: string;
|
||||
color: string;
|
||||
};
|
||||
|
||||
export enum Status {
|
||||
'Online' = 'Online',
|
||||
'Syncing' = 'Syncing',
|
||||
'Offline' = 'Offline',
|
||||
'Loading' = 'Loading',
|
||||
}
|
||||
|
||||
export type AllStatuses =
|
||||
| Status.Online
|
||||
| Status.Syncing
|
||||
| Status.Offline
|
||||
| Status.Loading;
|
||||
|
||||
export type NodeStatuses = {
|
||||
[key in AllStatuses]: StatusDetails;
|
||||
};
|
||||
26
src/react/utils.tsx
Normal file
26
src/react/utils.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { shell } from 'electron';
|
||||
export const sendToEthStakerDiscord = () => {
|
||||
shell.openExternal('http://invite.gg/ethstaker');
|
||||
};
|
||||
|
||||
export const sendToEthStakerSubreddit = () => {
|
||||
shell.openExternal('https://www.reddit.com/r/ethstaker/');
|
||||
};
|
||||
|
||||
export const sendToGoerliEtherscan = () => {
|
||||
shell.openExternal('http://goerli.etherscan.io/');
|
||||
};
|
||||
|
||||
export const sendToPyrmontBeaconchain = () => {
|
||||
shell.openExternal('https://pyrmont.beaconcha.in/');
|
||||
};
|
||||
|
||||
export const sendToGetGoerliEth = () => {
|
||||
shell.openExternal(
|
||||
'https://www.reddit.com/r/ethstaker/comments/ij56ox/best_way_to_get_goerli_ether/',
|
||||
);
|
||||
};
|
||||
|
||||
export const sendToEthereumStudymaster = () => {
|
||||
shell.openExternal('https://ethereumstudymaster.com/');
|
||||
};
|
||||
Reference in New Issue
Block a user