mirror of
https://github.com/smartyellow/status.git
synced 2025-01-18 05:27:58 +00:00
Merged some files
This commit is contained in:
parent
a2a11c3bd2
commit
bd9444f30f
@ -2,7 +2,7 @@ import { get, writable } from 'svelte/store';
|
|||||||
import { quintOut } from 'svelte/easing';
|
import { quintOut } from 'svelte/easing';
|
||||||
import { crossfade } from 'svelte/transition';
|
import { crossfade } from 'svelte/transition';
|
||||||
|
|
||||||
function createSettingsStore() {
|
export const settings = (() => {
|
||||||
const defaults = {
|
const defaults = {
|
||||||
theme: 'dark',
|
theme: 'dark',
|
||||||
cols: 4,
|
cols: 4,
|
||||||
@ -14,10 +14,7 @@ function createSettingsStore() {
|
|||||||
const s = writable(defaults);
|
const s = writable(defaults);
|
||||||
|
|
||||||
function updateStorage(val) {
|
function updateStorage(val) {
|
||||||
window.localStorage.setItem('statusdash', JSON.stringify({
|
window.localStorage.setItem('statusdash', JSON.stringify({ ...defaults, ...val }));
|
||||||
...defaults,
|
|
||||||
...val,
|
|
||||||
}));
|
|
||||||
s.set(val);
|
s.set(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,7 +22,7 @@ function createSettingsStore() {
|
|||||||
let localStorage = {};
|
let localStorage = {};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
localStorage = JSON.parse(localStorageString);
|
localStorage = JSON.parse(localStorageString || '{}');
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
localStorage = {};
|
localStorage = {};
|
||||||
@ -35,12 +32,10 @@ function createSettingsStore() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
subscribe: s.subscribe,
|
subscribe: s.subscribe,
|
||||||
set: val => updateStorage(val),
|
set: val => updateStorage(val || {}),
|
||||||
update: val => updateStorage({ ...get(s), ...val }),
|
update: val => updateStorage({ ...get(s), ...val }),
|
||||||
};
|
};
|
||||||
}
|
})();
|
||||||
|
|
||||||
export const settings = createSettingsStore();
|
|
||||||
|
|
||||||
export const shuffle = crossfade({
|
export const shuffle = crossfade({
|
||||||
fallback(node) {
|
fallback(node) {
|
||||||
|
319
index.js
319
index.js
@ -1,13 +1,18 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const { fork } = require('child_process');
|
const { fork } = require('child_process');
|
||||||
const { processOutage } = require('./lib/processoutage');
|
|
||||||
const buildDashboard = require('./lib/dashboard/build');
|
|
||||||
const createDashboardSocket = require('./lib/dashboard/socket');
|
|
||||||
const { minifyHtml } = require('core/strings');
|
const { minifyHtml } = require('core/strings');
|
||||||
const { readFile } = require('fs/promises');
|
const { readFile } = require('fs/promises');
|
||||||
|
const { makeId } = require('core/makeid');
|
||||||
|
const buildDeps = {
|
||||||
|
rollup: require('rollup').rollup,
|
||||||
|
commonjs: require('@rollup/plugin-commonjs'),
|
||||||
|
css: require('rollup-plugin-css-only'),
|
||||||
|
resolve: require('@rollup/plugin-node-resolve').nodeResolve,
|
||||||
|
svelte: require('rollup-plugin-svelte'),
|
||||||
|
terser: require('rollup-plugin-terser').terser,
|
||||||
|
};
|
||||||
|
|
||||||
const guiCluster = 'web service status';
|
|
||||||
const icons = {
|
const icons = {
|
||||||
server: '<path d="M0 308.58v150.83a57.73 57.73 0 0 0 10.69 33.38H757.3a57.62 57.62 0 0 0 10.7-33.37V308.58a57.73 57.73 0 0 0-10.69-33.38H10.7A57.76 57.76 0 0 0 0 308.58Zm665.6 81.82a12.8 12.8 0 1 1-.01 25.6 12.8 12.8 0 0 1 .01-25.6ZM640 352a12.8 12.8 0 1 1 0 25.61 12.8 12.8 0 0 1 0-25.61Zm-25.6 38.4a12.8 12.8 0 1 1-.01 25.6 12.8 12.8 0 0 1 0-25.6ZM588.8 352a12.8 12.8 0 1 1 0 25.61 12.8 12.8 0 0 1 0-25.61Zm-25.6 38.4a12.8 12.8 0 1 1 0 25.6 12.8 12.8 0 0 1 0-25.6ZM537.6 352a12.8 12.8 0 1 1-.01 25.6 12.8 12.8 0 0 1 .01-25.6ZM512 390.4a12.8 12.8 0 1 1 0 25.6 12.8 12.8 0 0 1 0-25.6ZM486.4 352a12.8 12.8 0 1 1-.01 25.61 12.8 12.8 0 0 1 0-25.61Zm-25.6 38.4a12.8 12.8 0 1 1 0 25.6 12.8 12.8 0 0 1 0-25.6ZM435.2 352a12.8 12.8 0 1 1 0 25.61 12.8 12.8 0 0 1 0-25.61Zm-300.8-25.6A57.67 57.67 0 0 1 192 384a57.67 57.67 0 0 1-57.6 57.6A57.67 57.67 0 0 1 76.8 384a57.67 57.67 0 0 1 57.6-57.6Zm622.91-76.8A57.76 57.76 0 0 0 768 216.22V65.38A59.05 59.05 0 0 0 709.02 6.4H58.98A59.05 59.05 0 0 0 0 65.38V216.2a57.73 57.73 0 0 0 10.69 33.39H757.3Zm-91.7-102.4a12.8 12.8 0 1 1-.02 25.6 12.8 12.8 0 0 1 .01-25.6ZM640 108.8a12.8 12.8 0 1 1 0 25.61 12.8 12.8 0 0 1 0-25.6Zm-25.6 38.4a12.8 12.8 0 1 1-.01 25.6 12.8 12.8 0 0 1 0-25.6Zm-25.6-38.4a12.8 12.8 0 1 1 0 25.61 12.8 12.8 0 0 1 0-25.6Zm-25.6 38.4a12.8 12.8 0 1 1 0 25.6 12.8 12.8 0 0 1 0-25.6Zm-25.6-38.4a12.8 12.8 0 1 1-.01 25.6 12.8 12.8 0 0 1 .01-25.6ZM512 147.2a12.8 12.8 0 1 1 0 25.6 12.8 12.8 0 0 1 0-25.6Zm-25.6-38.4a12.8 12.8 0 1 1-.01 25.61 12.8 12.8 0 0 1 0-25.6Zm-25.6 38.4a12.8 12.8 0 1 1 0 25.6 12.8 12.8 0 0 1 0-25.6Zm-25.6-38.4a12.8 12.8 0 1 1 0 25.61 12.8 12.8 0 0 1 0-25.6ZM134.4 83.2a57.67 57.67 0 0 1 57.6 57.6 57.67 57.67 0 0 1-57.6 57.6 57.67 57.67 0 0 1-57.6-57.6 57.67 57.67 0 0 1 57.6-57.6ZM10.69 518.4A57.76 57.76 0 0 0 0 551.78v150.83c0 32.53 26.46 59 58.98 59H709a59.05 59.05 0 0 0 58.99-59V551.79a57.73 57.73 0 0 0-10.69-33.38Zm123.7 166.4a57.67 57.67 0 0 1-57.59-57.6 57.67 57.67 0 0 1 57.6-57.6 57.67 57.67 0 0 1 57.6 57.6 57.67 57.67 0 0 1-57.6 57.6Zm300.8-64a12.8 12.8 0 1 1 .02-25.6 12.8 12.8 0 0 1-.01 25.6Zm25.61 38.4a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.6Zm25.6-38.4a12.8 12.8 0 1 1 .01-25.6 12.8 12.8 0 0 1-.01 25.6Zm25.6 38.4a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.6Zm25.6-38.4a12.8 12.8 0 1 1 .01-25.6 12.8 12.8 0 0 1 0 25.6Zm25.6 38.4a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.6Zm25.6-38.4a12.8 12.8 0 1 1 0-25.6 12.8 12.8 0 0 1 0 25.6Zm25.6 38.4a12.8 12.8 0 1 1 .01-25.6 12.8 12.8 0 0 1-.01 25.6Zm25.6-38.4a12.8 12.8 0 1 1 0-25.6 12.8 12.8 0 0 1 0 25.6Zm25.6 38.4a12.8 12.8 0 1 1 .01-25.61 12.8 12.8 0 0 1 0 25.6Zm0 0"/>',
|
server: '<path d="M0 308.58v150.83a57.73 57.73 0 0 0 10.69 33.38H757.3a57.62 57.62 0 0 0 10.7-33.37V308.58a57.73 57.73 0 0 0-10.69-33.38H10.7A57.76 57.76 0 0 0 0 308.58Zm665.6 81.82a12.8 12.8 0 1 1-.01 25.6 12.8 12.8 0 0 1 .01-25.6ZM640 352a12.8 12.8 0 1 1 0 25.61 12.8 12.8 0 0 1 0-25.61Zm-25.6 38.4a12.8 12.8 0 1 1-.01 25.6 12.8 12.8 0 0 1 0-25.6ZM588.8 352a12.8 12.8 0 1 1 0 25.61 12.8 12.8 0 0 1 0-25.61Zm-25.6 38.4a12.8 12.8 0 1 1 0 25.6 12.8 12.8 0 0 1 0-25.6ZM537.6 352a12.8 12.8 0 1 1-.01 25.6 12.8 12.8 0 0 1 .01-25.6ZM512 390.4a12.8 12.8 0 1 1 0 25.6 12.8 12.8 0 0 1 0-25.6ZM486.4 352a12.8 12.8 0 1 1-.01 25.61 12.8 12.8 0 0 1 0-25.61Zm-25.6 38.4a12.8 12.8 0 1 1 0 25.6 12.8 12.8 0 0 1 0-25.6ZM435.2 352a12.8 12.8 0 1 1 0 25.61 12.8 12.8 0 0 1 0-25.61Zm-300.8-25.6A57.67 57.67 0 0 1 192 384a57.67 57.67 0 0 1-57.6 57.6A57.67 57.67 0 0 1 76.8 384a57.67 57.67 0 0 1 57.6-57.6Zm622.91-76.8A57.76 57.76 0 0 0 768 216.22V65.38A59.05 59.05 0 0 0 709.02 6.4H58.98A59.05 59.05 0 0 0 0 65.38V216.2a57.73 57.73 0 0 0 10.69 33.39H757.3Zm-91.7-102.4a12.8 12.8 0 1 1-.02 25.6 12.8 12.8 0 0 1 .01-25.6ZM640 108.8a12.8 12.8 0 1 1 0 25.61 12.8 12.8 0 0 1 0-25.6Zm-25.6 38.4a12.8 12.8 0 1 1-.01 25.6 12.8 12.8 0 0 1 0-25.6Zm-25.6-38.4a12.8 12.8 0 1 1 0 25.61 12.8 12.8 0 0 1 0-25.6Zm-25.6 38.4a12.8 12.8 0 1 1 0 25.6 12.8 12.8 0 0 1 0-25.6Zm-25.6-38.4a12.8 12.8 0 1 1-.01 25.6 12.8 12.8 0 0 1 .01-25.6ZM512 147.2a12.8 12.8 0 1 1 0 25.6 12.8 12.8 0 0 1 0-25.6Zm-25.6-38.4a12.8 12.8 0 1 1-.01 25.61 12.8 12.8 0 0 1 0-25.6Zm-25.6 38.4a12.8 12.8 0 1 1 0 25.6 12.8 12.8 0 0 1 0-25.6Zm-25.6-38.4a12.8 12.8 0 1 1 0 25.61 12.8 12.8 0 0 1 0-25.6ZM134.4 83.2a57.67 57.67 0 0 1 57.6 57.6 57.67 57.67 0 0 1-57.6 57.6 57.67 57.67 0 0 1-57.6-57.6 57.67 57.67 0 0 1 57.6-57.6ZM10.69 518.4A57.76 57.76 0 0 0 0 551.78v150.83c0 32.53 26.46 59 58.98 59H709a59.05 59.05 0 0 0 58.99-59V551.79a57.73 57.73 0 0 0-10.69-33.38Zm123.7 166.4a57.67 57.67 0 0 1-57.59-57.6 57.67 57.67 0 0 1 57.6-57.6 57.67 57.67 0 0 1 57.6 57.6 57.67 57.67 0 0 1-57.6 57.6Zm300.8-64a12.8 12.8 0 1 1 .02-25.6 12.8 12.8 0 0 1-.01 25.6Zm25.61 38.4a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.6Zm25.6-38.4a12.8 12.8 0 1 1 .01-25.6 12.8 12.8 0 0 1-.01 25.6Zm25.6 38.4a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.6Zm25.6-38.4a12.8 12.8 0 1 1 .01-25.6 12.8 12.8 0 0 1 0 25.6Zm25.6 38.4a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.6Zm25.6-38.4a12.8 12.8 0 1 1 0-25.6 12.8 12.8 0 0 1 0 25.6Zm25.6 38.4a12.8 12.8 0 1 1 .01-25.6 12.8 12.8 0 0 1-.01 25.6Zm25.6-38.4a12.8 12.8 0 1 1 0-25.6 12.8 12.8 0 0 1 0 25.6Zm25.6 38.4a12.8 12.8 0 1 1 .01-25.61 12.8 12.8 0 0 1 0 25.6Zm0 0"/>',
|
||||||
outage: '<path d="M601.6 435.2c-91.75 0-166.4 74.65-166.4 166.4S509.85 768 601.6 768 768 693.35 768 601.6s-74.65-166.4-166.4-166.4Zm0 307.2c-77.63 0-140.8-63.17-140.8-140.8 0-77.63 63.17-140.8 140.8-140.8 77.63 0 140.8 63.17 140.8 140.8 0 77.63-63.17 140.8-140.8 140.8Zm0 0"/><path d="M664.96 538.25c-5-5-13.1-5-18.1 0L601.6 583.5l-45.26-45.25a12.79 12.79 0 1 0-18.1 18.1l45.26 45.25-45.26 45.25a12.79 12.79 0 0 0 9.05 21.85 12.8 12.8 0 0 0 9.05-3.75l45.26-45.25 45.26 45.25a12.77 12.77 0 0 0 18.1 0c5-5 5-13.1 0-18.1L619.7 601.6l45.26-45.25c5-5 5-13.1 0-18.1ZM709.02 0H58.98A59.05 59.05 0 0 0 0 58.98V209.8a57.76 57.76 0 0 0 10.69 33.4H757.3a57.68 57.68 0 0 0 10.7-33.38V58.98A59.05 59.05 0 0 0 709.02 0ZM134.4 192a57.67 57.67 0 0 1-57.6-57.6 57.67 57.67 0 0 1 57.6-57.6 57.67 57.67 0 0 1 57.6 57.6 57.67 57.67 0 0 1-57.6 57.6Zm300.8-64a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.61Zm25.6 38.4a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.6Zm25.6-38.4a12.8 12.8 0 1 1 .01-25.6 12.8 12.8 0 0 1-.01 25.6Zm25.6 38.4a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.6Zm25.6-38.4a12.8 12.8 0 1 1 .01-25.61 12.8 12.8 0 0 1 0 25.61Zm25.6 38.4a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.6Zm25.6-38.4a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.61Zm25.6 38.4a12.8 12.8 0 1 1 .01-25.6 12.8 12.8 0 0 1-.01 25.6ZM640 128a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.61Zm25.6 38.4a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.6ZM396.8 601.6c0-32.15 7.65-62.5 20.93-89.6H10.69A57.76 57.76 0 0 0 0 545.38V696.2c0 32.54 26.46 59 58.98 59h407.6c-42.67-37.57-69.78-92.41-69.78-153.6Zm-262.4 76.8a57.67 57.67 0 0 1-57.6-57.6 57.67 57.67 0 0 1 57.6-57.6 57.67 57.67 0 0 1 57.6 57.6 57.67 57.67 0 0 1-57.6 57.6ZM10.69 268.8A57.75 57.75 0 0 0 0 302.18V453a57.75 57.75 0 0 0 10.69 33.4h421.7c36.92-54.04 98.98-89.6 169.2-89.6 65.57 0 123.9 31.1 161.4 79.2a56.8 56.8 0 0 0 5-22.98V302.18a57.73 57.73 0 0 0-10.69-33.38Zm123.7 166.4a57.67 57.67 0 0 1-57.59-57.6 57.67 57.67 0 0 1 57.6-57.6 57.67 57.67 0 0 1 57.6 57.6 57.67 57.67 0 0 1-57.6 57.6Zm300.8-64a12.8 12.8 0 1 1 .02-25.61 12.8 12.8 0 0 1-.01 25.6Zm51.2 0a12.8 12.8 0 1 1 .02-25.6 12.8 12.8 0 0 1-.01 25.6Zm51.21 0a12.8 12.8 0 1 1 .01-25.61 12.8 12.8 0 0 1 0 25.6Zm51.2 0a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.6Zm51.2 0a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.6Zm0 0"/>',
|
outage: '<path d="M601.6 435.2c-91.75 0-166.4 74.65-166.4 166.4S509.85 768 601.6 768 768 693.35 768 601.6s-74.65-166.4-166.4-166.4Zm0 307.2c-77.63 0-140.8-63.17-140.8-140.8 0-77.63 63.17-140.8 140.8-140.8 77.63 0 140.8 63.17 140.8 140.8 0 77.63-63.17 140.8-140.8 140.8Zm0 0"/><path d="M664.96 538.25c-5-5-13.1-5-18.1 0L601.6 583.5l-45.26-45.25a12.79 12.79 0 1 0-18.1 18.1l45.26 45.25-45.26 45.25a12.79 12.79 0 0 0 9.05 21.85 12.8 12.8 0 0 0 9.05-3.75l45.26-45.25 45.26 45.25a12.77 12.77 0 0 0 18.1 0c5-5 5-13.1 0-18.1L619.7 601.6l45.26-45.25c5-5 5-13.1 0-18.1ZM709.02 0H58.98A59.05 59.05 0 0 0 0 58.98V209.8a57.76 57.76 0 0 0 10.69 33.4H757.3a57.68 57.68 0 0 0 10.7-33.38V58.98A59.05 59.05 0 0 0 709.02 0ZM134.4 192a57.67 57.67 0 0 1-57.6-57.6 57.67 57.67 0 0 1 57.6-57.6 57.67 57.67 0 0 1 57.6 57.6 57.67 57.67 0 0 1-57.6 57.6Zm300.8-64a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.61Zm25.6 38.4a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.6Zm25.6-38.4a12.8 12.8 0 1 1 .01-25.6 12.8 12.8 0 0 1-.01 25.6Zm25.6 38.4a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.6Zm25.6-38.4a12.8 12.8 0 1 1 .01-25.61 12.8 12.8 0 0 1 0 25.61Zm25.6 38.4a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.6Zm25.6-38.4a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.61Zm25.6 38.4a12.8 12.8 0 1 1 .01-25.6 12.8 12.8 0 0 1-.01 25.6ZM640 128a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.61Zm25.6 38.4a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.6ZM396.8 601.6c0-32.15 7.65-62.5 20.93-89.6H10.69A57.76 57.76 0 0 0 0 545.38V696.2c0 32.54 26.46 59 58.98 59h407.6c-42.67-37.57-69.78-92.41-69.78-153.6Zm-262.4 76.8a57.67 57.67 0 0 1-57.6-57.6 57.67 57.67 0 0 1 57.6-57.6 57.67 57.67 0 0 1 57.6 57.6 57.67 57.67 0 0 1-57.6 57.6ZM10.69 268.8A57.75 57.75 0 0 0 0 302.18V453a57.75 57.75 0 0 0 10.69 33.4h421.7c36.92-54.04 98.98-89.6 169.2-89.6 65.57 0 123.9 31.1 161.4 79.2a56.8 56.8 0 0 0 5-22.98V302.18a57.73 57.73 0 0 0-10.69-33.38Zm123.7 166.4a57.67 57.67 0 0 1-57.59-57.6 57.67 57.67 0 0 1 57.6-57.6 57.67 57.67 0 0 1 57.6 57.6 57.67 57.67 0 0 1-57.6 57.6Zm300.8-64a12.8 12.8 0 1 1 .02-25.61 12.8 12.8 0 0 1-.01 25.6Zm51.2 0a12.8 12.8 0 1 1 .02-25.6 12.8 12.8 0 0 1-.01 25.6Zm51.21 0a12.8 12.8 0 1 1 .01-25.61 12.8 12.8 0 0 1 0 25.6Zm51.2 0a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.6Zm51.2 0a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.6Zm0 0"/>',
|
||||||
@ -16,6 +21,142 @@ const icons = {
|
|||||||
|
|
||||||
let renderedDashboard = null;
|
let renderedDashboard = null;
|
||||||
|
|
||||||
|
async function processOutage({ outage, server, settings, onDateUpdated }) {
|
||||||
|
if (typeof onDateUpdated !== 'function') {
|
||||||
|
onDateUpdated = () => null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [ id, testResult ] of Object.entries(outage)) {
|
||||||
|
// Update check date
|
||||||
|
server.storage.store('smartyellow/webservice').update(
|
||||||
|
{ id },
|
||||||
|
{ $set: { lastChecked: new Date() } }
|
||||||
|
).then(() => onDateUpdated(id));
|
||||||
|
|
||||||
|
// Get service entry
|
||||||
|
const service = await server
|
||||||
|
.storage
|
||||||
|
.store('smartyellow/webservice')
|
||||||
|
.findOne({ id });
|
||||||
|
|
||||||
|
// Get last heartbeat
|
||||||
|
const heartbeat = await server
|
||||||
|
.storage
|
||||||
|
.store('smartyellow/webserviceheartbeat')
|
||||||
|
.find({ webservice: id })
|
||||||
|
.toArray();
|
||||||
|
const lastBeat = heartbeat[heartbeat.length - 1];
|
||||||
|
|
||||||
|
// Encountered an error while checking status
|
||||||
|
if (testResult.error) {
|
||||||
|
server.error('status: error while checking status of ' + id);
|
||||||
|
server.error(testResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Service is down
|
||||||
|
else if (!testResult.serviceUp) {
|
||||||
|
// Don't perform automatic actions if already done
|
||||||
|
if ((lastBeat && lastBeat.down == false) || !lastBeat) {
|
||||||
|
// Insert heartbeat if last one is not valid anymore
|
||||||
|
try {
|
||||||
|
server.storage.store('smartyellow/webserviceheartbeat').insert({
|
||||||
|
id: makeId(10),
|
||||||
|
down: true,
|
||||||
|
webservice: id,
|
||||||
|
testResult,
|
||||||
|
date: new Date(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
server.error('status: could not save web service heartbeat');
|
||||||
|
server.error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send e-mail notification
|
||||||
|
if (server.sendEmail && settings.emailSender && settings.emailRecipient) {
|
||||||
|
try {
|
||||||
|
const date = new Date().toLocaleString('en-GB', {
|
||||||
|
dateStyle: 'full',
|
||||||
|
timeStyle: 'full',
|
||||||
|
timeZone: 'Etc/UTC',
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.sendEmail({
|
||||||
|
sender: settings.emailSender,
|
||||||
|
to: settings.emailRecipient,
|
||||||
|
subject: `[outage] ${service.name} is down`,
|
||||||
|
body: `Hello,
|
||||||
|
|
||||||
|
As of ${date} UTC time, the service "${service.name}" does not meet the requirements for being
|
||||||
|
considered as working.
|
||||||
|
|
||||||
|
Technical information containing the reason for this alert:
|
||||||
|
${JSON.stringify(testResult, null, 2)}
|
||||||
|
|
||||||
|
Please always check this before taking action. This is an automated message.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
server.warn('status: could not send endpoint status notification e-mail');
|
||||||
|
server.warn(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draft outage entry
|
||||||
|
if (settings.draftOutageEntries) {
|
||||||
|
try {
|
||||||
|
server
|
||||||
|
.storage
|
||||||
|
.store('smartyellow/webserviceoutage')
|
||||||
|
.insert({
|
||||||
|
id: makeId(),
|
||||||
|
name: {
|
||||||
|
en: `[automatic] Outage for ${service.name.en}`,
|
||||||
|
},
|
||||||
|
state: 'concept',
|
||||||
|
resolved: false,
|
||||||
|
services: [ service.id ],
|
||||||
|
tags: [ 'automatically created' ],
|
||||||
|
notes: [ {
|
||||||
|
date: new Date(),
|
||||||
|
userId: 'system',
|
||||||
|
text: `Automatically created outage. Reason: ${JSON.stringify(testResult, null, 2)}`,
|
||||||
|
} ],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
server.warn('status: could not automatically draft outage entry');
|
||||||
|
server.warn(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Service up
|
||||||
|
else {
|
||||||
|
// Don't perform automatic actions if already done
|
||||||
|
if ((lastBeat && lastBeat.down == true) || !lastBeat) {
|
||||||
|
// Insert heartbeat if last one is not valid anymore
|
||||||
|
try {
|
||||||
|
await server.storage.store('smartyellow/webserviceheartbeat').insert({
|
||||||
|
id: makeId(10),
|
||||||
|
down: false,
|
||||||
|
webservice: id,
|
||||||
|
testResult,
|
||||||
|
date: new Date(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
server.warn('status: could not save web service heartbeat');
|
||||||
|
server.warn(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
||||||
// Friendly name
|
// Friendly name
|
||||||
@ -133,7 +274,7 @@ module.exports = {
|
|||||||
{ path: 'webservices.svelte',
|
{ path: 'webservices.svelte',
|
||||||
requires: [ 'seeServices', 'editServices' ],
|
requires: [ 'seeServices', 'editServices' ],
|
||||||
menu: {
|
menu: {
|
||||||
cluster: guiCluster,
|
cluster: 'web service status',
|
||||||
icon: icons.server,
|
icon: icons.server,
|
||||||
title: 'web services',
|
title: 'web services',
|
||||||
},
|
},
|
||||||
@ -142,7 +283,7 @@ module.exports = {
|
|||||||
{ path: 'webserviceoutages.svelte',
|
{ path: 'webserviceoutages.svelte',
|
||||||
requires: [ 'seeServices', 'editServices' ],
|
requires: [ 'seeServices', 'editServices' ],
|
||||||
menu: {
|
menu: {
|
||||||
cluster: guiCluster,
|
cluster: 'web service status',
|
||||||
icon: icons.outage,
|
icon: icons.outage,
|
||||||
title: 'outages',
|
title: 'outages',
|
||||||
},
|
},
|
||||||
@ -150,7 +291,7 @@ module.exports = {
|
|||||||
|
|
||||||
{ path: 'webservicedashboard.svelte',
|
{ path: 'webservicedashboard.svelte',
|
||||||
menu: {
|
menu: {
|
||||||
cluster: guiCluster,
|
cluster: 'web service status',
|
||||||
icon: icons.external,
|
icon: icons.external,
|
||||||
title: 'dashboard',
|
title: 'dashboard',
|
||||||
},
|
},
|
||||||
@ -191,6 +332,7 @@ module.exports = {
|
|||||||
|
|
||||||
runtime.on('message', message => {
|
runtime.on('message', message => {
|
||||||
if (message.error) {
|
if (message.error) {
|
||||||
|
server.error('status: runtime error');
|
||||||
server.error(message.error);
|
server.error(message.error);
|
||||||
}
|
}
|
||||||
else if (message.outage) {
|
else if (message.outage) {
|
||||||
@ -206,7 +348,108 @@ module.exports = {
|
|||||||
event: 'boot',
|
event: 'boot',
|
||||||
order: 100,
|
order: 100,
|
||||||
purpose: 'Start the websocket for the dashboard after boot',
|
purpose: 'Start the websocket for the dashboard after boot',
|
||||||
handler: () => createDashboardSocket(server),
|
handler: () => {
|
||||||
|
const decoder = new TextDecoder('utf-8');
|
||||||
|
let downIdsBefore = [];
|
||||||
|
let downIdsAfter = [];
|
||||||
|
|
||||||
|
const mapService = (s, beat) => ({
|
||||||
|
id: s.id,
|
||||||
|
name: s.name,
|
||||||
|
cluster: s.cluster,
|
||||||
|
lastBeat: beat,
|
||||||
|
checked: s.lastChecked,
|
||||||
|
});
|
||||||
|
|
||||||
|
server.ws({
|
||||||
|
route: '/status/dashboard/socket',
|
||||||
|
onOpen: async ws => {
|
||||||
|
async function sendStatuses() {
|
||||||
|
const services = await server.storage
|
||||||
|
.store('smartyellow/webservice')
|
||||||
|
.find({ public: true })
|
||||||
|
.toArray();
|
||||||
|
const heartbeats = await server.storage
|
||||||
|
.store('smartyellow/webserviceheartbeat')
|
||||||
|
.find({ webservice: { $in: services.map(s => s.id) } })
|
||||||
|
.sort({ date: -1 })
|
||||||
|
.toArray();
|
||||||
|
|
||||||
|
const servicesUp = [];
|
||||||
|
const servicesDown = [];
|
||||||
|
const servicesUnknown = [];
|
||||||
|
downIdsAfter = [];
|
||||||
|
|
||||||
|
for (let service of services) {
|
||||||
|
const beat = heartbeats.find(b => b.webservice === service.id);
|
||||||
|
service = mapService(service, beat);
|
||||||
|
|
||||||
|
if (!beat) {
|
||||||
|
servicesUnknown.push(service);
|
||||||
|
}
|
||||||
|
else if (beat.down) {
|
||||||
|
servicesDown.push(service);
|
||||||
|
downIdsAfter.push(service.id);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
servicesUp.push(service);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const total = [
|
||||||
|
...servicesUp,
|
||||||
|
...servicesDown,
|
||||||
|
...servicesUnknown,
|
||||||
|
].length;
|
||||||
|
|
||||||
|
let newOutage = false;
|
||||||
|
for (const id of downIdsAfter) {
|
||||||
|
if (!downIdsBefore.includes(id)) {
|
||||||
|
newOutage = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
downIdsBefore = JSON.parse(JSON.stringify(downIdsAfter));
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (newOutage) {
|
||||||
|
ws.send(JSON.stringify({ cmd: 'bell' }));
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.send(JSON.stringify({
|
||||||
|
cmd: 'data',
|
||||||
|
servicesUp,
|
||||||
|
servicesDown,
|
||||||
|
servicesUnknown,
|
||||||
|
total,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sendStatuses();
|
||||||
|
setInterval(sendStatuses, 5000);
|
||||||
|
},
|
||||||
|
onUpgrade: async () => ({ id: makeId(10) }),
|
||||||
|
onMessage: async (ws, msg) => {
|
||||||
|
msg = JSON.parse(decoder.decode(msg));
|
||||||
|
|
||||||
|
if (!msg || !msg.command) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (msg.command) {
|
||||||
|
case 'data':
|
||||||
|
ws.send('data');
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
{ id: 'autotestOnSave',
|
{ id: 'autotestOnSave',
|
||||||
@ -219,6 +462,7 @@ module.exports = {
|
|||||||
runtime.send({ command: 'testOne', service: item });
|
runtime.send({ command: 'testOne', service: item });
|
||||||
runtime.on('message', message => {
|
runtime.on('message', message => {
|
||||||
if (message.error) {
|
if (message.error) {
|
||||||
|
server.error('status: runtime error');
|
||||||
server.error(message.error);
|
server.error(message.error);
|
||||||
}
|
}
|
||||||
else if (message.outage) {
|
else if (message.outage) {
|
||||||
@ -296,6 +540,7 @@ module.exports = {
|
|||||||
runtime.on('message', async message => {
|
runtime.on('message', async message => {
|
||||||
res.json(message);
|
res.json(message);
|
||||||
if (message.error) {
|
if (message.error) {
|
||||||
|
server.error('status: runtime error');
|
||||||
server.error(message.error);
|
server.error(message.error);
|
||||||
}
|
}
|
||||||
else if (message.outage) {
|
else if (message.outage) {
|
||||||
@ -558,14 +803,52 @@ module.exports = {
|
|||||||
{ route: '/status/dashboard',
|
{ route: '/status/dashboard',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
handler: async (req, res) => {
|
handler: async (req, res) => {
|
||||||
try {
|
if (!renderedDashboard) {
|
||||||
if (!renderedDashboard) {
|
// Build dashboard
|
||||||
renderedDashboard = await buildDashboard();
|
let cssOutput = '';
|
||||||
renderedDashboard.globalCss = await readFile(
|
|
||||||
__dirname + '/gui/dashboard/app.css'
|
try {
|
||||||
);
|
const bundle = await buildDeps.rollup({
|
||||||
|
input: __dirname + '/gui/dashboard/index.js',
|
||||||
|
plugins: [
|
||||||
|
buildDeps.svelte({
|
||||||
|
compilerOptions: {
|
||||||
|
dev: false,
|
||||||
|
generate: 'dom',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
buildDeps.css({ output: style => cssOutput = style }),
|
||||||
|
buildDeps.resolve({
|
||||||
|
browser: true,
|
||||||
|
dedupe: [ 'svelte' ],
|
||||||
|
}),
|
||||||
|
buildDeps.commonjs(),
|
||||||
|
buildDeps.terser(),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const { output } = await bundle.generate({
|
||||||
|
sourcemap: false,
|
||||||
|
format: 'iife',
|
||||||
|
name: 'app',
|
||||||
|
file: 'public/build/bundle.js',
|
||||||
|
});
|
||||||
|
|
||||||
|
renderedDashboard = {
|
||||||
|
map: output[0].map ? output[0].map.toUrl() : '',
|
||||||
|
code: output[0].code,
|
||||||
|
css: cssOutput,
|
||||||
|
globalCss: await readFile(
|
||||||
|
__dirname + '/gui/dashboard/app.css'
|
||||||
|
),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
const dashboardHtml = minifyHtml(`
|
catch (error) {
|
||||||
|
server.error('status: error while building dashboard: ', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const dashboardHtml = minifyHtml(`
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
@ -584,11 +867,7 @@ module.exports = {
|
|||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
`);
|
`);
|
||||||
res.send(dashboardHtml);
|
res.send(dashboardHtml);
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
server.error('could not compile web service status dashboard', error);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const { rollup } = require('rollup');
|
|
||||||
const commonjs = require('@rollup/plugin-commonjs');
|
|
||||||
const css = require('rollup-plugin-css-only');
|
|
||||||
const { default: resolve } = require('@rollup/plugin-node-resolve');
|
|
||||||
const svelte = require('rollup-plugin-svelte');
|
|
||||||
const { terser } = require('rollup-plugin-terser');
|
|
||||||
|
|
||||||
async function build() {
|
|
||||||
let cssOutput = '';
|
|
||||||
|
|
||||||
try {
|
|
||||||
const bundle = await rollup({
|
|
||||||
input: __dirname + '/../../gui/dashboard/index.js',
|
|
||||||
plugins: [
|
|
||||||
// Svelte
|
|
||||||
svelte({
|
|
||||||
compilerOptions: {
|
|
||||||
dev: false,
|
|
||||||
generate: 'dom',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
|
|
||||||
// Extract CSS
|
|
||||||
css({ output: style => cssOutput = style }),
|
|
||||||
|
|
||||||
// Resolve dependencies
|
|
||||||
resolve({
|
|
||||||
browser: true,
|
|
||||||
dedupe: [ 'svelte' ],
|
|
||||||
}),
|
|
||||||
|
|
||||||
// CommonJS functions
|
|
||||||
commonjs(),
|
|
||||||
|
|
||||||
// Minify
|
|
||||||
terser(),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
const { output } = await bundle.generate({
|
|
||||||
sourcemap: false,
|
|
||||||
format: 'iife',
|
|
||||||
name: 'app',
|
|
||||||
file: 'public/build/bundle.js',
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
map: output[0].map ? output[0].map.toUrl() : '',
|
|
||||||
code: output[0].code,
|
|
||||||
css: cssOutput,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.error('Error while building status dashboard: ', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = build;
|
|
@ -1,108 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const { makeId } = require('core/makeid');
|
|
||||||
|
|
||||||
const decoder = new TextDecoder('utf-8');
|
|
||||||
let downIdsBefore = [];
|
|
||||||
let downIdsAfter = [];
|
|
||||||
|
|
||||||
const mapService = (s, beat) => ({
|
|
||||||
id: s.id,
|
|
||||||
name: s.name,
|
|
||||||
cluster: s.cluster,
|
|
||||||
lastBeat: beat,
|
|
||||||
checked: s.lastChecked,
|
|
||||||
});
|
|
||||||
|
|
||||||
async function createDashboardSocket(server) {
|
|
||||||
server.ws({
|
|
||||||
route: '/status/dashboard/socket',
|
|
||||||
onOpen: async ws => {
|
|
||||||
async function sendStatuses() {
|
|
||||||
const services = await server.storage
|
|
||||||
.store('smartyellow/webservice')
|
|
||||||
.find({ public: true })
|
|
||||||
.toArray();
|
|
||||||
const heartbeats = await server.storage
|
|
||||||
.store('smartyellow/webserviceheartbeat')
|
|
||||||
.find({ webservice: { $in: services.map(s => s.id) } })
|
|
||||||
.sort({ date: -1 })
|
|
||||||
.toArray();
|
|
||||||
|
|
||||||
const servicesUp = [];
|
|
||||||
const servicesDown = [];
|
|
||||||
const servicesUnknown = [];
|
|
||||||
downIdsAfter = [];
|
|
||||||
|
|
||||||
for (let service of services) {
|
|
||||||
const beat = heartbeats.find(b => b.webservice === service.id);
|
|
||||||
service = mapService(service, beat);
|
|
||||||
|
|
||||||
if (!beat) {
|
|
||||||
servicesUnknown.push(service);
|
|
||||||
}
|
|
||||||
else if (beat.down) {
|
|
||||||
servicesDown.push(service);
|
|
||||||
downIdsAfter.push(service.id);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
servicesUp.push(service);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const total = [
|
|
||||||
...servicesUp,
|
|
||||||
...servicesDown,
|
|
||||||
...servicesUnknown,
|
|
||||||
].length;
|
|
||||||
|
|
||||||
let newOutage = false;
|
|
||||||
for (const id of downIdsAfter) {
|
|
||||||
if (!downIdsBefore.includes(id)) {
|
|
||||||
newOutage = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
downIdsBefore = JSON.parse(JSON.stringify(downIdsAfter));
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (newOutage) {
|
|
||||||
ws.send(JSON.stringify({ cmd: 'bell' }));
|
|
||||||
}
|
|
||||||
|
|
||||||
ws.send(JSON.stringify({
|
|
||||||
cmd: 'data',
|
|
||||||
servicesUp,
|
|
||||||
servicesDown,
|
|
||||||
servicesUnknown,
|
|
||||||
total,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sendStatuses();
|
|
||||||
setInterval(sendStatuses, 5000);
|
|
||||||
},
|
|
||||||
onUpgrade: async () => ({ id: makeId(10) }),
|
|
||||||
onMessage: async (ws, msg) => {
|
|
||||||
msg = JSON.parse(decoder.decode(msg));
|
|
||||||
|
|
||||||
if (!msg || !msg.command) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (msg.command) {
|
|
||||||
case 'data':
|
|
||||||
ws.send('data');
|
|
||||||
return;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = createDashboardSocket;
|
|
@ -1,141 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const { makeId } = require('core/makeid');
|
|
||||||
|
|
||||||
async function processOutage({ outage, server, settings, onDateUpdated }) {
|
|
||||||
if (typeof onDateUpdated !== 'function') {
|
|
||||||
onDateUpdated = () => null;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [ id, testResult ] of Object.entries(outage)) {
|
|
||||||
// Update check date
|
|
||||||
server.storage.store('smartyellow/webservice').update(
|
|
||||||
{ id },
|
|
||||||
{ $set: { lastChecked: new Date() } }
|
|
||||||
).then(() => onDateUpdated(id));
|
|
||||||
|
|
||||||
// Get service entry
|
|
||||||
const service = await server
|
|
||||||
.storage
|
|
||||||
.store('smartyellow/webservice')
|
|
||||||
.findOne({ id });
|
|
||||||
|
|
||||||
// Get last heartbeat
|
|
||||||
const heartbeat = await server
|
|
||||||
.storage
|
|
||||||
.store('smartyellow/webserviceheartbeat')
|
|
||||||
.find({ webservice: id })
|
|
||||||
.toArray();
|
|
||||||
const lastBeat = heartbeat[heartbeat.length - 1];
|
|
||||||
|
|
||||||
// Encountered an error while checking status
|
|
||||||
if (testResult.error) {
|
|
||||||
server.error('Error while checking status of ' + id);
|
|
||||||
server.error(testResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Service is down
|
|
||||||
else if (!testResult.serviceUp) {
|
|
||||||
// Don't perform automatic actions if already done
|
|
||||||
if ((lastBeat && lastBeat.down == false) || !lastBeat) {
|
|
||||||
// Insert heartbeat if last one is not valid anymore
|
|
||||||
try {
|
|
||||||
server.storage.store('smartyellow/webserviceheartbeat').insert({
|
|
||||||
id: makeId(10),
|
|
||||||
down: true,
|
|
||||||
webservice: id,
|
|
||||||
testResult,
|
|
||||||
date: new Date(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
server.error('could not save web service heartbeat');
|
|
||||||
server.error(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send e-mail notification
|
|
||||||
if (server.sendEmail && settings.emailSender && settings.emailRecipient) {
|
|
||||||
try {
|
|
||||||
const date = new Date().toLocaleString('en-GB', {
|
|
||||||
dateStyle: 'full',
|
|
||||||
timeStyle: 'full',
|
|
||||||
timeZone: 'Etc/UTC',
|
|
||||||
});
|
|
||||||
const a = await server.sendEmail({
|
|
||||||
sender: settings.emailSender,
|
|
||||||
to: settings.emailRecipient,
|
|
||||||
subject: `[outage] ${service.name} is down`,
|
|
||||||
body: `Dear recipient,
|
|
||||||
|
|
||||||
As of ${date} UTC time, the service "${service.name}" does not meet the requirements for being
|
|
||||||
considered as working.
|
|
||||||
|
|
||||||
Technical information containing the reason for this alert:
|
|
||||||
${JSON.stringify(testResult, null, 2)}
|
|
||||||
|
|
||||||
Please always check this before taking action. This is an automated message.`,
|
|
||||||
});
|
|
||||||
console.log(a);
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
server.error('could not send endpoint status notification e-mail');
|
|
||||||
server.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draft outage entry
|
|
||||||
if (settings.draftOutageEntries) {
|
|
||||||
try {
|
|
||||||
server
|
|
||||||
.storage
|
|
||||||
.store('smartyellow/webserviceoutage')
|
|
||||||
.insert({
|
|
||||||
id: makeId(),
|
|
||||||
name: {
|
|
||||||
en: `[automatic] Outage for ${service.name.en}`,
|
|
||||||
},
|
|
||||||
state: 'concept',
|
|
||||||
resolved: false,
|
|
||||||
services: [ service.id ],
|
|
||||||
tags: [ 'automatically created' ],
|
|
||||||
notes: [ {
|
|
||||||
date: new Date(),
|
|
||||||
userId: 'system',
|
|
||||||
text: `Automatically created outage. Reason: ${JSON.stringify(testResult, null, 2)}`,
|
|
||||||
} ],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
server.error('could not automatically draft outage entry');
|
|
||||||
server.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Service up
|
|
||||||
else {
|
|
||||||
// Don't perform automatic actions if already done
|
|
||||||
if ((lastBeat && lastBeat.down == true) || !lastBeat) {
|
|
||||||
// Insert heartbeat if last one is not valid anymore
|
|
||||||
try {
|
|
||||||
await server.storage.store('smartyellow/webserviceheartbeat').insert({
|
|
||||||
id: makeId(10),
|
|
||||||
down: false,
|
|
||||||
webservice: id,
|
|
||||||
testResult,
|
|
||||||
date: new Date(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
server.error('could not save web service heartbeat');
|
|
||||||
server.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { processOutage };
|
|
@ -1,6 +1,84 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const { testEndpoints } = require('./testendpoints');
|
const fetch = require('node-fetch');
|
||||||
|
const { operators } = require('./operators');
|
||||||
|
const { realValues } = require('./realvalues');
|
||||||
|
const http = require('http');
|
||||||
|
const https = require('https');
|
||||||
|
|
||||||
|
// Force requests over IPv4
|
||||||
|
const httpAgent = new http.Agent({ family: 4 });
|
||||||
|
const httpsAgent = new https.Agent({ family: 4 });
|
||||||
|
|
||||||
|
async function testEndpoints(endpoints) {
|
||||||
|
const output = {
|
||||||
|
serviceUp: undefined,
|
||||||
|
success: true,
|
||||||
|
error: false,
|
||||||
|
requirement: undefined,
|
||||||
|
realValue: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const endpoint of endpoints) {
|
||||||
|
try {
|
||||||
|
const headers = endpoint.headers.reduce((obj, item) => {
|
||||||
|
obj[item.name] = item.value;
|
||||||
|
return obj;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const res = await fetch(endpoint.uri, {
|
||||||
|
headers,
|
||||||
|
agent: url => {
|
||||||
|
if (url.protocol === 'http:') {
|
||||||
|
return httpAgent;
|
||||||
|
}
|
||||||
|
return httpsAgent;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const body = await res.text();
|
||||||
|
|
||||||
|
endpoint.requirements.forEach(requirement => {
|
||||||
|
if (output.success === false || output.serviceUp === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Object.keys(operators).includes(requirement.operator)) {
|
||||||
|
output.success = false;
|
||||||
|
output.error = 'unknown operator: ' + requirement.operator;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Object.keys(realValues).includes(requirement.type)) {
|
||||||
|
output.success = false;
|
||||||
|
output.error = 'unknown type: ' + requirement.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
const realValue = realValues[requirement.type]({ res, body });
|
||||||
|
let result = operators[requirement.operator](realValue, requirement.string);
|
||||||
|
|
||||||
|
if (!requirement.truth) {
|
||||||
|
result = !result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
output.serviceUp = false;
|
||||||
|
output.requirement = requirement;
|
||||||
|
output.realValue = realValue;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
output.serviceUp = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
output.success = false;
|
||||||
|
output.serviceUp = false;
|
||||||
|
output.error = err;
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
process.on('message', async message => {
|
process.on('message', async message => {
|
||||||
switch (message.command) {
|
switch (message.command) {
|
||||||
|
@ -1,83 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const fetch = require('node-fetch');
|
|
||||||
const { operators } = require('./operators');
|
|
||||||
const { realValues } = require('./realvalues');
|
|
||||||
const http = require('http');
|
|
||||||
const https = require('https');
|
|
||||||
|
|
||||||
// Force requests over IPv4
|
|
||||||
const httpAgent = new http.Agent({ family: 4 });
|
|
||||||
const httpsAgent = new https.Agent({ family: 4 });
|
|
||||||
|
|
||||||
async function testEndpoints(endpoints) {
|
|
||||||
const output = {
|
|
||||||
serviceUp: undefined,
|
|
||||||
success: true,
|
|
||||||
error: false,
|
|
||||||
requirement: undefined,
|
|
||||||
realValue: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const endpoint of endpoints) {
|
|
||||||
try {
|
|
||||||
const headers = endpoint.headers.reduce((obj, item) => {
|
|
||||||
obj[item.name] = item.value;
|
|
||||||
return obj;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
const res = await fetch(endpoint.uri, {
|
|
||||||
headers,
|
|
||||||
agent: url => {
|
|
||||||
if (url.protocol === 'http:') {
|
|
||||||
return httpAgent;
|
|
||||||
}
|
|
||||||
return httpsAgent;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const body = await res.text();
|
|
||||||
|
|
||||||
endpoint.requirements.forEach(requirement => {
|
|
||||||
if (output.success === false || output.serviceUp === false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Object.keys(operators).includes(requirement.operator)) {
|
|
||||||
output.success = false;
|
|
||||||
output.error = 'unknown operator: ' + requirement.operator;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Object.keys(realValues).includes(requirement.type)) {
|
|
||||||
output.success = false;
|
|
||||||
output.error = 'unknown type: ' + requirement.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
const realValue = realValues[requirement.type]({ res, body });
|
|
||||||
let result = operators[requirement.operator](realValue, requirement.string);
|
|
||||||
|
|
||||||
if (!requirement.truth) {
|
|
||||||
result = !result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result) {
|
|
||||||
output.serviceUp = false;
|
|
||||||
output.requirement = requirement;
|
|
||||||
output.realValue = realValue;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
output.serviceUp = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
output.success = false;
|
|
||||||
output.serviceUp = false;
|
|
||||||
output.error = err;
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { testEndpoints };
|
|
Loading…
Reference in New Issue
Block a user