diff --git a/cli/auth.go b/cli/auth.go new file mode 100644 index 000000000..f4b5ea132 --- /dev/null +++ b/cli/auth.go @@ -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 + }, +} diff --git a/cli/cmd.go b/cli/cmd.go index afeec86b9..631d0d49d 100644 --- a/cli/cmd.go +++ b/cli/cmd.go @@ -110,6 +110,7 @@ func ReqContext(cctx *cli.Context) context.Context { } var Commands = []*cli.Command{ + authCmd, chainCmd, clientCmd, createMinerCmd, diff --git a/lotuspond/front/package-lock.json b/lotuspond/front/package-lock.json index 10b714fb5..907aea84d 100644 --- a/lotuspond/front/package-lock.json +++ b/lotuspond/front/package-lock.json @@ -5816,6 +5816,11 @@ "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", "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": { "version": "5.0.0", "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", "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": { "version": "1.0.1", "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", "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": { "version": "0.5.0", "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", "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": { "version": "3.0.1", "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", "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": { "version": "0.2.1", "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", "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": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -12478,6 +12575,11 @@ "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": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.0.tgz", diff --git a/lotuspond/front/package.json b/lotuspond/front/package.json index e1adde73a..a5f397612 100644 --- a/lotuspond/front/package.json +++ b/lotuspond/front/package.json @@ -11,6 +11,7 @@ "react": "^16.8.6", "react-cristal": "^0.0.12", "react-dom": "^16.8.6", + "react-router-dom": "^5.0.1", "react-scripts": "3.0.1", "rpc-websockets": "^4.5.1", "styled-components": "^3.3.3", diff --git a/lotuspond/front/src/App.js b/lotuspond/front/src/App.js index e2ea90732..0e1dc7195 100644 --- a/lotuspond/front/src/App.js +++ b/lotuspond/front/src/App.js @@ -1,53 +1,92 @@ import React from 'react'; import './App.css'; -import { Client } from 'rpc-websockets' -import NodeList from "./NodeList"; +import { BrowserRouter as Router, Route, Link } from "react-router-dom"; +import Pond from "./Pond"; +import SingleNode from "./SingleNode"; +class Index extends React.Component { + constructor(props) { + super(props) + + this.state = {rpcUrl: "ws://127.0.0.1:1234/rpc/v0", rpcToken: ''} + + const initialState = JSON.parse(window.localStorage.getItem('saved-nodes')) + if (initialState) { + this.state.nodes = initialState + } else { + this.state.nodes = [] + } + } + + componentDidUpdate(prevProps, prevState, snapshot) { + window.localStorage.setItem('saved-nodes', JSON.stringify(this.state.nodes)) + } + + onAdd = () => { + this.setState({addingNode: true}) + } + + update = (name) => (e) => this.setState({ [name]: e.target.value }) + + tokenOk = () => { + let m = this.state.rpcToken.match(/\.(.+)\./) + // TODO: eww + if(m && atob(m[1]) === '{"Allow":["read","write","sign","admin"]}') { + return ( + -Token OK- +
+ +
+
+ ) + } + return -Expecting valid admin token- + } + + addNode = async () => { + this.setState(p => ({nodes: [...p.nodes, {addr: this.state.rpcUrl, token: this.state.rpcToken}], addingNode: true})) + } + + render() { + return ( +
+
Open Pond
+
----------------
+
+
Managed Nodes:
+ { + this.state.nodes.map((node, i) =>
+ {i}. {node.addr} [OPEN UI] +
) + } +
+ + +
+ ) + } +} class App 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 ( -
- Connecting to RPC -
- ) - } - return ( -
- -
- {Object.keys(this.state.windows).map((w, i) =>
{this.state.windows[w]}
)} -
-
+ + + + + ) } } diff --git a/lotuspond/front/src/App.test.js b/lotuspond/front/src/App.test.js index a754b201b..b63d6b53a 100644 --- a/lotuspond/front/src/App.test.js +++ b/lotuspond/front/src/App.test.js @@ -1,9 +1,9 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import App from './App'; +import Pond from './Pond'; it('renders without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(, div); + ReactDOM.render(, div); ReactDOM.unmountComponentAtNode(div); }); diff --git a/lotuspond/front/src/Client.js b/lotuspond/front/src/Client.js index 8a4472ddd..a1c7e2022 100644 --- a/lotuspond/front/src/Client.js +++ b/lotuspond/front/src/Client.js @@ -70,7 +70,7 @@ class Client extends React.Component { let ppb = Math.round(this.state.total / this.state.blocks * 100) / 100 let ppmbb = Math.round(ppb / (this.state.kbs / 1000) * 100) / 100 - let dealMaker =
+ let dealMaker = ) - return + return
{dealMaker}
{deals}
diff --git a/lotuspond/front/src/FullNode.js b/lotuspond/front/src/FullNode.js index 7889e91c3..232c167a5 100644 --- a/lotuspond/front/src/FullNode.js +++ b/lotuspond/front/src/FullNode.js @@ -121,7 +121,7 @@ class FullNode extends React.Component { miners = this.state.minerList.map((a, k) =>
) } - let storageMine = [Spawn Storage Miner] + let storageMine = let addresses = this.state.addrs.map((addr) => { let line =
@@ -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 (
diff --git a/lotuspond/front/src/Pond.js b/lotuspond/front/src/Pond.js new file mode 100644 index 000000000..ae3726540 --- /dev/null +++ b/lotuspond/front/src/Pond.js @@ -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 ( +
+ Connecting to RPC +
+ ) + } + + return ( +
+ +
+ {Object.keys(this.state.windows).map((w, i) =>
{this.state.windows[w]}
)} +
+
+ ) + } +} + +export default Pond diff --git a/lotuspond/front/src/SingleNode.js b/lotuspond/front/src/SingleNode.js new file mode 100644 index 000000000..941d96185 --- /dev/null +++ b/lotuspond/front/src/SingleNode.js @@ -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 ( +
+ Connecting to Node RPC: {`${this.state.addr}?token=****`} +
+ ) + } + + let node = + + return ( +
+ {node} +
+ {Object.keys(this.state.windows).map((w, i) =>
{this.state.windows[w]}
)} +
+
+ ) + } +} + +export default SingleNode;