From b626f23792d601b26a730b309e4b9565f44dc7e3 Mon Sep 17 00:00:00 2001 From: Romein van Buren Date: Mon, 11 Jul 2022 14:21:57 +0200 Subject: [PATCH] Basic cluster functionality Signed-off-by: Romein van Buren --- dashboard/build.js | 4 +++- dashboard/socket.js | 28 +++++++++++++++------- entities/webservice.js | 13 ++++++++++ gui/dashboard/app.svelte | 51 ++++++++++++++++++++++++++++++++++++++-- index.js | 45 ++++++++++++++++++++--------------- 5 files changed, 110 insertions(+), 31 deletions(-) diff --git a/dashboard/build.js b/dashboard/build.js index 04608ad..cc46d4d 100644 --- a/dashboard/build.js +++ b/dashboard/build.js @@ -9,7 +9,7 @@ const { default: resolve } = require('@rollup/plugin-node-resolve'); const svelte = require('rollup-plugin-svelte'); const { terser } = require('rollup-plugin-terser'); -async function build(server) { +async function build({ server, settings }) { const serverDomain = server.settings.domain || 'localhost'; const serverPort = server.settings.port || 80; const serverBase = `${serverDomain}:${serverPort}`; @@ -49,6 +49,7 @@ async function build(server) { preventAssignment: false, values: { '__SERVER__': serverBase, + '__CLUSTERS__': JSON.stringify(settings.clusters), }, }), ], @@ -62,6 +63,7 @@ async function build(server) { }); return { + map: output[0].map.toUrl(), code: output[0].code, css: cssOutput.css, }; diff --git a/dashboard/socket.js b/dashboard/socket.js index 3a5b64f..8716b45 100644 --- a/dashboard/socket.js +++ b/dashboard/socket.js @@ -10,10 +10,15 @@ async function createDashboardSocket(server) { route: '/statusdashboard/socket', onOpen: async ws => { function sendTime() { - ws.send(JSON.stringify({ - cmd: 'time', - time: new Date().getTime(), - })); + try { + ws.send(JSON.stringify({ + cmd: 'time', + time: new Date().getTime(), + })); + } + catch { + return; + } } sendTime(); @@ -36,13 +41,19 @@ async function createDashboardSocket(server) { mappedServices[s.id] = { name: s.name, lastBeat: lastBeat || {}, + cluster: s.cluster, }; } - ws.send(JSON.stringify({ - cmd: 'data', - data: mappedServices, - })); + try { + ws.send(JSON.stringify({ + cmd: 'data', + data: mappedServices, + })); + } + catch { + return; + } } sendStatuses(); @@ -51,7 +62,6 @@ async function createDashboardSocket(server) { onUpgrade: async () => ({ id: makeId(10) }), onMessage: async (ws, msg) => { msg = JSON.parse(decoder.decode(msg)); - console.log('msg', msg); if (!msg || !msg.command) { return; diff --git a/entities/webservice.js b/entities/webservice.js index 8753ac5..a805761 100644 --- a/entities/webservice.js +++ b/entities/webservice.js @@ -24,6 +24,7 @@ module.exports = { 'name', 'state', 'public', + 'cluster', 'tags', 'channels', ], @@ -95,6 +96,18 @@ module.exports = { ], }, + cluster: { + label: 'cluster', + fields: [ + { key: 'cluster', + editor: 'select', + placeholder: 'select a cluster for this service...', + visible: !!(settings.clusters && Object.keys(settings.clusters).length), + options: settings.clusters, + }, + ], + }, + tags: { label: 'tags', fields: [ diff --git a/gui/dashboard/app.svelte b/gui/dashboard/app.svelte index 6725b2a..68bc3aa 100644 --- a/gui/dashboard/app.svelte +++ b/gui/dashboard/app.svelte @@ -10,7 +10,13 @@ let servicesUp = {}; let servicesDown = {}; let servicesUnknown = {}; + let servicesInCluster = {}; + let servicesInClusterUnknown = {}; + const clusters = JSON.parse('__CLUSTERS__'); + const clusterKeys = clusters ? Object.keys(clusters) : false; + let currentClusterIndex = -1; let loading = true; + let lock = false; onMount(() => { const ws = new WebSocket('ws://__SERVER__/statusdashboard/socket'); @@ -27,6 +33,8 @@ break; case 'data': + while (lock) {} + services = data.data; const ids = Object.keys(services); @@ -60,6 +68,39 @@ } } }); + + $: if (clusterKeys?.length && !loading) { + function nextCluster() { + lock = true; + currentClusterIndex++; + + if (currentClusterIndex >= clusterKeys.length) { + currentClusterIndex = 0; + } + + const inClusterTemp = {}; + const inClusterTempUnknown = {}; + const currentClusterKey = clusterKeys[currentClusterIndex]; + + for (const [ id, s ] of Object.entries(services)) { + if (s.cluster === currentClusterKey) { + if (!s.lastBeat || !s.lastBeat.date) { + inClusterTempUnknown[id] = s; + } + else { + inClusterTemp[id] = s; + } + } + } + + servicesInCluster = inClusterTemp; + servicesInClusterUnknown = inClusterTempUnknown; + lock = false; + } + + nextCluster(); + setInterval(() => nextCluster, 10_000); + } @@ -71,8 +112,14 @@ {#if !loading} - - + + {#if !clusterKeys?.length} + + + {:else} + + + {/if} {:else} loading {/if} diff --git a/index.js b/index.js index b9f4c0c..dfaa8d0 100644 --- a/index.js +++ b/index.js @@ -85,17 +85,11 @@ module.exports = { }, settings: { - emailSender: { - type: 'string', - label: 'notification sender', - description: 'Sender of notifications about service statuses. Format: Name ', - default: '', - }, - emailRecipient: { - type: 'array', - label: 'notification recipients', - description: 'Recipients of notifications about service statuses. Format: Name ', - default: [], + clusters: { + type: 'keys', + label: 'clusters', + description: 'Clusters can be used to catogorise web services into groups.', + default: {}, }, serviceTags: { type: 'keys', @@ -109,10 +103,22 @@ module.exports = { description: 'Tags that can be assigned to outage messages to categorise them.', default: {}, }, + emailSender: { + type: 'string', + label: 'notification sender', + description: 'Sender of notifications about service statuses. Format: Name ', + default: '', + }, + emailRecipient: { + type: 'array', + label: 'notification recipients', + description: 'Recipients of notifications about service statuses. Format: Name ', + default: [], + }, draftOutageEntries: { type: 'boolean', label: 'draft outage entries', - description: 'Automatically draft outage entry when a service is down.', + description: 'Automatically draft an outage entry when a service is down?', default: true, }, }, @@ -200,10 +206,8 @@ module.exports = { { id: 'startDashboardSocket', event: 'boot', order: 100, - purpose: 'Start the websocket for the dashboard after server has booted', - handler: async ({ server }) => { - createDashboardSocket(server); - }, + purpose: 'Start the websocket for the dashboard after boot', + handler: () => createDashboardSocket(server), }, { id: 'autotestOnSave', @@ -232,7 +236,7 @@ module.exports = { }, ], - routes: ({ server }) => [ + routes: ({ server, settings }) => [ // Get all services { route: '/webservices', @@ -533,7 +537,7 @@ module.exports = { handler: async (req, res) => { try { if (!renderedDashboard) { - renderedDashboard = await buildDashboard(server); + renderedDashboard = await buildDashboard({ server, settings }); renderedDashboard.globalCss = await readFile( __dirname + '/gui/dashboard/app.css' ); @@ -550,7 +554,10 @@ module.exports = { - + `);