elstat/priv/frontend/src/components/App.js

154 lines
3.5 KiB
JavaScript

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';
const DOMAIN = 'https://elstatus.stayathomeserver.club';
// const ENDPOINT = 'http://localhost:8069/api/status'
export default class App extends Component {
websocket = null;
reconnectionTime = 1000;
state = {
loading: true,
error: null,
metrics: null,
};
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);
this._send({
op: OP.SUBSCRIBE,
channels,
});
}
handlePacket(packet) {
const { op, c: channel, d: data } = packet;
if (op !== OP.DATA) {
log('ignoring boring packet:', packet);
return;
}
const [, name] = channel.split(':');
log('updating from channel:', channel);
const { metrics } = this.state;
const graph = metrics.graph[name].slice(1);
const newGraph = [data, ...graph];
log('adding data:', data);
this.setState(({ metrics: oldMetrics }, _props) => {
const newMetrics = { ...oldMetrics };
newMetrics.graph[name] = newGraph;
const [, latency] = data;
newMetrics.status[name].latency = latency;
return {
metrics: newMetrics,
};
});
}
connect() {
log('connecting to ws');
const endpoint = (`${DOMAIN}/api/streaming`).replace('https', 'wss');
this.websocket = new WebSocket(endpoint);
this.websocket.onopen = () => {
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;
};
this.websocket.onmessage = (message) => {
const { data } = message;
const parsed = JSON.parse(data);
log('ws recv:', 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() });
}
}
render() {
const metrics = !this.state.metrics ? null : (
<div className="services">
{Object.entries(this.state.metrics.status)
.map(([name, info]) => (
<Service
name={name}
key={name}
graph={this.state.metrics.graph[name]}
{...info}
/>
))
}
</div>
);
return (
<div className="dashboard">
<h1>elstatus</h1>
{this.state.error ? (
<div className="error">
Error: {this.state.error}
</div>
) : null}
{this.state.loading && !this.state.error ? (
<React.Fragment>
<ServicePlaceholder />
<ServicePlaceholder />
<ServicePlaceholder />
</React.Fragment>
) : metrics}
</div>
);
}
}