auto-format code by prettier (similar to gofmt) (#405)

This commit is contained in:
Alex Sharov 2020-03-25 12:45:21 +07:00 committed by GitHub
parent b490192e67
commit b0f962e3ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1227 additions and 1170 deletions

View File

@ -2,9 +2,9 @@ This project was bootstrapped with [Create React App](https://github.com/faceboo
## Prerequisites ## Prerequisites
* Node.js v13 - Node.js v13
* yarn (install with `npm install -g yarn`) - yarn (install with `npm install -g yarn`)
## Quickstart ## Quickstart
@ -16,6 +16,9 @@ This project was bootstrapped with [Create React App](https://github.com/faceboo
4. Run the web UI: `make run-web-ui` 4. Run the web UI: `make run-web-ui`
## Auto-Format code on save:
For Webstorm: https://blog.jetbrains.com/webstorm/2020/02/webstorm-2020-1-eap-5/
## Available Scripts ## Available Scripts
@ -44,16 +47,6 @@ Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `yarn eject`
**Note: this is a one-way operation. Once you `eject`, you cant go back!**
If you arent satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point youre on your own.
You dont have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldnt feel obligated to use this feature. However we understand that this tool wouldnt be useful if you couldnt customize it when you are ready for it.
## Learn More ## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).

View File

@ -5,17 +5,17 @@
"dependencies": { "dependencies": {
"axios": "^0.19.2", "axios": "^0.19.2",
"bootstrap": "^4.4.1", "bootstrap": "^4.4.1",
"react": "^16.13.0", "react": "^16.13.1",
"react-bootstrap": "^1.0.0-beta.17", "react-bootstrap": "^1.0.0",
"react-dom": "^16.13.0", "react-dom": "^16.13.1",
"react-router-dom": "^5.1.2", "react-router-dom": "^5.1.2",
"react-scripts": "3.4.0" "react-scripts": "3.4.1"
}, },
"devDependencies": { "devDependencies": {
"@testing-library/jest-dom": "^5.1.1", "@testing-library/jest-dom": "^5.1.1",
"@testing-library/react": "^10.0.1", "@testing-library/react": "^10.0.1",
"@testing-library/user-event": "^10.0.0", "@testing-library/user-event": "^10.0.0",
"prettier": "^1.19.1" "prettier": "^2.0.2"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "react-scripts start",
@ -26,6 +26,10 @@
"eslintConfig": { "eslintConfig": {
"extends": "react-app" "extends": "react-app"
}, },
"prettier": {
"singleQuote": true,
"printWidth": 120
},
"browserslist": { "browserslist": {
"production": [ "production": [
">0.2%", ">0.2%",

View File

@ -1,23 +1,23 @@
body { body {
font-size: 0.8125em; font-size: 0.8125em;
} }
.sidebar a { .sidebar a {
position: relative; position: relative;
display: block; display: block;
} }
.sidebar a .active-pointer { .sidebar a .active-pointer {
display: none; display: none;
} }
.sidebar a.active .active-pointer { .sidebar a.active .active-pointer {
display: inline-block; display: inline-block;
position: absolute; position: absolute;
border-bottom: 10px solid transparent; border-bottom: 10px solid transparent;
border-right: 10px solid #45378E; border-right: 10px solid #45378e;
border-top: 10px solid transparent; border-top: 10px solid transparent;
height: 0; height: 0;
width: 0; width: 0;
right: 0; right: 0;
} }

View File

@ -1,83 +1,83 @@
import React, {useState} from 'react'; import React, { useState } from 'react';
import {Col, Container, Nav, Row} from 'react-bootstrap'; import { Col, Container, Nav, Row } from 'react-bootstrap';
import API from './utils/API.js' import API from './utils/API.js';
import ErrorCatcher from './components/ErrorCatcher.js' import ErrorCatcher from './components/ErrorCatcher.js';
import {BrowserRouter as Router, Link, NavLink, Redirect, Route, Switch} from 'react-router-dom'; import { BrowserRouter as Router, Link, NavLink, Redirect, Route, Switch } from 'react-router-dom';
import AccountsPage from './page/Accounts'; import AccountsPage from './page/Accounts';
import StorageTombstonesPage from './page/StorageTombstonesPage'; import StorageTombstonesPage from './page/StorageTombstonesPage';
import {ReactComponent as Logo} from './logo.svg'; import { ReactComponent as Logo } from './logo.svg';
import './App.css'; import './App.css';
import StoragePage from './page/Storage'; import StoragePage from './page/Storage';
import RemoteSidebar from './components/RemoteSidebar'; import RemoteSidebar from './components/RemoteSidebar';
const sidebar = [ const sidebar = [
{ {
url: '/accounts', url: '/accounts',
label: 'Accounts', label: 'Accounts',
}, },
{ {
url: '/storage', url: '/storage',
label: 'Storage', label: 'Storage',
}, },
{ {
url: '/storage-tombstones', url: '/storage-tombstones',
label: 'Storage Tombs', label: 'Storage Tombs',
}, },
]; ];
function App() { function App() {
const [host, setHost] = useState('localhost'); const [host, setHost] = useState('localhost');
const [port, setPort] = useState('8080'); const [port, setPort] = useState('8080');
const onApiChange = (data) => { const onApiChange = (data) => {
setHost(data.host) setHost(data.host);
setPort(data.port) setPort(data.port);
} };
const api = new API('http://' + host + ':' + port) const api = new API('http://' + host + ':' + port);
return ( return (
<ErrorCatcher> <ErrorCatcher>
<Router> <Router>
<Container fluid> <Container fluid>
<Row> <Row>
<Col className="border-right p-0 sidebar min-vh-100" xs={3} md={2} lg={1}> <Col className="border-right p-0 sidebar min-vh-100" xs={3} md={2} lg={1}>
<Nav className="flex-column sticky-top"> <Nav className="flex-column sticky-top">
<div className="mb-2 pb-1 border-bottom"> <div className="mb-2 pb-1 border-bottom">
<Link to="/"> <Link to="/">
<Logo alt="Logo" sidth="120" height="120" className="d-block w-100"/> <Logo alt="Logo" sidth="120" height="120" className="d-block w-100" />
</Link> </Link>
<div className="d-flex justify-content-center h4">Scope</div> <div className="d-flex justify-content-center h4">Scope</div>
</div> </div>
{sidebar.map((el, i) => {sidebar.map((el, i) => (
<NavLink key={i} to={el.url} <NavLink key={i} to={el.url} className="pl-2 mb-2 font-weight-light">
className="pl-2 mb-2 font-weight-light">{el.label} {el.label}
<div className="active-pointer"/> <div className="active-pointer" />
</NavLink> </NavLink>
)} ))}
<div className="mt-5 border-secondary border-top"/> <div className="mt-5 border-secondary border-top" />
<RemoteSidebar api={api} restHost={host} restPort={port} onApiChange={onApiChange}/> <RemoteSidebar api={api} restHost={host} restPort={port} onApiChange={onApiChange} />
</Nav> </Nav>
</Col> </Col>
<Col xs={9} md={10} lg={11}> <Col xs={9} md={10} lg={11}>
<Switch> <Switch>
<Redirect exact strict from="/" to="/accounts"/> <Redirect exact strict from="/" to="/accounts" />
<Route exact path="/accounts"> <Route exact path="/accounts">
<AccountsPage api={api}/> <AccountsPage api={api} />
</Route> </Route>
<Route path="/storage"> <Route path="/storage">
<StoragePage api={api}/> <StoragePage api={api} />
</Route> </Route>
<Route path="/storage-tombstones"> <Route path="/storage-tombstones">
<StorageTombstonesPage api={api}/> <StorageTombstonesPage api={api} />
</Route> </Route>
</Switch> </Switch>
</Col> </Col>
</Row> </Row>
</Container> </Container>
</Router> </Router>
</ErrorCatcher> </ErrorCatcher>
); );
} }
export default App; export default App;

View File

@ -1,62 +1,66 @@
import React from 'react' import React from 'react';
import Modal from 'react-bootstrap/Modal' import Modal from 'react-bootstrap/Modal';
import Button from 'react-bootstrap/Button' import Button from 'react-bootstrap/Button';
export default class ErrorCatcher extends React.Component { export default class ErrorCatcher extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = {error: undefined, errorInfo: undefined} this.state = { error: undefined, errorInfo: undefined };
this.handleClose = this.handleClose.bind(this); this.handleClose = this.handleClose.bind(this);
this.handleReload = this.handleReload.bind(this); this.handleReload = this.handleReload.bind(this);
} }
static getDerivedStateFromError(error) { static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI. // Update state so the next render will show the fallback UI.
return {error: error}; return { error: error };
} }
componentDidCatch(error, errorInfo) { componentDidCatch(error, errorInfo) {
this.setState({error: error, errorInfo: errorInfo}) this.setState({ error: error, errorInfo: errorInfo });
} }
handleClose(event) { handleClose(event) {
this.setState({error: undefined}); this.setState({ error: undefined });
} }
handleReload(event) { handleReload(event) {
// force reload // force reload
window.location.reload(); window.location.reload();
} }
render() { render() {
let err = this.state.error; let err = this.state.error;
let show = err !== undefined; let show = err !== undefined;
let info = this.state.errorInfo !== undefined ? this.state.errorInfo.componentStack : ''; let info = this.state.errorInfo !== undefined ? this.state.errorInfo.componentStack : '';
let details = process.env.NODE_ENV === 'development' ? info : ''; let details = process.env.NODE_ENV === 'development' ? info : '';
let serverErrors = err !== undefined && err.response !== undefined && Array.isArray(err.response.data) ? err.response.data : []; let serverErrors =
err !== undefined && err.response !== undefined && Array.isArray(err.response.data) ? err.response.data : [];
return ( return (
<div className={this.props.className}> <div className={this.props.className}>
{this.props.children} {this.props.children}
<Modal show={show} onHide={this.handleClose} size="xl"> <Modal show={show} onHide={this.handleClose} size="xl">
<Modal.Header> <Modal.Header>
<Modal.Title>Unexpected Error</Modal.Title> <Modal.Title>Unexpected Error</Modal.Title>
</Modal.Header> </Modal.Header>
<Modal.Body> <Modal.Body>
<code>Server Error: {serverErrors.join("<br/>")}</code> <code>Server Error: {serverErrors.join('<br/>')}</code>
<code>{this.state.error && this.state.error.message}</code> <code>{this.state.error && this.state.error.message}</code>
<pre>{details}</pre> <pre>{details}</pre>
</Modal.Body> </Modal.Body>
<Modal.Footer> <Modal.Footer>
<Button variant="secondary" onClick={this.handleClose}>Ignore</Button> <Button variant="secondary" onClick={this.handleClose}>
<Button variant="primary" onClick={this.handleReload}>Reload</Button> Ignore
</Modal.Footer> </Button>
</Modal> <Button variant="primary" onClick={this.handleReload}>
</div> Reload
); </Button>
} </Modal.Footer>
</Modal>
} </div>
);
}
}

View File

@ -1,9 +1,7 @@
import React from 'react'; import React from 'react';
const ErrorForm = ({message}) => { const ErrorForm = ({ message }) => {
return ( return <code>{message}</code>;
<code>{message}</code> };
);
}
export default ErrorForm; export default ErrorForm;

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react' import React, { useState } from 'react';
import Row from 'react-bootstrap/Row'; import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col'; import Col from 'react-bootstrap/Col';
@ -7,63 +7,68 @@ import { Table } from 'react-bootstrap';
import ErrorForm from './ErrorForm.js'; import ErrorForm from './ErrorForm.js';
import SearchField from './SearchField.js'; import SearchField from './SearchField.js';
const loadAccount = (id, api) => { const loadAccount = (id, api) => {
return api.lookupAccount(id) return api.lookupAccount(id);
} };
const LookupAccountForm = ({api}) => { const LookupAccountForm = ({ api }) => {
const [state, setState] = useState({account: undefined, error: undefined, loading: false}); const [state, setState] = useState({ account: undefined, error: undefined, loading: false });
const lookupSuccess = (response) => setState({account: response.data, error: undefined}); const lookupSuccess = (response) => setState({ account: response.data, error: undefined });
const lookupFail = (error) => { const lookupFail = (error) => {
if (error && error.response && error.response.status === 404) { if (error && error.response && error.response.status === 404) {
setState({account: undefined, error: error}) setState({ account: undefined, error: error });
} else { } else {
setState(() => { throw error }) setState(() => {
} throw error;
});
} }
};
return ( return (
<div> <div>
<SearchField placeholder="lookup by id or hash" <SearchField
onClick={(id) => loadAccount(id, api) placeholder="lookup by id or hash"
.then(lookupSuccess) onClick={(id) => loadAccount(id, api).then(lookupSuccess).catch(lookupFail)}
.catch(lookupFail)} /> />
<hr /> <hr />
{state.account && <DetailsForm account={state.account} />} {state.account && <DetailsForm account={state.account} />}
{state.error && <ErrorForm message={"Account not found"} />} {state.error && <ErrorForm message={'Account not found'} />}
</div> </div>
); );
} };
const DetailsForm = ({account}) => ( const DetailsForm = ({ account }) => (
<Row> <Row>
<Col> <Col>
<Table size="sm"> <Table size="sm">
<thead> <thead>
<tr> <tr>
<th><strong>Account</strong></th> <th>
<th></th> <strong>Account</strong>
</tr> </th>
</thead> <th></th>
<tbody> </tr>
<TableRow name="balance" value={account.balance} /> </thead>
<TableRow name="nonce" value={account.nonce} /> <tbody>
<TableRow name="code hash" value={account.code_hash} /> <TableRow name="balance" value={account.balance} />
<TableRow name="storage hash" value={account.root_hash} /> <TableRow name="nonce" value={account.nonce} />
<TableRow name="incarnation (turbo-geth)" value={account.implementation.incarnation} /> <TableRow name="code hash" value={account.code_hash} />
</tbody> <TableRow name="storage hash" value={account.root_hash} />
</Table> <TableRow name="incarnation (turbo-geth)" value={account.implementation.incarnation} />
</Col> </tbody>
</Row> </Table>
</Col>
</Row>
); );
const TableRow = ({name, value}) => ( const TableRow = ({ name, value }) => (
<tr> <tr>
<td>{name}</td> <td>{name}</td>
<td><code>{value}</code></td> <td>
</tr> <code>{value}</code>
</td>
</tr>
); );
export default LookupAccountForm; export default LookupAccountForm;

View File

@ -1,60 +1,67 @@
import React, {useState} from 'react' import React, { useState } from 'react';
import Row from 'react-bootstrap/Row'; import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col'; import Col from 'react-bootstrap/Col';
import {Spinner, Table} from 'react-bootstrap'; import { Spinner, Table } from 'react-bootstrap';
import SearchField from './SearchField.js'; import SearchField from './SearchField.js';
const search = (prefix, api, setState) => { const search = (prefix, api, setState) => {
setState({data: undefined, loading: true}); setState({ data: undefined, loading: true });
const lookupSuccess = (response) => setState({data: response.data, loading: false}); const lookupSuccess = (response) => setState({ data: response.data, loading: false });
const lookupFail = (error) => { const lookupFail = (error) => {
setState({data: undefined, loading: false}) setState({ data: undefined, loading: false });
setState(() => { setState(() => {
throw error throw error;
}) });
} };
return api.lookupStorage(prefix).then(lookupSuccess).catch(lookupFail); return api.lookupStorage(prefix).then(lookupSuccess).catch(lookupFail);
} };
const LookupStorageForm = ({api}) => { const LookupStorageForm = ({ api }) => {
const [state, setState] = useState({data: undefined, loading: false}); const [state, setState] = useState({ data: undefined, loading: false });
return ( return (
<div> <div>
{state.loading && <Spinner animation="border"/>} {state.loading && <Spinner animation="border" />}
{!state.loading && {!state.loading && (
<SearchField placeholder="lookup by prefix" onClick={(prefix) => search(prefix, api, setState)}/>} <SearchField placeholder="lookup by prefix" onClick={(prefix) => search(prefix, api, setState)} />
{state.data && <Details data={state.data}/>} )}
</div> {state.data && <Details data={state.data} />}
); </div>
} );
};
const Details = ({data}) => ( const Details = ({ data }) => (
<Row> <Row>
<Col> <Col>
<Table size="sm" borderless> <Table size="sm" borderless>
<thead> <thead>
<tr> <tr>
<th><strong>Key</strong></th> <th>
<th><strong>Value</strong></th> <strong>Key</strong>
</tr> </th>
</thead> <th>
<tbody> <strong>Value</strong>
{data.map((item, i) => <TableRow key={i} item={item}/>)} </th>
</tbody> </tr>
</Table> </thead>
</Col> <tbody>
</Row> {data.map((item, i) => (
<TableRow key={i} item={item} />
))}
</tbody>
</Table>
</Col>
</Row>
); );
const TableRow = ({item}) => ( const TableRow = ({ item }) => (
<tr> <tr>
<td className="text-monospace">{item.prefix}</td> <td className="text-monospace">{item.prefix}</td>
<td className="text-monospace">{item.value}</td> <td className="text-monospace">{item.value}</td>
</tr> </tr>
); );
export default LookupStorageForm; export default LookupStorageForm;

View File

@ -1,70 +1,73 @@
import React, {useState} from 'react' import React, { useState } from 'react';
import Row from 'react-bootstrap/Row'; import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col'; import Col from 'react-bootstrap/Col';
import {Spinner, Table} from 'react-bootstrap'; import { Spinner, Table } from 'react-bootstrap';
import SearchField from './SearchField.js'; import SearchField from './SearchField.js';
const search = (prefix, api, setState) => { const search = (prefix, api, setState) => {
setState({hashes: undefined, loading: true}); setState({ hashes: undefined, loading: true });
const lookupSuccess = (response) => setState({hashes: response.data, loading: false}); const lookupSuccess = (response) => setState({ hashes: response.data, loading: false });
const lookupFail = (error) => { const lookupFail = (error) => {
setState({hashes: undefined, loading: false}) setState({ hashes: undefined, loading: false });
setState(() => { setState(() => {
throw error throw error;
}) });
} };
return api.lookupStorageTombstones(prefix).then(lookupSuccess).catch(lookupFail);
}
const LookupStorageTombstonesForm = ({api}) => { return api.lookupStorageTombstones(prefix).then(lookupSuccess).catch(lookupFail);
const [state, setState] = useState({hashes: undefined, loading: false});
return (
<div>
{state.loading && <Spinner animation="border"/>}
{!state.loading && <SearchField placeholder="lookup by prefix"
onClick={(prefix) => search(prefix, api, setState)}/>}
{state.hashes && <Details hashes={state.hashes}/>}
</div>
);
}
const Details = ({hashes}) => (
<Row>
<Col>
<Table size="sm" borderless>
<thead>
<tr>
<th><strong>Prefix</strong></th>
<th><strong>Hide storage</strong></th>
</tr>
</thead>
<tbody>
{hashes.map((item, i) => <TableRow key={i} item={item}/>)}
</tbody>
</Table>
</Col>
</Row>
);
const TableRow = ({item}) => {
const {prefix, hideStorage} = item
return (
<tr>
<td className="text-monospace">
{prefix}
</td>
<td className={hideStorage ? '' : 'bg-danger'}>
{hideStorage ? 'yes' : 'no'}
</td>
</tr>
);
}; };
export default LookupStorageTombstonesForm; const LookupStorageTombstonesForm = ({ api }) => {
const [state, setState] = useState({ hashes: undefined, loading: false });
return (
<div>
{state.loading && <Spinner animation="border" />}
{!state.loading && (
<SearchField placeholder="lookup by prefix" onClick={(prefix) => search(prefix, api, setState)} />
)}
{state.hashes && <Details hashes={state.hashes} />}
</div>
);
};
const Details = ({ hashes }) => (
<Row>
<Col>
<Table size="sm" borderless>
<thead>
<tr>
<th>
<strong>Prefix</strong>
</th>
<th>
<strong>Hide storage</strong>
</th>
</tr>
</thead>
<tbody>
{hashes.map((item, i) => (
<TableRow key={i} item={item} />
))}
</tbody>
</Table>
</Col>
</Row>
);
const TableRow = ({ item }) => {
const { prefix, hideStorage } = item;
return (
<tr>
<td className="text-monospace">{prefix}</td>
<td className={hideStorage ? '' : 'bg-danger'}>{hideStorage ? 'yes' : 'no'}</td>
</tr>
);
};
export default LookupStorageTombstonesForm;

View File

@ -1,132 +1,132 @@
import React, {useState} from 'react'; import React, { useState } from 'react';
import {Button, Form, Modal} from 'react-bootstrap'; import { Button, Form, Modal } from 'react-bootstrap';
const RemoteSidebar = ({api, restHost, restPort, onApiChange}) => { const RemoteSidebar = ({ api, restHost, restPort, onApiChange }) => {
return ( return (
<React.Fragment> <React.Fragment>
<RestApiForm host={restHost} port={restPort} onApiChange={onApiChange}/> <RestApiForm host={restHost} port={restPort} onApiChange={onApiChange} />
<RemoteDBForm api={api}/> <RemoteDBForm api={api} />
</React.Fragment> </React.Fragment>
) );
} };
const RestApiForm = ({host, port, onApiChange}) => { const RestApiForm = ({ host, port, onApiChange }) => {
const [show, setShow] = useState(false); const [show, setShow] = useState(false);
const handleSubmit = (e) => { const handleSubmit = (e) => {
e.preventDefault(); e.preventDefault();
setShow(false); setShow(false);
let form = e.target; let form = e.target;
onApiChange({host: form.elements.host.value, port: form.elements.port.value}); onApiChange({ host: form.elements.host.value, port: form.elements.port.value });
} };
const handleClick = (e) => { const handleClick = (e) => {
e.preventDefault(); e.preventDefault();
setShow(true) setShow(true);
} };
return ( return (
<div className="mb-2 font-weight-light text-break"> <div className="mb-2 font-weight-light text-break">
<a href="/rest-api" className="nav-link px-2" onClick={handleClick}> <a href="/rest-api" className="nav-link px-2" onClick={handleClick}>
Rest API<br/> Rest API
{host && host + ':' + port} <br />
</a> {host && host + ':' + port}
<ModalWindow title="Rest API" show={show} onHide={() => setShow(false)}> </a>
<Form onSubmit={handleSubmit}> <ModalWindow title="Rest API" show={show} onHide={() => setShow(false)}>
<Input label="Host" defaultValue={host}/> <Form onSubmit={handleSubmit}>
<Input label="Port" defaultValue={port}/> <Input label="Host" defaultValue={host} />
<Button type="submit">Submit</Button> <Input label="Port" defaultValue={port} />
</Form> <Button type="submit">Submit</Button>
</ModalWindow> </Form>
</div> </ModalWindow>
); </div>
} );
};
const get = (api, setHost, setPort) => { const get = (api, setHost, setPort) => {
const lookupSuccess = (response) => { const lookupSuccess = (response) => {
setHost(response.data.host); setHost(response.data.host);
setPort(response.data.port); setPort(response.data.port);
} };
const lookupFail = (error) => { const lookupFail = (error) => {
setHost(() => { setHost(() => {
throw error throw error;
}) });
} };
return api.getRemoteDB().then(lookupSuccess).catch(lookupFail); return api.getRemoteDB().then(lookupSuccess).catch(lookupFail);
} };
const set = (host, port, api, setHost, setPort) => { const set = (host, port, api, setHost, setPort) => {
const lookupSuccess = () => { const lookupSuccess = () => {
setHost(host); setHost(host);
setPort(port); setPort(port);
}; };
const lookupFail = (error) => { const lookupFail = (error) => {
setHost(() => { setHost(() => {
throw error throw error;
}) });
} };
return api.setRemoteDB(host, port).then(lookupSuccess).catch(lookupFail); return api.setRemoteDB(host, port).then(lookupSuccess).catch(lookupFail);
} };
const RemoteDBForm = ({api}) => { const RemoteDBForm = ({ api }) => {
const [host, setHost] = useState(''); const [host, setHost] = useState('');
const [port, setPort] = useState(''); const [port, setPort] = useState('');
const [show, setShow] = useState(false); const [show, setShow] = useState(false);
const handleSubmit = (e) => { const handleSubmit = (e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
const form = e.target; const form = e.target;
set(form.elements.host.value, form.elements.port.value, api, setHost, setPort) set(form.elements.host.value, form.elements.port.value, api, setHost, setPort);
setShow(false) setShow(false);
} };
const handleClick = (e) => { const handleClick = (e) => {
e.preventDefault(); e.preventDefault();
setShow(true) setShow(true);
get(api, setHost, setPort) get(api, setHost, setPort);
} };
return ( return (
<div className="pl-2 mb-2 font-weight-light text-break"> <div className="pl-2 mb-2 font-weight-light text-break">
<a href="/remote-db" onClick={handleClick}> <a href="/remote-db" onClick={handleClick}>
Remote DB<br/> Remote DB
{host && host + ':' + port} <br />
</a> {host && host + ':' + port}
<ModalWindow title="Remote DB" show={show} onHide={() => setShow(false)}> </a>
<Form onSubmit={handleSubmit}> <ModalWindow title="Remote DB" show={show} onHide={() => setShow(false)}>
<Input label="Host" defaultValue={host}/> <Form onSubmit={handleSubmit}>
<Input label="Port" defaultValue={port}/> <Input label="Host" defaultValue={host} />
<Button type="submit">Submit</Button> <Input label="Port" defaultValue={port} />
</Form> <Button type="submit">Submit</Button>
</ModalWindow> </Form>
</div> </ModalWindow>
); </div>
} );
};
const Input = ({label, ...props}) => ( const Input = ({ label, ...props }) => (
<Form.Group controlId={label.toLowerCase()}> <Form.Group controlId={label.toLowerCase()}>
<Form.Label>{label}</Form.Label> <Form.Label>{label}</Form.Label>
<Form.Control <Form.Control
type="text" type="text"
placeholder={label} placeholder={label}
aria-describedby={"addon" + label.toLowerCase()} aria-describedby={'addon' + label.toLowerCase()}
name={label.toLowerCase()} name={label.toLowerCase()}
{...props} {...props}
/> />
</Form.Group> </Form.Group>
) );
const ModalWindow = ({children, title, ...props}) => ( const ModalWindow = ({ children, title, ...props }) => (
<Modal {...props}> <Modal {...props}>
<Modal.Header> <Modal.Header>
<Modal.Title>{title}</Modal.Title> <Modal.Title>{title}</Modal.Title>
</Modal.Header> </Modal.Header>
<Modal.Body> <Modal.Body>{children}</Modal.Body>
{children} </Modal>
</Modal.Body> );
</Modal>
)
export default RemoteSidebar; export default RemoteSidebar;

View File

@ -1,45 +1,48 @@
import React from 'react'; import React from 'react';
import Form from 'react-bootstrap/Form'; import Form from 'react-bootstrap/Form';
import Col from 'react-bootstrap/Col' import Col from 'react-bootstrap/Col';
import Button from 'react-bootstrap/Button' import Button from 'react-bootstrap/Button';
class SearchField extends React.Component { class SearchField extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = {value: ''}; this.state = { value: '' };
this.handleChange = this.handleChange.bind(this); this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this); this.handleSubmit = this.handleSubmit.bind(this);
} }
handleChange(event) { handleChange(event) {
this.setState({value: event.target.value}); this.setState({ value: event.target.value });
} }
handleSubmit(event) { handleSubmit(event) {
this.props.onClick(this.state.value); this.props.onClick(this.state.value);
event.preventDefault(); event.preventDefault();
} }
render() { render() {
return ( return (
<Form onSubmit={this.handleSubmit} > <Form onSubmit={this.handleSubmit}>
<Form.Row> <Form.Row>
<Col> <Col>
<Form.Control size="sm" <Form.Control
placeholder={this.props.placeholder} size="sm"
value={this.state.value || ''} placeholder={this.props.placeholder}
onChange={this.handleChange} /> value={this.state.value || ''}
</Col> onChange={this.handleChange}
<Col> />
<Button variant="primary" type="submit" size="sm">Find</Button> </Col>
</Col> <Col>
</Form.Row> <Button variant="primary" type="submit" size="sm">
</Form> Find
); </Button>
} </Col>
</Form.Row>
</Form>
);
}
} }
export default SearchField; export default SearchField;

View File

@ -1,58 +1,68 @@
import React, {useState} from 'react' import React, { useState } from 'react';
import Row from 'react-bootstrap/Row'; import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col'; import Col from 'react-bootstrap/Col';
import {Button, Spinner, Table} from 'react-bootstrap'; import { Button, Spinner, Table } from 'react-bootstrap';
const load = (api, setState) => { const load = (api, setState) => {
const lookupSuccess = (response) => setState({data: response.data, loading: false}); const lookupSuccess = (response) => setState({ data: response.data, loading: false });
const lookupFail = (error) => { const lookupFail = (error) => {
setState({data: undefined, loading: false}) setState({ data: undefined, loading: false });
setState(() => { setState(() => {
throw error throw error;
}) });
} };
setState({data: undefined, loading: true}) setState({ data: undefined, loading: true });
return api.storageTombstonesIntegrityChecks().then(lookupSuccess).catch(lookupFail); return api.storageTombstonesIntegrityChecks().then(lookupSuccess).catch(lookupFail);
} };
const StorageTombstonesIntegrityChecks = ({api}) => { const StorageTombstonesIntegrityChecks = ({ api }) => {
const [state, setState] = useState({data: undefined, loading: false}); const [state, setState] = useState({ data: undefined, loading: false });
return ( return (
<div> <div>
{state.loading && <Spinner animation="border"/>} {state.loading && <Spinner animation="border" />}
{!state.loading && <Button size="sm" onClick={() => load(api, setState)}>Integrity Checks</Button>} {!state.loading && (
{state.data && <DetailsForm data={state.data}/>} <Button size="sm" onClick={() => load(api, setState)}>
</div> Integrity Checks
); </Button>
} )}
{state.data && <DetailsForm data={state.data} />}
</div>
);
};
const DetailsForm = ({data}) => ( const DetailsForm = ({ data }) => (
<Row> <Row>
<Col> <Col>
<Table size="sm" borderless> <Table size="sm" borderless>
<thead> <thead>
<tr> <tr>
<th><strong>Check</strong></th> <th>
<th>Result</th> <strong>Check</strong>
</tr> </th>
</thead> <th>Result</th>
<tbody> </tr>
{data.map((el, i) => <TableRow key={i} name={el.name} value={el.value}/>)} </thead>
</tbody> <tbody>
</Table> {data.map((el, i) => (
</Col> <TableRow key={i} name={el.name} value={el.value} />
</Row> ))}
</tbody>
</Table>
</Col>
</Row>
); );
const TableRow = ({name, value}) => ( const TableRow = ({ name, value }) => (
<tr> <tr>
<td>{name}</td> <td>{name}</td>
<td><code>{value}</code></td> <td>
</tr> <code>{value}</code>
</td>
</tr>
); );
export default StorageTombstonesIntegrityChecks; export default StorageTombstonesIntegrityChecks;

View File

@ -1,22 +1,22 @@
import React from 'react'; import React from 'react';
import Row from "react-bootstrap/Row"; import Row from 'react-bootstrap/Row';
import Col from "react-bootstrap/Col"; import Col from 'react-bootstrap/Col';
import Container from "react-bootstrap/Container"; import Container from 'react-bootstrap/Container';
import LookupAccountForm from "../components/LookupAccountForm"; import LookupAccountForm from '../components/LookupAccountForm';
const AccountsPage = ({api}) => ( const AccountsPage = ({ api }) => (
<Container fluid className="mt-1"> <Container fluid className="mt-1">
<Row> <Row>
<Col> <Col>
<h1>Lookup Accounts</h1> <h1>Lookup Accounts</h1>
</Col> </Col>
</Row> </Row>
<Row> <Row>
<Col> <Col>
<LookupAccountForm api={api}/> <LookupAccountForm api={api} />
</Col> </Col>
</Row> </Row>
</Container> </Container>
) );
export default AccountsPage; export default AccountsPage;

View File

@ -1,22 +1,20 @@
import React from 'react'; import React from 'react';
import Row from 'react-bootstrap/Row'; import { Col, Container, Row } from 'react-bootstrap';
import Col from 'react-bootstrap/Col';
import Container from 'react-bootstrap/Container';
import LookupStorageForm from '../components/LookupStorageForm'; import LookupStorageForm from '../components/LookupStorageForm';
const StoragePage = ({api}) => ( const StoragePage = ({ api }) => (
<Container fluid className="mt-1"> <Container fluid className="mt-1">
<Row> <Row>
<Col> <Col>
<h1>Storage</h1> <h1>Storage</h1>
</Col> </Col>
</Row> </Row>
<Row> <Row>
<Col> <Col>
<LookupStorageForm api={api}/> <LookupStorageForm api={api} />
</Col> </Col>
</Row> </Row>
</Container> </Container>
) );
export default StoragePage; export default StoragePage;

View File

@ -1,26 +1,25 @@
import React from 'react'; import React from 'react';
import {Col, Container, Row} from 'react-bootstrap'; import { Col, Container, Row } from 'react-bootstrap';
import LookupStorageTombstonesForm from '../components/LookupStorageTombstonesForm'; import LookupStorageTombstonesForm from '../components/LookupStorageTombstonesForm';
import StorageTombstonesIntegrityChecks from '../components/StorageTombstonesIntegrityChecks'; import StorageTombstonesIntegrityChecks from '../components/StorageTombstonesIntegrityChecks';
const StorageTombstonesPage = ({api}) => ( const StorageTombstonesPage = ({ api }) => (
<Container fluid className="mt-1">
<Row>
<Col>
<h1>Storage Tombstones</h1>
</Col>
</Row>
<Row>
<Col xs={10}>
<LookupStorageTombstonesForm api={api} />
</Col>
<Col xs={2}>
<StorageTombstonesIntegrityChecks api={api} />
</Col>
</Row>
</Container>
);
<Container fluid className="mt-1"> export default StorageTombstonesPage;
<Row>
<Col>
<h1>Storage Tombstones</h1>
</Col>
</Row>
<Row>
<Col xs={10}>
<LookupStorageTombstonesForm api={api}/>
</Col>
<Col xs={2}>
<StorageTombstonesIntegrityChecks api={api}/>
</Col>
</Row>
</Container>
)
export default StorageTombstonesPage;

View File

@ -15,9 +15,7 @@ const isLocalhost = Boolean(
// [::1] is the IPv6 localhost address. // [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' || window.location.hostname === '[::1]' ||
// 127.0.0.0/8 are considered localhost for IPv4. // 127.0.0.0/8 are considered localhost for IPv4.
window.location.hostname.match( window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/)
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
); );
export function register(config) { export function register(config) {
@ -57,7 +55,7 @@ export function register(config) {
function registerValidSW(swUrl, config) { function registerValidSW(swUrl, config) {
navigator.serviceWorker navigator.serviceWorker
.register(swUrl) .register(swUrl)
.then(registration => { .then((registration) => {
registration.onupdatefound = () => { registration.onupdatefound = () => {
const installingWorker = registration.installing; const installingWorker = registration.installing;
if (installingWorker == null) { if (installingWorker == null) {
@ -93,7 +91,7 @@ function registerValidSW(swUrl, config) {
}; };
}; };
}) })
.catch(error => { .catch((error) => {
console.error('Error during service worker registration:', error); console.error('Error during service worker registration:', error);
}); });
} }
@ -101,17 +99,14 @@ function registerValidSW(swUrl, config) {
function checkValidServiceWorker(swUrl, config) { function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page. // Check if the service worker can be found. If it can't reload the page.
fetch(swUrl, { fetch(swUrl, {
headers: { 'Service-Worker': 'script' } headers: { 'Service-Worker': 'script' },
}) })
.then(response => { .then((response) => {
// Ensure service worker exists, and that we really are getting a JS file. // Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type'); const contentType = response.headers.get('content-type');
if ( if (response.status === 404 || (contentType != null && contentType.indexOf('javascript') === -1)) {
response.status === 404 ||
(contentType != null && contentType.indexOf('javascript') === -1)
) {
// No service worker found. Probably a different app. Reload the page. // No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => { navigator.serviceWorker.ready.then((registration) => {
registration.unregister().then(() => { registration.unregister().then(() => {
window.location.reload(); window.location.reload();
}); });
@ -122,15 +117,13 @@ function checkValidServiceWorker(swUrl, config) {
} }
}) })
.catch(() => { .catch(() => {
console.log( console.log('No internet connection found. App is running in offline mode.');
'No internet connection found. App is running in offline mode.'
);
}); });
} }
export function unregister() { export function unregister() {
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => { navigator.serviceWorker.ready.then((registration) => {
registration.unregister(); registration.unregister();
}); });
} }

View File

@ -1,63 +1,63 @@
const axios = require('axios'); const axios = require('axios');
export default class API { export default class API {
constructor(baseURL) { constructor(baseURL) {
this.baseURL = baseURL this.baseURL = baseURL;
} }
endpoint(name) { endpoint(name) {
return this.baseURL + name return this.baseURL + name;
} }
getRemoteDB() { getRemoteDB() {
return axios({ return axios({
url: this.endpoint('/api/v1/remote-db/'), url: this.endpoint('/api/v1/remote-db/'),
method: 'get', method: 'get',
}) });
} }
setRemoteDB(host, port) { setRemoteDB(host, port) {
return axios({ return axios({
url: this.endpoint('/api/v1/remote-db/'), url: this.endpoint('/api/v1/remote-db/'),
method: 'post', method: 'post',
params: { params: {
'host': host, host: host,
'port': port, port: port,
} },
}) });
} }
lookupAccount(id) { lookupAccount(id) {
return axios({ return axios({
url: this.endpoint('/api/v1/accounts/' + id), url: this.endpoint('/api/v1/accounts/' + id),
method: 'get', method: 'get',
}) });
} }
lookupStorage(prefix) { lookupStorage(prefix) {
return axios({ return axios({
url: this.endpoint('/api/v1/storage/'), url: this.endpoint('/api/v1/storage/'),
method: 'get', method: 'get',
params: { params: {
'prefix': prefix, prefix: prefix,
} },
}) });
} }
lookupStorageTombstones(prefix) { lookupStorageTombstones(prefix) {
return axios({ return axios({
url: this.endpoint('/api/v1/storage-tombstones/'), url: this.endpoint('/api/v1/storage-tombstones/'),
method: 'get', method: 'get',
params: { params: {
'prefix': prefix, prefix: prefix,
} },
}) });
} }
storageTombstonesIntegrityChecks() { storageTombstonesIntegrityChecks() {
return axios({ return axios({
url: this.endpoint('/api/v1/storage-tombstones/integrity/'), url: this.endpoint('/api/v1/storage-tombstones/integrity/'),
method: 'get', method: 'get',
}) });
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1 +1,18 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024"><title>logo</title><polygon points="416 617.14 288 672 288 960 512 720 416 617.14" fill="#1ae2e1"/><polygon points="736 480 512 576 608 617.14 736 480" fill="#ffb5d6"/><polygon points="512 720 608 617.14 512 576 416 617.14 512 720" fill="#fbdb88"/><polygon points="608 617.14 736 672 736 960 512 720 608 617.14" fill="#45378e"/><polygon points="288 480 512 576 416 617.14 288 480" fill="#45378e"/><polygon points="512 64 512 320 736 416 512 64" fill="#1ae2e1"/><polygon points="512 64 512 320 288 416 512 64" fill="#ffb5d6"/><polygon points="512 512 512 320 736 416 512 512" fill="#45378e"/><polygon points="512 512 512 320 288 416 512 512" fill="#fbdb88"/><polygon points="512 896 548.95 832 475.05 832 512 896" fill="#fbdb88"/><polygon points="564.98 961 585.95 924.67 544 924.67 564.98 961" fill="#ffb5d6"/><g opacity="0.1"><polygon points="416 617.14 288 960 288 960 512 720 416 617.14"/></g><polygon points="608 617.14 736 960 736 960 512 720 608 617.14" opacity="0.1"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<title>logo</title>
<polygon points="416 617.14 288 672 288 960 512 720 416 617.14" fill="#1ae2e1"/>
<polygon points="736 480 512 576 608 617.14 736 480" fill="#ffb5d6"/>
<polygon points="512 720 608 617.14 512 576 416 617.14 512 720" fill="#fbdb88"/>
<polygon points="608 617.14 736 672 736 960 512 720 608 617.14" fill="#45378e"/>
<polygon points="288 480 512 576 416 617.14 288 480" fill="#45378e"/>
<polygon points="512 64 512 320 736 416 512 64" fill="#1ae2e1"/>
<polygon points="512 64 512 320 288 416 512 64" fill="#ffb5d6"/>
<polygon points="512 512 512 320 736 416 512 512" fill="#45378e"/>
<polygon points="512 512 512 320 288 416 512 512" fill="#fbdb88"/>
<polygon points="512 896 548.95 832 475.05 832 512 896" fill="#fbdb88"/>
<polygon points="564.98 961 585.95 924.67 544 924.67 564.98 961" fill="#ffb5d6"/>
<g opacity="0.1">
<polygon points="416 617.14 288 960 288 960 512 720 416 617.14"/>
</g>
<polygon points="608 617.14 736 960 736 960 512 720 608 617.14" opacity="0.1"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB