feature(livereload): rewrite javascript: graceful async socket close

- rewrite javascript livereload script to use object for healthcheck
- aysnc operations shutting down healthcheck and livereload sockets
- handle signals properly
- fix error code reported by signals (node issue)
This commit is contained in:
Asif Bacchus 2021-07-30 17:06:05 -06:00
parent 9717e8a6fa
commit 106fe86cb9

View File

@ -1,55 +1,47 @@
// implement node-livereload over an HTTP or HTTPS connection /*
Implement node-livereload over HTTP or HTTPS connection with integrated
health-check, signal handling and graceful socket shutdown.
*/
function healthcheck() { // load required modules
const express = require('express');
const http = require('http');
const app = express();
const router = express.Router();
router.use((req, res, next) => {
res.header('Access-Control-Allow-Methods', 'GET');
next();
});
router.get('/health', (req, res) => {
res.status(200).send('Ok');
});
app.use('/api/v1', router);
const hServer = http.createServer(app);
hServer.listen(3000);
}
// load modules
const livereload = require('livereload'); const livereload = require('livereload');
const fs = require('fs'); const fs = require('fs');
const http = require('http');
// process from environment variable as array and convert elements to RegEx objects // health check object
const healthCheck = {
app: healthCheckApp(),
server: function () {
return http.createServer(this.app);
},
start: function () {
this.server().listen(3000);
},
stop: function (callback) {
this.server().close(callback());
}
};
// set LiveReload server options
const extraExclusions = process.env.LR_EXCLUDE.split(","); const extraExclusions = process.env.LR_EXCLUDE.split(",");
extraExclusions.forEach((exclusion, idx) => { extraExclusions.forEach((exclusion, idx) => {
extraExclusions[idx] = new RegExp(exclusion); extraExclusions[idx] = new RegExp(exclusion);
}); });
// set createServer options // noinspection SpellCheckingInspection
const options = { const lrOptions = {
port: process.env.LR_PORT, port: process.env.LR_PORT,
exts: process.env.LR_EXTS, exts: process.env.LR_EXTS,
exclusions: extraExclusions, exclusions: extraExclusions,
usePolling: true, usePolling: true,
delay: process.env.LR_DELAY delay: process.env.LR_DELAY
}; };
if (process.env.LR_DEBUG === 'true') {
// set debugging output as per LR_DEBUG lrOptions.debug = true;
if (process.env.LR_DEBUG === "true") {
options.debug = true
console.log("[Debug output ENABLED]"); console.log("[Debug output ENABLED]");
} }
if (process.env.LR_HTTPS === 'true') {
// set HTTPS as per LR_HTTPS lrOptions.https = {
if (process.env.LR_HTTPS === "true") {
options.https = {
cert: fs.readFileSync('/certs/fullchain.pem'), cert: fs.readFileSync('/certs/fullchain.pem'),
key: fs.readFileSync('/certs/privkey.pem') key: fs.readFileSync('/certs/privkey.pem')
}; };
@ -59,8 +51,50 @@ else {
console.log("[HTTP mode]"); console.log("[HTTP mode]");
} }
// start server // start LiveReload server
const lrServer = livereload.createServer(options, healthcheck); // noinspection JSVoidFunctionReturnValueUsed
lrServer.watch('/watch') const lrServer = livereload.createServer(lrOptions, healthCheck.start());
lrServer.watch('/watch');
//#EOF // graceful termination on signals
const termSignals = {
'SIGHUP': 1,
'SIGINT': 2,
'SIGTERM': 15
};
const shutdown = async (signal, value) => {
console.log("shutting down...\n");
await lrServer.close();
await healthCheck.stop(() => {
console.log("health-check STOPPED\n");
});
console.log(`server stopped after receiving ${signal}(${value}).`);
}
// iterate signals and create listener for each
Object.keys(termSignals).forEach((signal) => {
process.on(signal, () => {
console.log("\n! received signal:", signal, " !");
shutdown(signal, termSignals[signal]).then(() => {
process.exit(128 + termSignals[signal]);
});
});
});
//
// functions
function healthCheckApp() {
const express = require('express');
const app = express();
const router = express.Router();
router.use((req, res, next) => {
res.header('Access-Control-Allow-Methods', 'GET');
next();
});
router.get('/health', (req, res) => {
res.status(200).send('Ok');
});
return app.use('/api/v1', router);
}