Basic single node UI
This commit is contained in:
parent
f3854a4826
commit
96859b0f54
41
cli/auth.go
Normal file
41
cli/auth.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gopkg.in/urfave/cli.v2"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-lotus/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
var authCmd = &cli.Command{
|
||||||
|
Name: "auth",
|
||||||
|
Usage: "Manage RPC permissions",
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
authCreateAdminToken,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var authCreateAdminToken = &cli.Command{
|
||||||
|
Name: "create-admin-token",
|
||||||
|
Usage: "Create admin token",
|
||||||
|
Action: func(cctx *cli.Context) error {
|
||||||
|
napi, err := GetFullNodeAPI(cctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ctx := ReqContext(cctx)
|
||||||
|
|
||||||
|
// TODO: Probably tell the user how powerful this token is
|
||||||
|
|
||||||
|
token, err := napi.AuthNew(ctx, api.AllPermissions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Log in audit log when it is implemented
|
||||||
|
|
||||||
|
fmt.Println(string(token))
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
@ -110,6 +110,7 @@ func ReqContext(cctx *cli.Context) context.Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var Commands = []*cli.Command{
|
var Commands = []*cli.Command{
|
||||||
|
authCmd,
|
||||||
chainCmd,
|
chainCmd,
|
||||||
clientCmd,
|
clientCmd,
|
||||||
createMinerCmd,
|
createMinerCmd,
|
||||||
|
102
lotuspond/front/package-lock.json
generated
102
lotuspond/front/package-lock.json
generated
@ -5816,6 +5816,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
|
||||||
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE="
|
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE="
|
||||||
},
|
},
|
||||||
|
"gud": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw=="
|
||||||
|
},
|
||||||
"gzip-size": {
|
"gzip-size": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.0.0.tgz",
|
||||||
@ -5957,6 +5962,19 @@
|
|||||||
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
|
||||||
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
|
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
|
||||||
},
|
},
|
||||||
|
"history": {
|
||||||
|
"version": "4.10.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
|
||||||
|
"integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.1.2",
|
||||||
|
"loose-envify": "^1.2.0",
|
||||||
|
"resolve-pathname": "^3.0.0",
|
||||||
|
"tiny-invariant": "^1.0.2",
|
||||||
|
"tiny-warning": "^1.0.0",
|
||||||
|
"value-equal": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"hmac-drbg": {
|
"hmac-drbg": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
||||||
@ -8378,6 +8396,16 @@
|
|||||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
|
||||||
"integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="
|
"integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="
|
||||||
},
|
},
|
||||||
|
"mini-create-react-context": {
|
||||||
|
"version": "0.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz",
|
||||||
|
"integrity": "sha512-2v+OeetEyliMt5VHMXsBhABoJ0/M4RCe7fatd/fBy6SMiKazUSEt3gxxypfnk2SHMkdBYvorHRoQxuGoiwbzAw==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.4.0",
|
||||||
|
"gud": "^1.0.0",
|
||||||
|
"tiny-warning": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"mini-css-extract-plugin": {
|
"mini-css-extract-plugin": {
|
||||||
"version": "0.5.0",
|
"version": "0.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz",
|
||||||
@ -10521,6 +10549,60 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz",
|
||||||
"integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA=="
|
"integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA=="
|
||||||
},
|
},
|
||||||
|
"react-router": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-EM7suCPNKb1NxcTZ2LEOWFtQBQRQXecLxVpdsP4DW4PbbqYWeRiLyV/Tt1SdCrvT2jcyXAXmVTmzvSzrPR63Bg==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.1.2",
|
||||||
|
"history": "^4.9.0",
|
||||||
|
"hoist-non-react-statics": "^3.1.0",
|
||||||
|
"loose-envify": "^1.3.1",
|
||||||
|
"mini-create-react-context": "^0.3.0",
|
||||||
|
"path-to-regexp": "^1.7.0",
|
||||||
|
"prop-types": "^15.6.2",
|
||||||
|
"react-is": "^16.6.0",
|
||||||
|
"tiny-invariant": "^1.0.2",
|
||||||
|
"tiny-warning": "^1.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"hoist-non-react-statics": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==",
|
||||||
|
"requires": {
|
||||||
|
"react-is": "^16.7.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isarray": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
|
||||||
|
},
|
||||||
|
"path-to-regexp": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
|
||||||
|
"integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
|
||||||
|
"requires": {
|
||||||
|
"isarray": "0.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"react-router-dom": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-zaVHSy7NN0G91/Bz9GD4owex5+eop+KvgbxXsP/O+iW1/Ln+BrJ8QiIR5a6xNPtrdTvLkxqlDClx13QO1uB8CA==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.1.2",
|
||||||
|
"history": "^4.9.0",
|
||||||
|
"loose-envify": "^1.3.1",
|
||||||
|
"prop-types": "^15.6.2",
|
||||||
|
"react-router": "5.0.1",
|
||||||
|
"tiny-invariant": "^1.0.2",
|
||||||
|
"tiny-warning": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-scripts": {
|
"react-scripts": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.0.1.tgz",
|
||||||
@ -10887,6 +10969,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
|
||||||
"integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
|
"integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
|
||||||
},
|
},
|
||||||
|
"resolve-pathname": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng=="
|
||||||
|
},
|
||||||
"resolve-url": {
|
"resolve-url": {
|
||||||
"version": "0.2.1",
|
"version": "0.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
|
||||||
@ -12083,6 +12170,16 @@
|
|||||||
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
|
||||||
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
|
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
|
||||||
},
|
},
|
||||||
|
"tiny-invariant": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA=="
|
||||||
|
},
|
||||||
|
"tiny-warning": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
|
||||||
|
},
|
||||||
"tmp": {
|
"tmp": {
|
||||||
"version": "0.0.33",
|
"version": "0.0.33",
|
||||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
||||||
@ -12478,6 +12575,11 @@
|
|||||||
"spdx-expression-parse": "^3.0.0"
|
"spdx-expression-parse": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"value-equal": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
|
||||||
|
},
|
||||||
"varint": {
|
"varint": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/varint/-/varint-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/varint/-/varint-5.0.0.tgz",
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
"react": "^16.8.6",
|
"react": "^16.8.6",
|
||||||
"react-cristal": "^0.0.12",
|
"react-cristal": "^0.0.12",
|
||||||
"react-dom": "^16.8.6",
|
"react-dom": "^16.8.6",
|
||||||
|
"react-router-dom": "^5.0.1",
|
||||||
"react-scripts": "3.0.1",
|
"react-scripts": "3.0.1",
|
||||||
"rpc-websockets": "^4.5.1",
|
"rpc-websockets": "^4.5.1",
|
||||||
"styled-components": "^3.3.3",
|
"styled-components": "^3.3.3",
|
||||||
|
@ -1,55 +1,94 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
import { Client } from 'rpc-websockets'
|
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
|
||||||
import NodeList from "./NodeList";
|
import Pond from "./Pond";
|
||||||
|
import SingleNode from "./SingleNode";
|
||||||
|
|
||||||
|
class Index extends React.Component {
|
||||||
class App extends React.Component {
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
const client = new Client('ws://127.0.0.1:2222/rpc/v0')
|
this.state = {rpcUrl: "ws://127.0.0.1:1234/rpc/v0", rpcToken: ''}
|
||||||
client.on('open', () => {
|
|
||||||
this.setState(() => ({client: client}))
|
|
||||||
})
|
|
||||||
|
|
||||||
this.state = {
|
const initialState = JSON.parse(window.localStorage.getItem('saved-nodes'))
|
||||||
windows: {},
|
if (initialState) {
|
||||||
nextWindow: 0,
|
this.state.nodes = initialState
|
||||||
|
} else {
|
||||||
|
this.state.nodes = []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.mountWindow = this.mountWindow.bind(this)
|
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||||
|
window.localStorage.setItem('saved-nodes', JSON.stringify(this.state.nodes))
|
||||||
}
|
}
|
||||||
|
|
||||||
mountWindow(cb) {
|
onAdd = () => {
|
||||||
const id = this.state.nextWindow
|
this.setState({addingNode: true})
|
||||||
this.setState({nextWindow: id + 1})
|
}
|
||||||
|
|
||||||
const window = cb(() => {
|
update = (name) => (e) => this.setState({ [name]: e.target.value })
|
||||||
this.setState(prev => ({windows: {...prev.windows, [id]: undefined}}))
|
|
||||||
})
|
|
||||||
|
|
||||||
this.setState(prev => ({windows: {...prev.windows, [id]: window}}))
|
tokenOk = () => {
|
||||||
|
let m = this.state.rpcToken.match(/\.(.+)\./)
|
||||||
|
// TODO: eww
|
||||||
|
if(m && atob(m[1]) === '{"Allow":["read","write","sign","admin"]}') {
|
||||||
|
return (
|
||||||
|
<span>-Token OK-
|
||||||
|
<div>
|
||||||
|
<button onClick={this.addNode}>Add Node</button>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return <span>-Expecting valid admin token-</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
addNode = async () => {
|
||||||
|
this.setState(p => ({nodes: [...p.nodes, {addr: this.state.rpcUrl, token: this.state.rpcToken}], addingNode: true}))
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.state.client === undefined) {
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
Connecting to RPC
|
<div><Link to={"/app/pond"}>Open Pond</Link></div>
|
||||||
</div>
|
<div>----------------</div>
|
||||||
)
|
<div>
|
||||||
|
<div>Managed Nodes:</div>
|
||||||
|
{
|
||||||
|
this.state.nodes.map((node, i) => <div>
|
||||||
|
<span>{i}. {node.addr} <Link to={`/app/node/${i}`}>[OPEN UI]</Link></span>
|
||||||
|
</div>)
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
return (
|
<a hidden={this.state.addingNode} href='#' onClick={this.onAdd}>[Add]</a>
|
||||||
<div className="App">
|
<div hidden={!this.state.addingNode}>
|
||||||
<NodeList client={this.state.client} mountWindow={this.mountWindow}/>
|
<div>---------------</div>
|
||||||
<div>
|
<div>
|
||||||
{Object.keys(this.state.windows).map((w, i) => <div key={i}>{this.state.windows[w]}</div>)}
|
+ RPC:<input value={"ws://127.0.0.1:1234/rpc/v0"} onChange={this.update("rpcUrl")}/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Token (<code>lotus auth create-admin-token</code>): <input onChange={this.update("rpcToken")}/>{this.tokenOk()}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class App extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Router>
|
||||||
|
<Route path="/" exact component={Index} />
|
||||||
|
<Route path="/app/pond/" component={Pond} />
|
||||||
|
<Route path="/app/node/:node" component={SingleNode} />
|
||||||
|
</Router>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default App
|
export default App
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import App from './App';
|
import Pond from './Pond';
|
||||||
|
|
||||||
it('renders without crashing', () => {
|
it('renders without crashing', () => {
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
ReactDOM.render(<App />, div);
|
ReactDOM.render(<Pond />, div);
|
||||||
ReactDOM.unmountComponentAtNode(div);
|
ReactDOM.unmountComponentAtNode(div);
|
||||||
});
|
});
|
||||||
|
@ -70,7 +70,7 @@ class Client extends React.Component {
|
|||||||
let ppb = Math.round(this.state.total / this.state.blocks * 100) / 100
|
let ppb = Math.round(this.state.total / this.state.blocks * 100) / 100
|
||||||
let ppmbb = Math.round(ppb / (this.state.kbs / 1000) * 100) / 100
|
let ppmbb = Math.round(ppb / (this.state.kbs / 1000) * 100) / 100
|
||||||
|
|
||||||
let dealMaker = <div>
|
let dealMaker = <div hidden={!this.props.pondClient}>
|
||||||
<span>Make Deal: </span>
|
<span>Make Deal: </span>
|
||||||
<select><option>t0101</option></select>
|
<select><option>t0101</option></select>
|
||||||
<abbr title="Data length">L:</abbr> <input placeholder="KBs" defaultValue={1} onChange={this.update("kbs")}/>
|
<abbr title="Data length">L:</abbr> <input placeholder="KBs" defaultValue={1} onChange={this.update("kbs")}/>
|
||||||
@ -94,7 +94,7 @@ class Client extends React.Component {
|
|||||||
|
|
||||||
</div>)
|
</div>)
|
||||||
|
|
||||||
return <Cristal title={"Client - Node " + this.props.node.ID}>
|
return <Cristal title={"Client - Node " + this.props.node.ID} onClose={this.props.onClose}>
|
||||||
<div className="Client">
|
<div className="Client">
|
||||||
<div>{dealMaker}</div>
|
<div>{dealMaker}</div>
|
||||||
<div>{deals}</div>
|
<div>{deals}</div>
|
||||||
|
@ -121,7 +121,7 @@ class FullNode extends React.Component {
|
|||||||
miners = this.state.minerList.map((a, k) => <div key={k}><Address miner={true} client={this.props.client} addr={a} mountWindow={this.props.mountWindow}/></div>)
|
miners = this.state.minerList.map((a, k) => <div key={k}><Address miner={true} client={this.props.client} addr={a} mountWindow={this.props.mountWindow}/></div>)
|
||||||
}
|
}
|
||||||
|
|
||||||
let storageMine = <a href="#" onClick={this.startStorageMiner}>[Spawn Storage Miner]</a>
|
let storageMine = <a href="#" onClick={this.startStorageMiner} hidden={!this.props.spawnStorageNode}>[Spawn Storage Miner]</a>
|
||||||
|
|
||||||
let addresses = this.state.addrs.map((addr) => {
|
let addresses = this.state.addrs.map((addr) => {
|
||||||
let line = <Address client={this.props.client} add1k={this.add1k} add10k={true} nonce={true} addr={addr} mountWindow={this.props.mountWindow}/>
|
let line = <Address client={this.props.client} add1k={this.add1k} add10k={true} nonce={true} addr={addr} mountWindow={this.props.mountWindow}/>
|
||||||
@ -167,10 +167,13 @@ class FullNode extends React.Component {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let nodeID = this.props.node.ID ? this.props.node.ID : ''
|
||||||
|
let nodePos = this.props.node.ID ? {x: this.props.node.ID*30, y: this.props.node.ID * 30} : undefined
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Cristal
|
<Cristal
|
||||||
title={"Node " + this.props.node.ID}
|
title={"Node " + nodeID}
|
||||||
initialPosition={{x: this.props.node.ID*30, y: this.props.node.ID * 30}}
|
initialPosition={nodePos}
|
||||||
initialSize={{width: 690, height: 300}}
|
initialSize={{width: 690, height: 300}}
|
||||||
onClose={this.stop} >
|
onClose={this.stop} >
|
||||||
<div className="CristalScroll">
|
<div className="CristalScroll">
|
||||||
|
55
lotuspond/front/src/Pond.js
Normal file
55
lotuspond/front/src/Pond.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import './App.css';
|
||||||
|
import { Client } from 'rpc-websockets'
|
||||||
|
import NodeList from "./NodeList";
|
||||||
|
|
||||||
|
|
||||||
|
class Pond extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
const client = new Client('ws://127.0.0.1:2222/rpc/v0')
|
||||||
|
client.on('open', () => {
|
||||||
|
this.setState(() => ({client: client}))
|
||||||
|
})
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
windows: {},
|
||||||
|
nextWindow: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mountWindow = this.mountWindow.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
mountWindow(cb) {
|
||||||
|
const id = this.state.nextWindow
|
||||||
|
this.setState({nextWindow: id + 1})
|
||||||
|
|
||||||
|
const window = cb(() => {
|
||||||
|
this.setState(prev => ({windows: {...prev.windows, [id]: undefined}}))
|
||||||
|
})
|
||||||
|
|
||||||
|
this.setState(prev => ({windows: {...prev.windows, [id]: window}}))
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.state.client === undefined) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
Connecting to RPC
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="App">
|
||||||
|
<NodeList client={this.state.client} mountWindow={this.mountWindow}/>
|
||||||
|
<div>
|
||||||
|
{Object.keys(this.state.windows).map((w, i) => <div key={i}>{this.state.windows[w]}</div>)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Pond
|
64
lotuspond/front/src/SingleNode.js
Normal file
64
lotuspond/front/src/SingleNode.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import './App.css';
|
||||||
|
import {Client} from "rpc-websockets";
|
||||||
|
import FullNode from "./FullNode";
|
||||||
|
|
||||||
|
class SingleNode extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
const nodes = JSON.parse(window.localStorage.getItem('saved-nodes'))
|
||||||
|
const node = nodes[this.props.match.params.node]
|
||||||
|
|
||||||
|
const client = new Client(`${node.addr}?token=${node.token}`)
|
||||||
|
client.on('open', async () => {
|
||||||
|
this.setState(() => ({client: client}))
|
||||||
|
})
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
windows: {},
|
||||||
|
nextWindow: 0,
|
||||||
|
|
||||||
|
addr: node.addr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mountWindow = (cb) => {
|
||||||
|
const id = this.state.nextWindow
|
||||||
|
this.setState({nextWindow: id + 1})
|
||||||
|
|
||||||
|
const window = cb(() => {
|
||||||
|
this.setState(prev => ({windows: {...prev.windows, [id]: undefined}}))
|
||||||
|
})
|
||||||
|
|
||||||
|
this.setState(prev => ({windows: {...prev.windows, [id]: window}}))
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.state.client === undefined) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
Connecting to Node RPC: <code>{`${this.state.addr}?token=****`}</code>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let node = <FullNode
|
||||||
|
node={{Repo: '/i/dont/exist/fixme', ID: ''}}
|
||||||
|
client={this.state.client}
|
||||||
|
give1k={null}
|
||||||
|
mountWindow={this.mountWindow}
|
||||||
|
/>
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="App">
|
||||||
|
{node}
|
||||||
|
<div>
|
||||||
|
{Object.keys(this.state.windows).map((w, i) => <div key={i}>{this.state.windows[w]}</div>)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SingleNode;
|
Loading…
Reference in New Issue
Block a user