OSDN Git Service

add the peer information page.
authorZhiting Lin <zlin035@uottawa.ca>
Fri, 7 Dec 2018 07:33:07 +0000 (15:33 +0800)
committerZhiting Lin <zlin035@uottawa.ca>
Fri, 7 Dec 2018 07:33:07 +0000 (15:33 +0800)
17 files changed:
src/actions.js
src/features/app/components/Sync/Sync.jsx
src/features/peers/actions.js [new file with mode: 0644]
src/features/peers/components/List.jsx [new file with mode: 0644]
src/features/peers/components/List.scss [new file with mode: 0644]
src/features/peers/components/ListItem.jsx [new file with mode: 0644]
src/features/peers/components/index.js [new file with mode: 0644]
src/features/peers/index.js [new file with mode: 0644]
src/features/peers/reducers.js [new file with mode: 0644]
src/features/peers/routes.js [new file with mode: 0644]
src/features/shared/components/TableList/TableList.jsx
src/locales/en/translation.json
src/locales/zh/translation.json
src/reducers.js
src/routes.js
src/sdk/api/peer.js [new file with mode: 0644]
src/sdk/client.js

index f137741..2abeca7 100644 (file)
@@ -13,6 +13,7 @@ import { actions as transaction } from 'features/transactions'
 import { actions as transactionFeed } from 'features/transactionFeeds'
 import { actions as tutorial } from 'features/tutorial'
 import { actions as unspent } from 'features/unspents'
+import { actions as peer } from 'features/peers'
 
 const actions = {
   accessControl,
@@ -30,6 +31,7 @@ const actions = {
   transactionFeed,
   tutorial,
   unspent,
+  peer
 }
 
 export default actions
index 4262e52..7d28f16 100644 (file)
@@ -1,6 +1,7 @@
 import React from 'react'
 import { connect } from 'react-redux'
 import { ProgressBar, OverlayTrigger, Tooltip } from 'react-bootstrap'
+import { Link } from 'react-router'
 import navStyles from '../Navigation/Navigation.scss'
 import styles from './Sync.scss'
 import {withNamespaces} from 'react-i18next'
@@ -29,7 +30,11 @@ class Sync extends React.Component {
     if (syncing) {
       return <ul className={`${navStyles.navigation} ${styles.main}`}>
         <li key='sync-title' className={navStyles.navigationTitle}>{ networkID } { t('sync.status')}</li>
-        <li key='sync-peer-count' className={(peerCount>0)?styles.blockHightlight: null}>{t('sync.peer')}: {peerCount}</li>
+        <li key='sync-peer-count' className={(peerCount>0)?styles.blockHightlight: null}>
+          <Link to={'/peers'}>
+            {t('sync.peer')}: {peerCount}
+          </Link>
+        </li>
         <li key='sync-status'> <OverlayTrigger placement='top' overlay={tooltip}>
           <div> {t('sync.synchronizing')} {progressInstance} </div>
         </OverlayTrigger></li>
@@ -39,7 +44,11 @@ class Sync extends React.Component {
     const elems = []
 
     elems.push(<li key='sync-title' className={navStyles.navigationTitle}>{ networkID } {t('sync.status') }</li>)
-    elems.push(<li key='sync-peer-count' className={(peerCount>0)?styles.blockHightlight: null}>{t('sync.peer')}: {peerCount}</li>)
+    elems.push(<li key='sync-peer-count' className={(peerCount>0)?styles.blockHightlight: null}>
+        <Link to={'/peers'}>
+         {t('sync.peer')}: {peerCount}
+        </Link>
+      </li>)
 
     if(!syncing && currentBlock == highestBlock){
       elems.push(<li className={styles.blockHightlight} key='sync-done'>
diff --git a/src/features/peers/actions.js b/src/features/peers/actions.js
new file mode 100644 (file)
index 0000000..060e388
--- /dev/null
@@ -0,0 +1,33 @@
+import { baseListActions } from 'features/shared/actions'
+import { chainClient } from 'utility/environment'
+import {push} from 'react-router-redux'
+
+const disconnect = (id) => {
+  return (dispatch) => {
+    return chainClient().peers.disconnect({peer_id: id})
+      .then((resp) => {
+        if(resp.status == 'fail'){
+          dispatch({type: 'ERROR', payload: { 'message': resp.msg}})
+        }else{
+          dispatch(push({
+            pathname: '/peers',
+            state: {
+              preserveFlash: true
+            }
+          }))
+        }
+      })
+      .catch((err) => {
+        if (!err.status) {
+          dispatch({type: 'ERROR', payload: { 'message': err}})
+        }
+      })
+  }
+}
+
+let actions = {
+  disconnect,
+  ...baseListActions('peer')
+}
+
+export default actions
diff --git a/src/features/peers/components/List.jsx b/src/features/peers/components/List.jsx
new file mode 100644 (file)
index 0000000..96f3d66
--- /dev/null
@@ -0,0 +1,32 @@
+import { BaseList, TableList } from 'features/shared/components'
+import ListItem from './ListItem'
+import { withNamespaces } from 'react-i18next'
+import styles from './List.scss'
+import { actions } from 'features/peers'
+
+const type = 'peer'
+
+const mapStateToProps = (state, props) => {
+  return {
+    skipCreate: true,
+    ...BaseList.mapStateToProps(type, ListItem, {
+      wrapperComponent: TableList,
+      wrapperProps: {
+        titles: props.t('peers.formTitle', { returnObjects: true }),
+        styles: styles.main
+      }
+    })(state)
+  }
+}
+
+const mapDispatchToProps = ( dispatch ) => ({
+  itemActions: {
+    disconnect: (id) => dispatch(actions.disconnect(id))
+  },
+  ...BaseList.mapDispatchToProps(type),
+})
+
+export default withNamespaces('translations') (BaseList.connect(
+  mapStateToProps,
+  mapDispatchToProps
+))
diff --git a/src/features/peers/components/List.scss b/src/features/peers/components/List.scss
new file mode 100644 (file)
index 0000000..b80e2b4
--- /dev/null
@@ -0,0 +1,12 @@
+.main {
+  th, td {
+    &:first-child {
+      padding-left: $gutter-size;
+      width: 33%;
+    }
+
+    &:last-child {
+      width: 100px;
+    }
+  }
+}
diff --git a/src/features/peers/components/ListItem.jsx b/src/features/peers/components/ListItem.jsx
new file mode 100644 (file)
index 0000000..177a6b1
--- /dev/null
@@ -0,0 +1,25 @@
+import React from 'react'
+import {withNamespaces} from 'react-i18next'
+
+class ListItem extends React.Component {
+  render() {
+    const {item, t} = this.props
+
+    return(
+      <tr>
+        <td>{item.remoteAddr || '-'}</td>
+        <td><code>{item.height}</code></td>
+        <td><code>{item.ping}</code></td>
+        <td><code>{item.duration}</code></td>
+        <td>{ item.totalSent+ item.totalReceived }</td>
+        <td>
+          <button className='btn btn-link' onClick={() => this.props.disconnect(item.peerId)}>
+            {t('peers.disconnect')}
+          </button>
+        </td>
+      </tr>
+    )
+  }
+}
+
+export default withNamespaces('translations') (ListItem)
diff --git a/src/features/peers/components/index.js b/src/features/peers/components/index.js
new file mode 100644 (file)
index 0000000..ac005ec
--- /dev/null
@@ -0,0 +1,5 @@
+import List from './List'
+
+export {
+  List,
+}
diff --git a/src/features/peers/index.js b/src/features/peers/index.js
new file mode 100644 (file)
index 0000000..bf77b37
--- /dev/null
@@ -0,0 +1,9 @@
+import actions from './actions'
+import reducers from './reducers'
+import routes from './routes'
+
+export {
+  actions,
+  reducers,
+  routes,
+}
diff --git a/src/features/peers/reducers.js b/src/features/peers/reducers.js
new file mode 100644 (file)
index 0000000..70eb19f
--- /dev/null
@@ -0,0 +1,16 @@
+import { reducers } from 'features/shared'
+import { combineReducers } from 'redux'
+
+const type = 'peer'
+
+const itemsReducer = (state = {}, action) => {
+  if (action.type == 'RECEIVED_PEER_ITEMS') {
+    return action.param.data
+  }
+  return state
+}
+
+export default combineReducers({
+  items: itemsReducer,
+  queries: reducers.queriesReducer(type)
+})
diff --git a/src/features/peers/routes.js b/src/features/peers/routes.js
new file mode 100644 (file)
index 0000000..6ae0558
--- /dev/null
@@ -0,0 +1,16 @@
+// import { RoutingContainer } from 'features/shared/components'
+// import { PeerIndex } from './components'
+// import { List } from './components'
+//
+//
+// export default {
+//   path: 'peers',
+//   component: RoutingContainer,
+//   indexRoute: { component: List }
+// }
+//
+
+import { List } from './components'
+import { makeRoutes } from 'features/shared'
+
+export default (store) => makeRoutes(store, 'peer', List)
index 2e44ba0..388b5a9 100644 (file)
@@ -4,7 +4,7 @@ import styles from './TableList.scss'
 class TableList extends React.Component {
   render() {
     return (
-      <table className={styles.main}>
+      <table className={`${styles.main} ${this.props.styles || ''}`}>
         <thead>
           <tr>
             {this.props.titles.map(title => <th key={title}>{title}</th>)}
index 08dea1c..7d082b1 100644 (file)
@@ -54,7 +54,8 @@
     "newKey":"Create a key",
     "newToken":"Create an access token",
     "newAsset":"Create an asset",
-    "resetPassword":"Reset password"
+    "resetPassword":"Reset password",
+    "peer":"Peers information"
   },
   "form":{
     "detail": "Details",
     "selectFile":"Select Restore File",
     "restore":"Restore"
   },
+  "peers":{
+    "formTitle": ["Remote Address", "Height", "Ping", "Duration", "Total Byte"],
+    "disconnect" : "Disconnect"
+  },
   "mnemonic":{
     "backup":"Backup Seed",
     "backupMessage":"Mnemonic used to restore the key related information. Please write down the following seed and save it in a secure location.",
index b595f86..b9bc1fb 100644 (file)
@@ -54,7 +54,8 @@
     "newKey":"新建密钥",
     "newToken":"新建访问令牌",
     "newAsset":"新建资产",
-    "resetPassword":"重置密钥密码"
+    "resetPassword":"重置密钥密码",
+    "peer":"节点信息"
   },
   "form":{
     "detail": "详情",
     "selectFile":"选择备份文件",
     "restore":"恢复"
   },
+  "peers":{
+    "formTitle": ["远程地址", "高度", "Ping", "时长", "总字节"],
+    "disconnect" : "断开"
+  },
   "mnemonic":{
     "backup":"备份助记词",
     "backupMessage":"助记词用于恢复密钥相关的信息。 请将它准确的抄写到纸上,并存放在的安全的地方。",
index 4474dcc..34150c3 100644 (file)
@@ -14,6 +14,7 @@ import { reducers as transaction } from 'features/transactions'
 import { reducers as transactionFeed } from 'features/transactionFeeds'
 import { reducers as tutorial } from 'features/tutorial'
 import { reducers as unspent } from 'features/unspents'
+import { reducers as peer } from 'features/peers'
 import { clear as clearStorage } from 'utility/localStorage'
 
 const makeRootReducer = () => (state, action) => {
@@ -55,6 +56,7 @@ const makeRootReducer = () => (state, action) => {
     initialization,
     key: mockhsm,
     routing,
+    peer,
     testnet,
     transaction,
     transactionFeed,
index 6eb9fba..e6ce7c1 100644 (file)
@@ -12,6 +12,7 @@ import { routes as transactionFeeds } from 'features/transactionFeeds'
 import { routes as unspents } from 'features/unspents'
 import { routes as mockhsm } from 'features/mockhsm'
 import { routes as backup } from 'features/backup'
+import { routes as peers } from 'features/peers'
 
 const makeRoutes = (store) => ({
   path: '/',
@@ -23,6 +24,7 @@ const makeRoutes = (store) => ({
     balances(store),
     configuration,
     core,
+    peers(store),
     initialization,
     transactions(store),
     transactionFeeds(store),
diff --git a/src/sdk/api/peer.js b/src/sdk/api/peer.js
new file mode 100644 (file)
index 0000000..03641e1
--- /dev/null
@@ -0,0 +1,15 @@
+const shared = require('../shared')
+
+const peersAPI = (client) => {
+  return {
+    query: (params, cb) => shared.query(client, 'peers', '/list-peers', params, {cb}),
+
+    queryAll: (params, processor, cb) => shared.queryAll(client, 'peers', params, processor, cb),
+
+    connect: (params, cb) => shared.query(client, 'peers', '/connect-peer', params, {cb}),
+
+    disconnect: (params, cb) => shared.query(client, 'peers', '/disconnect-peer', params, {cb}),
+  }
+}
+
+module.exports = peersAPI
index bb39011..b42876c 100644 (file)
@@ -12,6 +12,7 @@ const mockHsmKeysAPI = require('./api/mockHsmKeys')
 const transactionsAPI = require('./api/transactions')
 const transactionFeedsAPI = require('./api/transactionFeeds')
 const unspentOutputsAPI = require('./api/unspentOutputs')
+const peersAPI = require('./api/peer')
 
 class Client {
   constructor(opts = {}) {
@@ -48,6 +49,8 @@ class Client {
       signerConnection: new Connection(`${opts.url}/mockhsm`, opts.accessToken, opts.agent)
     }
 
+    this.peers = peersAPI(this)
+
     this.transactions = transactionsAPI(this)
 
     this.transactionFeeds = transactionFeedsAPI(this)