Merged some files

This commit is contained in:
2023-02-23 10:08:41 +01:00
parent a2a11c3bd2
commit bd9444f30f
7 changed files with 383 additions and 423 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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 };

View File

@ -1,6 +1,84 @@
'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 => {
switch (message.command) {

View File

@ -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 };