diff --git a/index.js b/index.js index eb58145..820fedf 100644 --- a/index.js +++ b/index.js @@ -1,14 +1,14 @@ const express = require("express"); const fs = require("fs"); const bodyParser = require("body-parser"); -const ws = require('ws'); +const ws = require("ws"); const helper = require("./helpers.js"); -const loggy = require("./logging") +const loggy = require("./logging"); const Eta = require("eta"); -const _ = require("underscore") -const path = require("path") +const _ = require("underscore"); +const path = require("path"); -loggy.init(true) +loggy.init(true); loggy.log("Preparing server", "info", "Server"); const app = express(); @@ -27,25 +27,25 @@ app.use( // Allowed urls for requests to /assets/ const allowsURLs = [ - 'bootstrap-icons/font/bootstrap-icons.css', - 'js-cookie/dist/js.cookie.min.js', - 'bootstrap/dist/css/bootstrap.min.css', - 'mdbootstrap/css/style.css', - 'bootstrap/dist/js/bootstrap.bundle.min.js', - 'jquery/dist/jquery.min.js', - 'darkreader/darkreader.js', - 'bootstrap-duration-picker/dist/bootstrap-duration-picker.css', - 'flatpickr/dist/flatpickr.min.css', - 'bootstrap-duration-picker/dist/bootstrap-duration-picker-debug.js', - 'flatpickr/dist/flatpickr.js', - 'bootstrap-icons/font/fonts/bootstrap-icons.woff2', - 'bootstrap/dist/css/bootstrap.min.css.map', - 'less/dist/less.min.js', - 'less/dist/less.min.js.map', - 'mdbootstrap/js/mdb.min.js' + "bootstrap-icons/font/bootstrap-icons.css", + "js-cookie/dist/js.cookie.min.js", + "bootstrap/dist/css/bootstrap.min.css", + "mdbootstrap/css/style.css", + "bootstrap/dist/js/bootstrap.bundle.min.js", + "jquery/dist/jquery.min.js", + "darkreader/darkreader.js", + "bootstrap-duration-picker/dist/bootstrap-duration-picker.css", + "flatpickr/dist/flatpickr.min.css", + "bootstrap-duration-picker/dist/bootstrap-duration-picker-debug.js", + "flatpickr/dist/flatpickr.js", + "bootstrap-icons/font/fonts/bootstrap-icons.woff2", + "bootstrap/dist/css/bootstrap.min.css.map", + "less/dist/less.min.js", + "less/dist/less.min.js.map", + "mdbootstrap/js/mdb.min.js", ]; -let loadedData = {} +let loadedData = {}; loggy.log("Loading config", "info", "Config"); if (fs.existsSync("data-persistence.json")) { @@ -68,20 +68,27 @@ currentState = { showMessage: false, messageAppearTime: 0, showProgressbar: true, - colorSegments: { 40000: "yellow", 20000: "#FFAE00", 5000: "#ff0000", "START": "green" }, + colorSegments: { + 40000: "yellow", + 20000: "#FFAE00", + 5000: "#ff0000", + START: "green", + }, textColors: {}, srvTime: 0, enableColoredText: true, debug: false, enableOverrun: true, - sessionToken: Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15), + sessionToken: + Math.random().toString(36).substring(2, 15) + + Math.random().toString(36).substring(2, 15), }; let configObject = { language: "en_uk", - port: 3000 -} -if(!fs.existsSync("config.json")) { + port: 3000, +}; +if (!fs.existsSync("config.json")) { fs.writeFileSync("config.json", "{}"); } const tempJsonText = JSON.parse(fs.readFileSync("config.json", "utf8")); @@ -89,9 +96,9 @@ configObject = _.extend(configObject, tempJsonText); fs.writeFileSync("config.json", JSON.stringify(configObject)); currentState = Object.assign({}, currentState, loadedData); -currentState.textColors = currentState.colorSegments +currentState.textColors = currentState.colorSegments; -loggy.log("Searching for languages", "info", "Language") +loggy.log("Searching for languages", "info", "Language"); const languagesRaw = fs.readdirSync("./lang"); const languages = []; for (let i = 0; i < languagesRaw.length; i++) { @@ -99,19 +106,19 @@ for (let i = 0; i < languagesRaw.length; i++) { languages.push(languagesRaw[i].replace(".json", "")); } } -loggy.log("Found " + languages.length + " languages", "info", "Language") +loggy.log("Found " + languages.length + " languages", "info", "Language"); - - -loggy.log("Reading language file", "info", "Language") -let languageProfile = JSON.parse(fs.readFileSync("lang/" + configObject.language + ".json", "utf8")); +loggy.log("Reading language file", "info", "Language"); +let languageProfile = JSON.parse( + fs.readFileSync("lang/" + configObject.language + ".json", "utf8") +); loggy.log("Preparing websocket", "info", "Websocket"); const wsServer = new ws.Server({ noServer: true }); -wsServer.on('connection', socket => { - socket.on('message', function incoming(data) { +wsServer.on("connection", (socket) => { + socket.on("message", function incoming(data) { if (data.toString() == "new client") { - updatedData() + updatedData(); } }); }); @@ -127,7 +134,7 @@ wsServer.broadcast = function broadcast(data) { let updatey = undefined; function updatedData() { - currentState.srvTime = new Date().getTime() + currentState.srvTime = new Date().getTime(); wsServer.broadcast(JSON.stringify(currentState)); clearTimeout(updatey); setTimeout(updatedData, 5000); @@ -141,18 +148,20 @@ app.get("/", function (req, res) { Eta.render(data, { lang: languageProfile, additional: { - languages: languages - } - })); + languages: languages, + }, + }) + ); } catch (e) { loggy.log("Error rendering template", "error", "Server"); const dataN = fs.readFileSync("templates/brokenTranslation.html", "utf8"); res.send( Eta.render(dataN, { additional: { - languages: languages - } - })); + languages: languages, + }, + }) + ); } }); @@ -162,12 +171,12 @@ app.get("/timer", function (req, res) { }); app.get("/timer-old", function (req, res) { - const data = fs.readFileSync("templates/timerPage.html", "utf8"); - res.send(data); - }); + const data = fs.readFileSync("templates/timerPage.html", "utf8"); + res.send(data); +}); app.get("/api/v1/data", function (req, res) { - currentState.srvTime = new Date().getTime() + currentState.srvTime = new Date().getTime(); res.json(currentState); }); @@ -188,178 +197,179 @@ app.get("/api/v1/system", function (req, res) { nodeEnv: process.env, nodeConfig: process.config, nodeTitle: process.title, - systemVersion: tempPkgObj.version - } + systemVersion: tempPkgObj.version, + }; res.json(systemData); }); - app.get("/api/v1/set/mode", function (req, res) { currentState.mode = req.query.mode; - updatedData() + updatedData(); res.json({ status: "ok" }); }); app.get("/api/v1/set/layout/showMillis", function (req, res) { - const resy = helper.wrapBooleanConverter(req.query.show, res) + const resy = helper.wrapBooleanConverter(req.query.show, res); if (resy != undefined) { currentState.showMilliSeconds = resy; - if (req.query.persist === 'true') { - dataToBeWritten.showMilliSeconds = currentState.showMilliSeconds + if (req.query.persist === "true") { + dataToBeWritten.showMilliSeconds = currentState.showMilliSeconds; } res.json({ status: "ok" }); } - updatedData() - + updatedData(); }); app.get("/api/v1/set/timerGoal", function (req, res) { currentState.countdownGoal = new Date(parseInt(req.query.time)).getTime(); // ToDO error handling res.json({ status: "ok" }); - updatedData() + updatedData(); }); app.get("/api/v1/set/addMillisToTimer", function (req, res) { currentState.timeAmountInital = req.query.time; - currentState.countdownGoal = new Date().getTime() + parseInt(req.query.time) + currentState.countdownGoal = new Date().getTime() + parseInt(req.query.time); currentState.pauseMoment = new Date().getTime(); res.json({ status: "ok" }); - updatedData() + updatedData(); }); app.get("/api/v1/set/relativAddMillisToTimer", function (req, res) { - currentState.timeAmountInital = req.query.time; - currentState.countdownGoal = currentState.countdownGoal + parseInt(req.query.time) - currentState.pauseMoment = new Date().getTime(); - res.json({ status: "ok" }); - updatedData() + + currentState.timeAmountInital += parseInt(req.query.time); + currentState.countdownGoal = currentState.countdownGoal + parseInt(req.query.time); + // currentState.pauseMoment = new Date().getTime(); + + res.json({ status: "ok" }); + updatedData(); }); app.get("/api/v1/ctrl/timer/pause", function (req, res) { currentState.timerRunState = false; currentState.pauseMoment = new Date().getTime(); res.json({ status: "ok" }); - updatedData() + updatedData(); }); app.get("/api/v1/ctrl/timer/play", function (req, res) { - if (currentState.timerRunState == false) { - currentState.timerRunState = true - currentState.countdownGoal += new Date().getTime() - currentState.pauseMoment; + currentState.timerRunState = true; + currentState.countdownGoal += + new Date().getTime() - currentState.pauseMoment; } res.json({ status: "ok" }); - updatedData() + updatedData(); }); app.get("/api/v1/ctrl/timer/restart", function (req, res) { - currentState.countdownGoal = new Date().getTime() + parseInt(currentState.timeAmountInital) + currentState.countdownGoal = + new Date().getTime() + parseInt(currentState.timeAmountInital); res.json({ status: "ok" }); - updatedData() + updatedData(); }); app.get("/api/v1/set/layout/showTime", function (req, res) { - const resy = helper.wrapBooleanConverter(req.query.show, res) + const resy = helper.wrapBooleanConverter(req.query.show, res); if (resy != undefined) { currentState.showTimeOnCountdown = resy; - if (req.query.persist === 'true') { - dataToBeWritten.showTimeOnCountdown = currentState.showTimeOnCountdown + if (req.query.persist === "true") { + dataToBeWritten.showTimeOnCountdown = currentState.showTimeOnCountdown; } res.json({ status: "ok" }); } - updatedData() + updatedData(); }); app.get("/api/v1/set/enableOverrun", function (req, res) { - const resy = helper.wrapBooleanConverter(req.query.enable, res) - if (resy != undefined) { - currentState.enableOverrun = resy; - if (req.query.persist === 'true') { - dataToBeWritten.enableOverrun = currentState.enableOverrun - } - res.json({ status: "ok" }); - } - updatedData() - }); + const resy = helper.wrapBooleanConverter(req.query.enable, res); + if (resy != undefined) { + currentState.enableOverrun = resy; + if (req.query.persist === "true") { + dataToBeWritten.enableOverrun = currentState.enableOverrun; + } + res.json({ status: "ok" }); + } + updatedData(); +}); app.get("/api/v1/set/progressbar/show", function (req, res) { - currentState.showProgressbar = (req.query.show === 'true'); - if (req.query.persist === 'true') { - dataToBeWritten.showProgressbar = currentState.showProgressbar + currentState.showProgressbar = req.query.show === "true"; + if (req.query.persist === "true") { + dataToBeWritten.showProgressbar = currentState.showProgressbar; } res.json({ status: "ok" }); - updatedData() + updatedData(); }); app.get("/api/v1/set/progressbar/colors", function (req, res) { try { - let data = req.query.colors + let data = req.query.colors; if (req.query.isBase64 === "true") { - data = atob(data) + data = atob(data); } currentState.colorSegments = JSON.parse(data); - if (req.query.persist === 'true') { - dataToBeWritten.colorSegments = currentState.colorSegments + if (req.query.persist === "true") { + dataToBeWritten.colorSegments = currentState.colorSegments; } res.json({ status: "ok" }); } catch (error) { res.json({ status: "error", message: error }); - console.error(error) + console.error(error); } - updatedData() + updatedData(); }); app.get("/api/v1/set/text/colors", function (req, res) { try { if (req.query.copy === "true") { - currentState.textColors = currentState.colorSegments + currentState.textColors = currentState.colorSegments; res.json({ status: "ok" }); } else { - let data = req.query.colors + let data = req.query.colors; if (req.query.isBase64 === "true") { - data = atob(data) + data = atob(data); } - console.debug(data) + console.debug(data); currentState.textColors = JSON.parse(data); - if (req.query.persist === 'true') { - dataToBeWritten.textColors = currentState.textColors + if (req.query.persist === "true") { + dataToBeWritten.textColors = currentState.textColors; } } res.json({ status: "ok" }); } catch (error) { res.json({ status: "error", message: error }); - console.error(error) + console.error(error); } - updatedData() + updatedData(); }); app.get("/api/v1/set/text/enableColoring", function (req, res) { - currentState.enableColoredText = (req.query.enable === 'true'); - if (req.query.persist === 'true') { - dataToBeWritten.enableColoredText = currentState.enableColoredText + currentState.enableColoredText = req.query.enable === "true"; + if (req.query.persist === "true") { + dataToBeWritten.enableColoredText = currentState.enableColoredText; } res.json({ status: "ok" }); - updatedData() + updatedData(); }); app.get("/api/v1/ctrl/message/show", function (req, res) { - currentState.message = req.query.msg - currentState.showMessage = true - currentState.messageAppearTime = new Date().getTime() + currentState.message = req.query.msg; + currentState.showMessage = true; + currentState.messageAppearTime = new Date().getTime(); res.json({ status: "ok" }); - updatedData() + updatedData(); }); app.get("/api/v1/debug", function (req, res) { - currentState.debug = (req.query.enable === 'true'); + currentState.debug = req.query.enable === "true"; res.json({ status: "ok" }); - updatedData() + updatedData(); }); app.get("/api/v1/ctrl/message/hide", function (req, res) { - currentState.showMessage = false + currentState.showMessage = false; res.json({ status: "ok" }); - updatedData() + updatedData(); }); app.get("/api/v1/storage/commit", function (req, res) { @@ -370,10 +380,9 @@ app.get("/api/v1/storage/commit", function (req, res) { } catch (error) { res.json({ status: "error", reason: error }); } - updatedData() + updatedData(); }); - app.get("/api/v1/storage/delete", function (req, res) { if (req.query.delete === "true") { if (fs.existsSync("data-persistence.json")) { @@ -382,126 +391,159 @@ app.get("/api/v1/storage/delete", function (req, res) { } else { res.json({ status: "error", reason: "No persistence data was found" }); } - } else { - - } res.json({ status: "error", reason: "Missing delete argument" }); - updatedData() + } + res.json({ status: "error", reason: "Missing delete argument" }); + updatedData(); }); // UI Routes // Returns an object containg all available languages -app.get("/api/ui/v1/lang/list", function handleLangList(req, res){ +app.get("/api/ui/v1/lang/list", function handleLangList(req, res) { const tempRespObject = { status: "ok", - languages: languages - } + languages: languages, + }; res.json(tempRespObject); -}) +}); app.get("/api/ui/v1/lang/set", function (req, res) { - if(req.query.lang == undefined || req.query.lang == ""){ + if (req.query.lang == undefined || req.query.lang == "") { res.json({ status: "error", reason: "Missing language" }); return; } const testLang = req.query.lang; - loggy.log("Reloading language file", "info", "Language") - if(!fs.existsSync("lang/" + testLang + ".json")){ - loggy.log("Language reload failed, file does not exist", "error", "Language") - res.status(500).json({ status: "error", reason: "Language file not found" }); - return + loggy.log("Reloading language file", "info", "Language"); + if (!fs.existsSync("lang/" + testLang + ".json")) { + loggy.log( + "Language reload failed, file does not exist", + "error", + "Language" + ); + res + .status(500) + .json({ status: "error", reason: "Language file not found" }); + return; } const tempLang = fs.readFileSync("lang/" + testLang + ".json", "utf8"); const tempLangObj = helper.tryToParseJson(tempLang); - if(!tempLangObj){ - loggy.log("Language reload failed, file is not valid", "error", "Language") - res.status(500).json({ status: "error", reason: "Language file is not valid" }); - return + if (!tempLangObj) { + loggy.log("Language reload failed, file is not valid", "error", "Language"); + res + .status(500) + .json({ status: "error", reason: "Language file is not valid" }); + return; } - if(tempLangObj._metadata == undefined){ - loggy.log("Language reload failed, file is not valid, metadata missing", "error", "Language") - res.status(500).json({ status: "error", reason: "Language file is not valid" }); - return + if (tempLangObj._metadata == undefined) { + loggy.log( + "Language reload failed, file is not valid, metadata missing", + "error", + "Language" + ); + res + .status(500) + .json({ status: "error", reason: "Language file is not valid" }); + return; } - loggy.log("Language reloaded, loaded " + tempLangObj._metadata.lang + "@" + tempLangObj._metadata.version, "info", "Language") + loggy.log( + "Language reloaded, loaded " + + tempLangObj._metadata.lang + + "@" + + tempLangObj._metadata.version, + "info", + "Language" + ); configObject.language = req.query.lang; languageProfile = tempLangObj; res.status(200).json({ status: "ok" }); fs.writeFileSync("config.json", JSON.stringify(configObject)); }); - app.use("/assets/*", function handleModuleFiles(req, res) { - if(allowsURLs.indexOf(req.params[0]) > -1){ + if (allowsURLs.indexOf(req.params[0]) > -1) { res.sendFile(path.join(__dirname, "node_modules", req.params[0])); } else { - loggy.log("Attempt to access restricted asset file " + req.params[0], "error", "Security") - res.status(403).json({ status: "error", reason: "Access to restricted asset file denied" }); + loggy.log( + "Attempt to access restricted asset file " + req.params[0], + "error", + "Security" + ); + res.status(403).json({ + status: "error", + reason: "Access to restricted asset file denied", + }); } // console.log(recordedURLs) -}) +}); app.use(function (req, res, next) { res.status(404); loggy.log("Server responded with 404 error", "warn", "Server", true); // respond with html page - if (req.accepts('html')) { + if (req.accepts("html")) { const data = fs.readFileSync("templates/errorPages/404.html", "utf8"); - res.status(404) + res.status(404); res.send(data); return; } // respond with json - if (req.accepts('json')) { - res.json({ error: 'Not found' }); + if (req.accepts("json")) { + res.json({ error: "Not found" }); return; } // default to plain-text. send() - res.type('txt').send('Not found'); + res.type("txt").send("Not found"); }); - - - - /*app.use(function(err, req, res, next) { console.error(err.stack); if(String(err.stack).includes("TypeError: Cannot read properties of undefined")) { - const data = fs.readFileSync("templates/brokenTranslation.html", "utf8"); - res.send(data); + const data = fs.readFileSync("templates/brokenTranslation.html", "utf8"); + res.send(data); }else{ - res.status(500).send('Something broke!'); + res.status(500).send('Something broke!'); } });*/ - - loggy.log("Starting server", "info", "Server"); const port = configObject.port; -process.on('SIGINT', function () { - loggy.log("Caught interrupt signal and shutting down gracefully", "info", "Shutdown"); +process.on("SIGINT", function () { + loggy.log( + "Caught interrupt signal and shutting down gracefully", + "info", + "Shutdown" + ); server.close(); // Make the express server stop - loggy.log("Goodbye! 👋", "magic", "Shutdown", true) + loggy.log("Goodbye! 👋", "magic", "Shutdown", true); loggy.close(); // Close and write log process.exit(); // Quit the application }); const server = app.listen(port); -server.on('upgrade', (request, socket, head) => { - wsServer.handleUpgrade(request, socket, head, socket => { - wsServer.emit('connection', socket, request); +server.on("upgrade", (request, socket, head) => { + wsServer.handleUpgrade(request, socket, head, (socket) => { + wsServer.emit("connection", socket, request); }); }); - loggy.log("=======================", "info", "", true); loggy.log("Server running on port " + port, "magic", "", true); -loggy.log("Visit http://localhost:" + port + "/timer for the timer view", "magic", "", true); -loggy.log("Visit http://localhost:" + port + " for the admin view", "magic", "", true); -loggy.log("=======================", "info", "", true); \ No newline at end of file +loggy.log( + "Visit http://localhost:" + port + "/timer for the timer view", + "magic", + "", + true +); +loggy.log( + "Visit http://localhost:" + port + " for the admin view", + "magic", + "", + true +); +loggy.log("=======================", "info", "", true);