relint!
This commit is contained in:
parent
ebe6105fb3
commit
e33f0fa558
|
@ -1,12 +1,12 @@
|
|||
import React, { Component } from 'react';
|
||||
import React, { Component } from 'react'
|
||||
|
||||
import './App.css';
|
||||
import Service from './Service.js';
|
||||
import ServicePlaceholder from './ServicePlaceholder.js';
|
||||
import OP from '../ws/op.js';
|
||||
import { log } from '../util.js';
|
||||
import './App.css'
|
||||
import Service from './Service.js'
|
||||
import ServicePlaceholder from './ServicePlaceholder.js'
|
||||
import OP from '../ws/op.js'
|
||||
import { log } from '../util.js'
|
||||
|
||||
const DOMAIN = 'https://elstatus.stayathomeserver.club';
|
||||
const DOMAIN = 'https://elstatus.stayathomeserver.club'
|
||||
// const ENDPOINT = 'http://localhost:8069/api/status'
|
||||
|
||||
export default class App extends Component {
|
||||
|
@ -20,101 +20,101 @@ export default class App extends Component {
|
|||
metrics: null,
|
||||
};
|
||||
|
||||
async componentDidMount() {
|
||||
await this.loadMetrics();
|
||||
this.connect();
|
||||
async componentDidMount () {
|
||||
await this.loadMetrics()
|
||||
this.connect()
|
||||
}
|
||||
|
||||
subscribeToChannels() {
|
||||
const channels = Object.keys(this.state.metrics.graph).map((channel) => `latency:${channel}`);
|
||||
log('subscribing to channels:', channels);
|
||||
subscribeToChannels () {
|
||||
const channels = Object.keys(this.state.metrics.graph).map((channel) => `latency:${channel}`)
|
||||
log('subscribing to channels:', channels)
|
||||
|
||||
this._send({
|
||||
op: OP.SUBSCRIBE,
|
||||
channels,
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
handlePacket(packet) {
|
||||
const { op, c: channel, d: data } = packet;
|
||||
handlePacket (packet) {
|
||||
const { op, c: channel, d: data } = packet
|
||||
|
||||
if (op !== OP.DATA) {
|
||||
log('ignoring boring packet:', packet);
|
||||
return;
|
||||
log('ignoring boring packet:', packet)
|
||||
return
|
||||
}
|
||||
|
||||
const [, name] = channel.split(':');
|
||||
const [, name] = channel.split(':')
|
||||
|
||||
log('updating from channel:', channel);
|
||||
log('updating from channel:', channel)
|
||||
|
||||
const { metrics } = this.state;
|
||||
const graph = metrics.graph[name].slice(1);
|
||||
const newGraph = [data, ...graph];
|
||||
const { metrics } = this.state
|
||||
const graph = metrics.graph[name].slice(1)
|
||||
const newGraph = [data, ...graph]
|
||||
|
||||
log('adding data:', data);
|
||||
log('adding data:', data)
|
||||
|
||||
this.setState(({ metrics: oldMetrics }, _props) => {
|
||||
const newMetrics = { ...oldMetrics };
|
||||
newMetrics.graph[name] = newGraph;
|
||||
const newMetrics = { ...oldMetrics }
|
||||
newMetrics.graph[name] = newGraph
|
||||
|
||||
const [, latency] = data;
|
||||
newMetrics.status[name].latency = latency;
|
||||
const [, latency] = data
|
||||
newMetrics.status[name].latency = latency
|
||||
|
||||
return {
|
||||
metrics: newMetrics,
|
||||
};
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
connect() {
|
||||
log('connecting to ws');
|
||||
connect () {
|
||||
log('connecting to ws')
|
||||
|
||||
const endpoint = (`${DOMAIN}/api/streaming`).replace('https', 'wss');
|
||||
this.websocket = new WebSocket(endpoint);
|
||||
const endpoint = (`${DOMAIN}/api/streaming`).replace('https', 'wss')
|
||||
this.websocket = new WebSocket(endpoint)
|
||||
|
||||
this.websocket.onopen = () => {
|
||||
log('ws opened');
|
||||
this.subscribeToChannels();
|
||||
};
|
||||
log('ws opened')
|
||||
this.subscribeToChannels()
|
||||
}
|
||||
|
||||
this.websocket.onclose = ({ code, reason }) => {
|
||||
log(`ws closed with code ${code} (reason: ${reason || '<none>'}); `
|
||||
+ `attempting to reconnect in ${this.reconnectionTime}ms`);
|
||||
setTimeout(() => this.connect(), this.reconnectionTime);
|
||||
this.reconnectionTime *= 2;
|
||||
};
|
||||
log(`ws closed with code ${code} (reason: ${reason || '<none>'}); ` +
|
||||
`attempting to reconnect in ${this.reconnectionTime}ms`)
|
||||
setTimeout(() => this.connect(), this.reconnectionTime)
|
||||
this.reconnectionTime *= 2
|
||||
}
|
||||
|
||||
this.websocket.onmessage = (message) => {
|
||||
const { data } = message;
|
||||
const parsed = JSON.parse(data);
|
||||
log('ws recv:', parsed);
|
||||
const { data } = message
|
||||
const parsed = JSON.parse(data)
|
||||
log('ws recv:', parsed)
|
||||
|
||||
this.handlePacket(parsed);
|
||||
};
|
||||
this.handlePacket(parsed)
|
||||
}
|
||||
|
||||
this.websocket.onerror = (event) => {
|
||||
log('ws error:', event);
|
||||
};
|
||||
}
|
||||
|
||||
_send(payload) {
|
||||
this.websocket.send(JSON.stringify(payload));
|
||||
}
|
||||
|
||||
async loadMetrics() {
|
||||
log('loading metrics');
|
||||
|
||||
try {
|
||||
const resp = await fetch(`${DOMAIN}/api/status`);
|
||||
const json = await resp.json();
|
||||
|
||||
this.setState({ metrics: json, loading: false });
|
||||
} catch (err) {
|
||||
this.setState({ error: err.toString() });
|
||||
log('ws error:', event)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
_send (payload) {
|
||||
this.websocket.send(JSON.stringify(payload))
|
||||
}
|
||||
|
||||
async loadMetrics () {
|
||||
log('loading metrics')
|
||||
|
||||
try {
|
||||
const resp = await fetch(`${DOMAIN}/api/status`)
|
||||
const json = await resp.json()
|
||||
|
||||
this.setState({ metrics: json, loading: false })
|
||||
} catch (err) {
|
||||
this.setState({ error: err.toString() })
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const metrics = !this.state.metrics ? null : (
|
||||
<div className="services">
|
||||
{Object.entries(this.state.metrics.status)
|
||||
|
@ -128,7 +128,7 @@ export default class App extends Component {
|
|||
))
|
||||
}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="dashboard">
|
||||
|
@ -148,6 +148,6 @@ export default class App extends Component {
|
|||
</React.Fragment>
|
||||
) : metrics}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import ms from 'ms';
|
||||
import ms from 'ms'
|
||||
import {
|
||||
ResponsiveContainer,
|
||||
AreaChart,
|
||||
|
@ -11,9 +11,9 @@ import {
|
|||
Tooltip,
|
||||
Area,
|
||||
ReferenceLine,
|
||||
} from 'recharts';
|
||||
} from 'recharts'
|
||||
|
||||
import './Graph.css';
|
||||
import './Graph.css'
|
||||
|
||||
export default class Graph extends Component {
|
||||
static propTypes = {
|
||||
|
@ -24,34 +24,47 @@ export default class Graph extends Component {
|
|||
screenWidth: window.innerWidth,
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener('resize', this.handleScreenChange);
|
||||
componentDidMount () {
|
||||
window.addEventListener('resize', this.handleScreenChange)
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('resize', this.handleScreenChange);
|
||||
componentWillUnmount () {
|
||||
window.removeEventListener('resize', this.handleScreenChange)
|
||||
}
|
||||
|
||||
handleScreenChange = () => {
|
||||
this.setState({
|
||||
screenWidth: window.innerWidth,
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
processData() {
|
||||
const { data } = this.props;
|
||||
processData () {
|
||||
const { data } = this.props
|
||||
|
||||
return data.map(([timestamp, latency]) => ({
|
||||
const objects = data.map(([timestamp, latency]) => ({
|
||||
timestamp,
|
||||
latency,
|
||||
})).sort(({ timestamp: a }, { timestamp: b }) => a - b);
|
||||
}))
|
||||
|
||||
// sort so that new entries are first
|
||||
return objects.sort(({ timestamp: a }, { timestamp: b }) => a - b)
|
||||
}
|
||||
|
||||
isSmallScreen() {
|
||||
return this.state.screenWidth < 500;
|
||||
isSmallScreen () {
|
||||
return this.state.screenWidth < 500
|
||||
}
|
||||
|
||||
render() {
|
||||
render () {
|
||||
const yAxis = this.isSmallScreen()
|
||||
? null
|
||||
: (
|
||||
<YAxis
|
||||
dataKey="latency"
|
||||
tickLine={false}
|
||||
tickFormatter={(tick) => `${tick}ms`}
|
||||
/>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="graph-container">
|
||||
<ResponsiveContainer width="100%" height={175}>
|
||||
|
@ -63,14 +76,8 @@ export default class Graph extends Component {
|
|||
tickFormatter={(tick) => ms(Date.now() - tick)}
|
||||
tickLine={false}
|
||||
/>
|
||||
{this.isSmallScreen()
|
||||
? null
|
||||
: <YAxis
|
||||
dataKey="latency"
|
||||
tickLine={false}
|
||||
tickFormatter={(tick) => `${tick}ms`}
|
||||
/>
|
||||
}
|
||||
{yAxis}
|
||||
|
||||
<CartesianGrid strokeDasharray="1 1" />
|
||||
<Tooltip
|
||||
isAnimationActive={false}
|
||||
|
@ -94,6 +101,6 @@ export default class Graph extends Component {
|
|||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
|
||||
import './Service.css';
|
||||
import Graph from './Graph.js';
|
||||
import './Service.css'
|
||||
import Graph from './Graph.js'
|
||||
|
||||
const Service = ({ graph, name, status, latency, description }) => (
|
||||
<div className="service">
|
||||
|
@ -28,12 +28,12 @@ const Service = ({ graph, name, status, latency, description }) => (
|
|||
</p>
|
||||
{graph ? <Graph data={graph} /> : null}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
|
||||
Service.defaultProps = {
|
||||
graph: null,
|
||||
latency: null,
|
||||
};
|
||||
}
|
||||
|
||||
Service.propTypes = {
|
||||
graph: PropTypes.arrayOf(
|
||||
|
@ -43,6 +43,6 @@ Service.propTypes = {
|
|||
status: PropTypes.bool.isRequired,
|
||||
latency: PropTypes.number,
|
||||
description: PropTypes.string.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
export default Service;
|
||||
export default Service
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import React from 'react';
|
||||
import React from 'react'
|
||||
|
||||
import ReactPlaceholder from 'react-placeholder';
|
||||
import 'react-placeholder/lib/reactPlaceholder.css';
|
||||
import ReactPlaceholder from 'react-placeholder'
|
||||
import 'react-placeholder/lib/reactPlaceholder.css'
|
||||
|
||||
import './ServicePlaceholder.css';
|
||||
import './ServicePlaceholder.css'
|
||||
|
||||
const ServicePlaceholder = () => (
|
||||
<div className="service-placeholder">
|
||||
|
@ -44,6 +44,6 @@ const ServicePlaceholder = () => (
|
|||
{' '}
|
||||
</ReactPlaceholder>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
|
||||
export default ServicePlaceholder;
|
||||
export default ServicePlaceholder
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { library } from '@fortawesome/fontawesome-svg-core';
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faCheckCircle,
|
||||
faExclamationCircle,
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
export default function register() {
|
||||
export default function register () {
|
||||
library.add(
|
||||
faCheckCircle,
|
||||
faExclamationCircle,
|
||||
);
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
import './index.css';
|
||||
import App from './components/App';
|
||||
import register from './icons.js';
|
||||
import './index.css'
|
||||
import App from './components/App'
|
||||
import register from './icons.js'
|
||||
|
||||
register();
|
||||
register()
|
||||
|
||||
ReactDOM.render(
|
||||
<App />,
|
||||
document.getElementById('root'),
|
||||
);
|
||||
)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
export function log(...args) {
|
||||
export function log (...args) {
|
||||
console.log(
|
||||
'%c[elstat]%c',
|
||||
'color: purple; font-weight: bold',
|
||||
'color: inherit; font-weight: inherit',
|
||||
...args,
|
||||
);
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
module.exports = {
|
||||
export default {
|
||||
UNSUBSCRIBE: -1,
|
||||
SUBSCRIBE: 0,
|
||||
SUBSCRIBED: 1,
|
||||
UNSUBSCRIBED: 2,
|
||||
DATA: 3,
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue