mirror of
https://github.com/smartyellow/status.git
synced 2025-01-18 21:47:58 +00:00
Basic dashboard
Signed-off-by: Romein van Buren <romein@vburen.nl>
This commit is contained in:
parent
b3fd94b02a
commit
c561a00954
@ -4,16 +4,20 @@ const { minify: minifyCSS } = require('csso');
|
|||||||
const { rollup } = require('rollup');
|
const { rollup } = require('rollup');
|
||||||
const commonjs = require('@rollup/plugin-commonjs');
|
const commonjs = require('@rollup/plugin-commonjs');
|
||||||
const css = require('rollup-plugin-css-only');
|
const css = require('rollup-plugin-css-only');
|
||||||
|
const replace = require('@rollup/plugin-replace');
|
||||||
const { default: resolve } = require('@rollup/plugin-node-resolve');
|
const { default: resolve } = require('@rollup/plugin-node-resolve');
|
||||||
const svelte = require('rollup-plugin-svelte');
|
const svelte = require('rollup-plugin-svelte');
|
||||||
const { terser } = require('rollup-plugin-terser');
|
const { terser } = require('rollup-plugin-terser');
|
||||||
|
|
||||||
async function build() {
|
async function build(server) {
|
||||||
|
const serverDomain = server.settings.domain || 'localhost';
|
||||||
|
const serverPort = server.settings.port || 80;
|
||||||
|
const serverBase = `${serverDomain}:${serverPort}`;
|
||||||
let cssOutput = '';
|
let cssOutput = '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const bundle = await rollup({
|
const bundle = await rollup({
|
||||||
input: __dirname + '/gui/dashboard/index.js',
|
input: __dirname + '/../gui/dashboard/index.js',
|
||||||
plugins: [
|
plugins: [
|
||||||
// Svelte
|
// Svelte
|
||||||
svelte({
|
svelte({
|
||||||
@ -39,6 +43,14 @@ async function build() {
|
|||||||
|
|
||||||
// Minify
|
// Minify
|
||||||
terser(),
|
terser(),
|
||||||
|
|
||||||
|
// Replace env vars
|
||||||
|
replace({
|
||||||
|
preventAssignment: false,
|
||||||
|
values: {
|
||||||
|
'__SERVER__': serverBase,
|
||||||
|
},
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
72
dashboard/socket.js
Normal file
72
dashboard/socket.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { makeId } = require('core/makeid');
|
||||||
|
|
||||||
|
const decoder = new TextDecoder('utf-8');
|
||||||
|
let uws;
|
||||||
|
|
||||||
|
async function createDashboardSocket(server) {
|
||||||
|
uws = server.ws({
|
||||||
|
route: '/statusdashboard/socket',
|
||||||
|
onOpen: async ws => {
|
||||||
|
function sendTime() {
|
||||||
|
ws.send(JSON.stringify({
|
||||||
|
cmd: 'time',
|
||||||
|
time: new Date().getTime(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
sendTime();
|
||||||
|
setInterval(sendTime, 5000);
|
||||||
|
|
||||||
|
async function sendStatuses() {
|
||||||
|
const services = await server.storage
|
||||||
|
.store('smartyellow/webservice')
|
||||||
|
.find()
|
||||||
|
.toArray();
|
||||||
|
const heartbeats = await server.storage
|
||||||
|
.store('smartyellow/webserviceheartbeat')
|
||||||
|
.find({ webservice: { $in: services.map(s => s.id) } })
|
||||||
|
.sort({ date: -1 })
|
||||||
|
.toArray();
|
||||||
|
const mappedServices = {};
|
||||||
|
|
||||||
|
for (const s of services) {
|
||||||
|
const lastBeat = heartbeats.find(h => h.webservice === s.id);
|
||||||
|
mappedServices[s.id] = {
|
||||||
|
name: s.name,
|
||||||
|
lastBeat: lastBeat,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.send(JSON.stringify({
|
||||||
|
cmd: 'data',
|
||||||
|
data: mappedServices,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
sendStatuses();
|
||||||
|
setInterval(sendStatuses, 5000);
|
||||||
|
},
|
||||||
|
onUpgrade: async () => ({ id: makeId(10) }),
|
||||||
|
onMessage: async (ws, msg) => {
|
||||||
|
msg = JSON.parse(decoder.decode(msg));
|
||||||
|
console.log('msg', msg);
|
||||||
|
|
||||||
|
if (!msg || !msg.command) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (msg.command) {
|
||||||
|
case 'data':
|
||||||
|
ws.send('data');
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = createDashboardSocket;
|
@ -1,19 +1,69 @@
|
|||||||
<script>
|
<script>
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
import TileRawValue from './tile-rawvalue.svelte';
|
||||||
|
|
||||||
onMount(() => console.log('test'));
|
let lastUpdated = new Date();
|
||||||
|
let lastUpdatedFormatted = '';
|
||||||
|
let services = {};
|
||||||
|
let loading = true;
|
||||||
|
|
||||||
|
$:console.log(services);
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
const ws = new WebSocket('ws://__SERVER__/statusdashboard/socket');
|
||||||
|
|
||||||
|
ws.onmessage = async evt => {
|
||||||
|
const data = JSON.parse(evt.data || '""');
|
||||||
|
|
||||||
|
switch (data.cmd) {
|
||||||
|
case 'time':
|
||||||
|
lastUpdated = new Date(data.time);
|
||||||
|
lastUpdatedFormatted = lastUpdated.toLocaleTimeString('en-GB', {
|
||||||
|
timeStyle: 'short',
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'data':
|
||||||
|
services = data.data;
|
||||||
|
loading = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="center">
|
<div class="center">
|
||||||
<div class="ratio">
|
<div class="ratio">
|
||||||
|
<div class="tiles">
|
||||||
|
<TileRawValue title="Last updated" value={lastUpdatedFormatted} />
|
||||||
|
|
||||||
|
{#if !loading}
|
||||||
|
{#each Object.entries(services) as [ id, service ] (id)}
|
||||||
|
{@const isDown = service.lastBeat.down}
|
||||||
|
<TileRawValue
|
||||||
|
title={service.name.en}
|
||||||
|
value={isDown ? 'down' : 'up'}
|
||||||
|
color={isDown ? 'red' : 'green'}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
{:else}
|
||||||
|
loading
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
:global(html), :global(body) {
|
:global(html), :global(body) {
|
||||||
--radius: 10px;
|
|
||||||
--tile-bg: #181818;
|
--tile-bg: #181818;
|
||||||
|
--red: red;
|
||||||
|
--green: green;
|
||||||
|
--radius: 10px;
|
||||||
|
--cols: 4;
|
||||||
|
--rows: 3;
|
||||||
|
|
||||||
background-color: #000;
|
background-color: #000;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
@ -40,4 +90,13 @@
|
|||||||
transform: translate(-50%,-50%);
|
transform: translate(-50%,-50%);
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tiles {
|
||||||
|
margin: 1rem;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(var(--cols), 1fr);
|
||||||
|
grid-template-rows: repeat(var(--rows), 1fr);
|
||||||
|
gap: 1rem;
|
||||||
|
justify-items: stretch;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
29
gui/dashboard/tile-rawvalue.svelte
Normal file
29
gui/dashboard/tile-rawvalue.svelte
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<script>
|
||||||
|
import Tile from './tile.svelte';
|
||||||
|
|
||||||
|
export let title;
|
||||||
|
export let subtitle;
|
||||||
|
export let color;
|
||||||
|
export let value;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Tile {title} {subtitle}>
|
||||||
|
<div class="value {color}">
|
||||||
|
{value}
|
||||||
|
</div>
|
||||||
|
</Tile>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.value {
|
||||||
|
font-size: 3rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.red {
|
||||||
|
color: var(--red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.green {
|
||||||
|
color: var(--green);
|
||||||
|
}
|
||||||
|
</style>
|
@ -4,27 +4,38 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="tile">
|
<div class="tile">
|
||||||
{#if title}
|
{#if title || subtitle}
|
||||||
<div class="title">{title}</div>
|
<div class="desc">
|
||||||
|
{#if title}
|
||||||
|
<div class="title">{title}</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if subtitle}
|
||||||
|
<div class="subtitle">{subtitle}</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if subtitle}
|
<slot />
|
||||||
<div class="subtitle">{subtitle}</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.desc {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.tile {
|
.tile {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
background-color: var(--tile-bg);
|
background-color: var(--tile-bg);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
.title, .subtitle {
|
.title {
|
||||||
font-size: 1.3rem;
|
font-weight: 200;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.subtitle {
|
||||||
font-weight: 600;
|
font-weight: 100;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
51
index.js
51
index.js
@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
const { fork } = require('child_process');
|
const { fork } = require('child_process');
|
||||||
const { processOutage } = require('./lib/processoutage');
|
const { processOutage } = require('./lib/processoutage');
|
||||||
const buildDashboard = require('./builddashboard');
|
const buildDashboard = require('./dashboard/build');
|
||||||
|
const createDashboardSocket = require('./dashboard/socket');
|
||||||
const { minifyHtml } = require('core/strings');
|
const { minifyHtml } = require('core/strings');
|
||||||
|
|
||||||
const guiCluster = 'web service status';
|
const guiCluster = 'web service status';
|
||||||
@ -210,6 +211,15 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
|
|
||||||
hooks: ({ server, settings }) => [
|
hooks: ({ server, settings }) => [
|
||||||
|
{ id: 'startDashboardSocket',
|
||||||
|
event: 'boot',
|
||||||
|
order: 100,
|
||||||
|
purpose: 'Start the websocket for the dashboard after server has booted',
|
||||||
|
handler: async ({ server }) => {
|
||||||
|
createDashboardSocket(server);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
{ id: 'autotestOnSave',
|
{ id: 'autotestOnSave',
|
||||||
order: 500,
|
order: 500,
|
||||||
event: 'saveEntity',
|
event: 'saveEntity',
|
||||||
@ -535,24 +545,29 @@ module.exports = {
|
|||||||
{ route: '/statusdashboard',
|
{ route: '/statusdashboard',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
handler: async (req, res) => {
|
handler: async (req, res) => {
|
||||||
if (!renderedDashboard) {
|
try {
|
||||||
renderedDashboard = await buildDashboard();
|
if (!renderedDashboard) {
|
||||||
|
renderedDashboard = await buildDashboard(server);
|
||||||
|
}
|
||||||
|
const dashboardHtml = minifyHtml(`
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Web service status dashboard</title>
|
||||||
|
<style>${renderedDashboard.css || ''}</style>
|
||||||
|
<script>${renderedDashboard.code || ''}</script>
|
||||||
|
</head>
|
||||||
|
<body></body>
|
||||||
|
</html>
|
||||||
|
`);
|
||||||
|
res.send(dashboardHtml);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
server.error('could not compile web service status dashboard', error);
|
||||||
}
|
}
|
||||||
const dashboardHtml = minifyHtml(`
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Web service status dashboard</title>
|
|
||||||
<style>${renderedDashboard.css || ''}</style>
|
|
||||||
<script>${renderedDashboard.code || ''}</script>
|
|
||||||
</head>
|
|
||||||
<body></body>
|
|
||||||
</html>
|
|
||||||
`);
|
|
||||||
res.send(dashboardHtml);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/smartyellow/status#readme",
|
"homepage": "https://github.com/smartyellow/status#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@rollup/plugin-replace": "^4.0.0",
|
||||||
"csso": "^5.0.3",
|
"csso": "^5.0.3",
|
||||||
"rollup-plugin-css-only": "^3.1.0"
|
"rollup-plugin-css-only": "^3.1.0"
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user