From 1a64b540466d03867af779ecf168442da3241467 Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Mon, 14 Jun 2021 20:42:02 +0100 Subject: [PATCH] Wrote server functions for checking uptime --- server.js | 14 ++++++++-- services/ping.js | 66 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 services/ping.js diff --git a/server.js b/server.js index 9bdbca41..d1401c47 100644 --- a/server.js +++ b/server.js @@ -9,6 +9,8 @@ const os = require('os'); require('./src/utils/ConfigValidator'); +const pingUrl = require('./services/ping'); + const isDocker = !!process.env.IS_DOCKER; /* Checks env var for port. If undefined, will use Port 80 for Docker, or 4000 for metal */ @@ -64,8 +66,16 @@ const printWelcomeMessage = () => { try { connect() - .use(serveStatic(`${__dirname}/dist`)) - .use(serveStatic(`${__dirname}/public`, { index: 'default.html' })) + .use(serveStatic(`${__dirname}/dist`)) /* Serves up the main built application to the root */ + .use(serveStatic(`${__dirname}/public`, { index: 'default.html' })) /* During build, a custom page will be served */ + .use('/ping', (req, res) => { /* This root returns the status of a given service - used for uptime monitoring */ + try { + pingUrl(req.url, async (results) => { + await res.end(results); + }); + // next(); + } catch (e) { console.warn(`Error running ping check for ${req.url}\n`, e); } + }) .listen(port, () => { try { printWelcomeMessage(); } catch (e) { console.log('Dashy is Starting...'); } }); diff --git a/services/ping.js b/services/ping.js new file mode 100644 index 00000000..43e5caa4 --- /dev/null +++ b/services/ping.js @@ -0,0 +1,66 @@ +/** + * This file contains the Node.js code, used for the optional status check feature + * It accepts a single url parameter, and will make an empty GET request to that + * endpoint, and then resolve the response status code, time taken, and short message + */ +const axios = require('axios').default; + +/* Determines if successful from the HTTP response code */ +const getResponseType = (code) => { + if (Number.isNaN(code)) return false; + const numericCode = parseInt(code, 10); + return (numericCode >= 200 && numericCode <= 302); +}; + +/* Makes human-readable response text for successful check */ +const makeMessageText = (data) => `${data.successStatus ? '✅' : '⚠️'} ` + + `${data.serverName || 'Server'} responded with ` + + `${data.statusCode} - ${data.statusText}. ` + + `\n⏱️Took ${data.timeTaken} ms`; + +/* Makes human-readable response text for failed check */ +const makeErrorMessage = (data) => `❌ Service Unavailable: ${data.hostname || 'Server'} ` + + `resulted in ${data.code || 'a fatal error'} ${data.errno ? `(${data.errno})` : ''}`; + +const makeErrorMessage2 = (data) => `❌ Service Error - ` + + `${data.status} - ${data.statusText}`; + +/* Kicks of a HTTP request, then formats and renders results */ +const makeRequest = (url, render) => { + const startTime = new Date(); + axios.get(url) + .then((response) => { + const statusCode = response.status; + const { statusText } = response; + const successStatus = getResponseType(statusCode); + const serverName = response.request.socket.servername; + const timeTaken = (new Date() - startTime); + const results = { + statusCode, statusText, serverName, successStatus, timeTaken, + }; + const messageText = makeMessageText(results); + results.message = messageText; + return results; + }) + .catch((error) => { + render(JSON.stringify({ + successStatus: false, + message: error.response ? makeErrorMessage2(error.response) : makeErrorMessage(error), + })); + }).then((results) => { + render(JSON.stringify(results)); + }); +}; + +/* Main function, will check if a URL present, and call function */ +module.exports = (params, render) => { + if (!params || !params.includes('=')) { + render(JSON.stringify({ + success: false, + message: '❌ Malformed URL', + })); + } else { + const url = params.split('=')[1]; + makeRequest(url, render); + } +};