dashboard: yarn commands, small fixes

This commit is contained in:
Kurkó Mihály 2018-03-07 18:45:52 +02:00
parent dbf5d53d58
commit 45c916790f
12 changed files with 802 additions and 810 deletions

3
.gitignore vendored
View file

@ -42,5 +42,6 @@ profile.cov
/dashboard/assets/node_modules
/dashboard/assets/stats.json
/dashboard/assets/bundle.js
/dashboard/assets/yarn-error.log
/dashboard/assets/package-lock.json
**/yarn-error.log

View file

@ -12,23 +12,20 @@ The client's UI uses [React][React] with JSX syntax, which is validated by the [
As the dashboard depends on certain NPM packages (which are not included in the `go-ethereum` repo), these need to be installed first:
```
$ (cd dashboard/assets && yarn install)
$ (cd dashboard/assets && ./node_modules/.bin/flow-typed install)
$ (cd dashboard/assets && yarn install && yarn flow)
```
Normally the dashboard assets are bundled into Geth via `go-bindata` to avoid external dependencies. Rebuilding Geth after each UI modification however is not feasible from a developer perspective. Instead, we can run `webpack-dev-server` to run a `geth` independent server which automatically rebundles the UI, and uses external assets to make connection with `geth`, which this way does not rely on compiled resources:
Normally the dashboard assets are bundled into Geth via `go-bindata` to avoid external dependencies. Rebuilding Geth after each UI modification however is not feasible from a developer perspective. Instead, we can run `yarn dev` to watch for file system changes and refresh the browser automatically.
```
$ geth --dashboard --vmodule=dashboard=5
$ (cd dashboard/assets && ./node_modules/.bin/webpack-dev-server)
$ (cd dashboard/assets && yarn dev)
```
The configuration of `webpack-dev-server` is in `webpack.config.js`.
To bundle up the final UI into Geth, run `go generate`:
```
$ go generate ./dashboard
$ (cd dashboard && go generate)
```
### Static type checking
@ -43,7 +40,7 @@ For more IDE support install the `linter-eslint` package too, which finds the `.
[Webpack][Webpack] offers handy tools for visualizing the bundle's dependency tree and space usage.
* Generate the bundle's profile running `webpack --profile --json > stats.json`
* Generate the bundle's profile running `yarn stats`
* For the _dependency tree_ go to [Webpack Analyze][WA], and import `stats.json`
* For the _space usage_ go to [Webpack Visualizer][WV], and import `stats.json`

File diff suppressed because one or more lines are too long

View file

@ -25,11 +25,6 @@ import Body from './Body';
import {MENU} from '../common';
import type {Content} from '../types/content';
// gethHost is the host of geth.
const gethHost = 'localhost';
// gethPort is the port of geth.
const gethPort = 8080;
// deepUpdate updates an object corresponding to the given update data, which has
// the shape of the same structure as the original object. updater also has the same
// structure, except that it contains functions where the original data needs to be
@ -82,15 +77,15 @@ const appender = <T>(limit: number, mapper = replacer) => (update: Array<T>, pre
// defaultContent is the initial value of the state content.
const defaultContent: Content = {
general: {
version: null,
commit: null,
},
home: {},
chain: {},
txpool: {},
network: {},
system: {},
logs: {
log: [],
},
footer: {
system: {
activeMemory: [],
virtualMemory: [],
networkIngress: [],
@ -99,9 +94,9 @@ const defaultContent: Content = {
systemCPU: [],
diskRead: [],
diskWrite: [],
version: null,
commit: null,
},
logs: {
log: [],
},
};
@ -109,15 +104,15 @@ const defaultContent: Content = {
//
// TODO (kurkomisi): Define a tricky type which embraces the content and the updaters.
const updaters = {
general: {
version: replacer,
commit: replacer,
},
home: null,
chain: null,
txpool: null,
network: null,
system: null,
logs: {
log: appender(200),
},
footer: {
system: {
activeMemory: appender(200),
virtualMemory: appender(200),
networkIngress: appender(200),
@ -126,9 +121,9 @@ const updaters = {
systemCPU: appender(200),
diskRead: appender(200),
diskWrite: appender(200),
version: replacer,
commit: replacer,
},
logs: {
log: appender(200),
},
};
@ -141,7 +136,7 @@ const styles = {
height: '100%',
zIndex: 1,
overflow: 'hidden',
}
},
};
// themeStyles returns the styles generated from the theme for the component.
@ -183,7 +178,8 @@ class Dashboard extends Component<Props, State> {
// reconnect establishes a websocket connection with the server, listens for incoming messages
// and tries to reconnect on connection loss.
reconnect = () => {
const server = new WebSocket(`${((window.location.protocol === 'https:') ? 'wss://' : 'ws://')}${gethHost}:${gethPort}/api`);
// PROD is defined by webpack.
const server = new WebSocket(`${((window.location.protocol === 'https:') ? 'wss://' : 'ws://')}${PROD ? window.location.host : 'localhost:8080'}/api`);
server.onopen = () => {
this.setState({content: defaultContent, shouldUpdate: {}});
};

View file

@ -26,7 +26,7 @@ import {ResponsiveContainer, AreaChart, Area, Tooltip} from 'recharts';
import ChartRow from './ChartRow';
import CustomTooltip, {bytePlotter, bytePerSecPlotter, percentPlotter, multiplier} from './CustomTooltip';
import {styles as commonStyles} from '../common';
import type {Footer as FooterType} from '../types/content';
import type {General, System} from '../types/content';
const FOOTER_SYNC_ID = 'footerSyncId';
@ -68,14 +68,15 @@ const themeStyles: Object = (theme: Object) => ({
export type Props = {
classes: Object, // injected by withStyles()
theme: Object,
content: FooterType,
general: General,
system: System,
shouldUpdate: Object,
};
// Footer renders the footer of the dashboard.
class Footer extends Component<Props> {
shouldComponentUpdate(nextProps) {
return typeof nextProps.shouldUpdate.footer !== 'undefined';
return typeof nextProps.shouldUpdate.general !== 'undefined' || typeof nextProps.shouldUpdate.system !== 'undefined';
}
// halfHeightChart renders an area chart with half of the height of its parent.
@ -125,7 +126,7 @@ class Footer extends Component<Props> {
};
render() {
const {content} = this.props;
const {general, system} = this.props;
return (
<Grid container className={this.props.classes.footer} direction='row' alignItems='center' style={styles.footer}>
@ -134,38 +135,38 @@ class Footer extends Component<Props> {
{this.doubleChart(
FOOTER_SYNC_ID,
CPU,
{data: content.processCPU, tooltip: percentPlotter('Process load')},
{data: content.systemCPU, tooltip: percentPlotter('System load', multiplier(-1))},
{data: system.processCPU, tooltip: percentPlotter('Process load')},
{data: system.systemCPU, tooltip: percentPlotter('System load', multiplier(-1))},
)}
{this.doubleChart(
FOOTER_SYNC_ID,
MEMORY,
{data: content.activeMemory, tooltip: bytePlotter('Active memory')},
{data: content.virtualMemory, tooltip: bytePlotter('Virtual memory', multiplier(-1))},
{data: system.activeMemory, tooltip: bytePlotter('Active memory')},
{data: system.virtualMemory, tooltip: bytePlotter('Virtual memory', multiplier(-1))},
)}
{this.doubleChart(
FOOTER_SYNC_ID,
DISK,
{data: content.diskRead, tooltip: bytePerSecPlotter('Disk read')},
{data: content.diskWrite, tooltip: bytePerSecPlotter('Disk write', multiplier(-1))},
{data: system.diskRead, tooltip: bytePerSecPlotter('Disk read')},
{data: system.diskWrite, tooltip: bytePerSecPlotter('Disk write', multiplier(-1))},
)}
{this.doubleChart(
FOOTER_SYNC_ID,
TRAFFIC,
{data: content.networkIngress, tooltip: bytePerSecPlotter('Download')},
{data: content.networkEgress, tooltip: bytePerSecPlotter('Upload', multiplier(-1))},
{data: system.networkIngress, tooltip: bytePerSecPlotter('Download')},
{data: system.networkEgress, tooltip: bytePerSecPlotter('Upload', multiplier(-1))},
)}
</ChartRow>
</Grid>
<Grid item >
<Typography type='caption' color='inherit'>
<span style={commonStyles.light}>Geth</span> {content.version}
<span style={commonStyles.light}>Geth</span> {general.version}
</Typography>
{content.commit && (
{general.commit && (
<Typography type='caption' color='inherit'>
<span style={commonStyles.light}>{'Commit '}</span>
<a href={`https://github.com/ethereum/go-ethereum/commit/${content.commit}`} target='_blank' style={{color: 'inherit', textDecoration: 'none'}} >
{content.commit.substring(0, 8)}
<a href={`https://github.com/ethereum/go-ethereum/commit/${general.commit}`} target='_blank' style={{color: 'inherit', textDecoration: 'none'}} >
{general.commit.substring(0, 8)}
</a>
</Typography>
)}

View file

@ -76,7 +76,8 @@ class Main extends Component<Props> {
<div style={styles.wrapper}>
<div className={classes.content} style={styles.content}>{children}</div>
<Footer
content={content.footer}
general={content.general}
system={content.system}
shouldUpdate={shouldUpdate}
/>
</div>

View file

@ -37,5 +37,11 @@
"url-loader": "^0.6.2",
"webpack": "^3.10.0",
"webpack-dev-server": "^2.11.1"
},
"scripts": {
"build": "NODE_ENV=production webpack",
"stats": "webpack --profile --json > stats.json",
"dev": "webpack-dev-server --port 8081",
"flow": "flow-typed install"
}
}

View file

@ -17,13 +17,13 @@
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
export type Content = {
general: General,
home: Home,
chain: Chain,
txpool: TxPool,
network: Network,
system: System,
logs: Logs,
footer: Footer,
};
export type ChartEntries = Array<ChartEntry>;
@ -33,6 +33,11 @@ export type ChartEntry = {
value: number,
};
export type General = {
version: ?string,
commit: ?string,
};
export type Home = {
/* TODO (kurkomisi) */
};
@ -50,14 +55,6 @@ export type Network = {
};
export type System = {
/* TODO (kurkomisi) */
};
export type Logs = {
log: Array<string>,
};
export type Footer = {
activeMemory: ChartEntries,
virtualMemory: ChartEntries,
networkIngress: ChartEntries,
@ -66,7 +63,8 @@ export type Footer = {
systemCPU: ChartEntries,
diskRead: ChartEntries,
diskWrite: ChartEntries,
version: ?string,
commit: ?string,
};
export type Logs = {
log: Array<string>,
};

View file

@ -32,6 +32,9 @@ module.exports = {
mangle: false,
beautify: true,
}),
new webpack.DefinePlugin({
PROD: process.env.NODE_ENV === 'production',
}),
],
module: {
rules: [
@ -71,7 +74,4 @@ module.exports = {
},
],
},
devServer: {
port: 8081,
}
};

View file

@ -16,11 +16,11 @@
package dashboard
//go:generate npm --prefix ./assets install
//go:generate ./assets/node_modules/.bin/webpack --config ./assets/webpack.config.js --context ./assets
//go:generate go-bindata -nometadata -o assets.go -prefix assets -nocompress -pkg dashboard assets/dashboard.html assets/bundle.js
//go:generate yarn --cwd ./assets install
//go:generate yarn --cwd ./assets build
//go:generate go-bindata -nometadata -o assets.go -prefix assets -nocompress -pkg dashboard assets/index.html assets/bundle.js
//go:generate sh -c "sed 's#var _bundleJs#//nolint:misspell\\\n&#' assets.go > assets.go.tmp && mv assets.go.tmp assets.go"
//go:generate sh -c "sed 's#var _dashboardHtml#//nolint:misspell\\\n&#' assets.go > assets.go.tmp && mv assets.go.tmp assets.go"
//go:generate sh -c "sed 's#var _indexHtml#//nolint:misspell\\\n&#' assets.go > assets.go.tmp && mv assets.go.tmp assets.go"
//go:generate gofmt -w -s assets.go
import (
@ -60,7 +60,7 @@ type Dashboard struct {
listener net.Listener
conns map[uint32]*client // Currently live websocket connections
charts *FooterMessage
charts *SystemMessage
commit string
lock sync.RWMutex // Lock protecting the dashboard's internals
@ -82,7 +82,7 @@ func New(config *Config, commit string) (*Dashboard, error) {
conns: make(map[uint32]*client),
config: config,
quit: make(chan chan error),
charts: &FooterMessage{
charts: &SystemMessage{
ActiveMemory: emptyChartEntries(now, activeMemorySampleLimit, config.Refresh),
VirtualMemory: emptyChartEntries(now, virtualMemorySampleLimit, config.Refresh),
NetworkIngress: emptyChartEntries(now, networkIngressSampleLimit, config.Refresh),
@ -178,7 +178,7 @@ func (db *Dashboard) webHandler(w http.ResponseWriter, r *http.Request) {
path := r.URL.String()
if path == "/" {
path = "/dashboard.html"
path = "/index.html"
}
blob, err := Asset(path[1:])
if err != nil {
@ -224,7 +224,11 @@ func (db *Dashboard) apiHandler(conn *websocket.Conn) {
}
// Send the past data.
client.msg <- Message{
Footer: &FooterMessage{
General: &GeneralMessage{
Version: fmt.Sprintf("v%d.%d.%d%s", params.VersionMajor, params.VersionMinor, params.VersionPatch, versionMeta),
Commit: db.commit,
},
System: &SystemMessage{
ActiveMemory: db.charts.ActiveMemory,
VirtualMemory: db.charts.VirtualMemory,
NetworkIngress: db.charts.NetworkIngress,
@ -233,9 +237,6 @@ func (db *Dashboard) apiHandler(conn *websocket.Conn) {
SystemCPU: db.charts.SystemCPU,
DiskRead: db.charts.DiskRead,
DiskWrite: db.charts.DiskWrite,
Version: fmt.Sprintf("v%d.%d.%d%s", params.VersionMajor, params.VersionMinor, params.VersionPatch, versionMeta),
Commit: db.commit,
},
}
// Start tracking the connection and drop at connection loss.
@ -350,7 +351,7 @@ func (db *Dashboard) collectData() {
db.charts.DiskWrite = append(db.charts.DiskRead[1:], diskWrite)
db.sendToAll(&Message{
Footer: &FooterMessage{
System: &SystemMessage{
ActiveMemory: ChartEntries{activeMemory},
VirtualMemory: ChartEntries{virtualMemory},
NetworkIngress: ChartEntries{networkIngress},

View file

@ -19,13 +19,13 @@ package dashboard
import "time"
type Message struct {
General *GeneralMessage `json:"general,omitempty"`
Home *HomeMessage `json:"home,omitempty"`
Chain *ChainMessage `json:"chain,omitempty"`
TxPool *TxPoolMessage `json:"txpool,omitempty"`
Network *NetworkMessage `json:"network,omitempty"`
System *SystemMessage `json:"system,omitempty"`
Logs *LogsMessage `json:"logs,omitempty"`
Footer *FooterMessage `json:"footer,omitempty"`
}
type ChartEntries []*ChartEntry
@ -35,6 +35,11 @@ type ChartEntry struct {
Value float64 `json:"value,omitempty"`
}
type GeneralMessage struct {
Version string `json:"version,omitempty"`
Commit string `json:"commit,omitempty"`
}
type HomeMessage struct {
/* TODO (kurkomisi) */
}
@ -52,14 +57,6 @@ type NetworkMessage struct {
}
type SystemMessage struct {
/* TODO (kurkomisi) */
}
type LogsMessage struct {
Log []string `json:"log,omitempty"`
}
type FooterMessage struct {
ActiveMemory ChartEntries `json:"activeMemory,omitempty"`
VirtualMemory ChartEntries `json:"virtualMemory,omitempty"`
NetworkIngress ChartEntries `json:"networkIngress,omitempty"`
@ -68,7 +65,8 @@ type FooterMessage struct {
SystemCPU ChartEntries `json:"systemCPU,omitempty"`
DiskRead ChartEntries `json:"diskRead,omitempty"`
DiskWrite ChartEntries `json:"diskWrite,omitempty"`
Version string `json:"version,omitempty"`
Commit string `json:"commit,omitempty"`
}
type LogsMessage struct {
Log []string `json:"log,omitempty"`
}