diff --git a/package.json b/package.json index 83706219..f92df0bb 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "dependencies": { "ajv": "^8.5.0", "axios": "^0.21.1", + "body-parser": "^1.19.0", "connect": "^3.7.0", "crypto-js": "^4.0.0", "highlight.js": "^11.0.0", @@ -83,4 +84,4 @@ "> 1%", "last 2 versions" ] -} \ No newline at end of file +} diff --git a/server.js b/server.js index 06594cbc..3bd98f74 100644 --- a/server.js +++ b/server.js @@ -11,9 +11,11 @@ const connect = require('connect'); const util = require('util'); const dns = require('dns'); const os = require('os'); +const bodyParser = require('body-parser'); /* Include helper functions */ const pingUrl = require('./services/ping'); // Used by the status check feature, to ping services +const saveConfig = require('./services/save-config'); // Saves users new conf.yml to file-system const printMessage = require('./services/print-message'); // Function to print welcome msg on start require('./src/utils/ConfigValidator'); // Include and kicks off the config file validation script @@ -41,8 +43,12 @@ const printWarning = (msg, error) => { console.warn(`\x1b[103m\x1b[34m${msg}\x1b[0m\n`, error || ''); // eslint-disable-line no-console }; +/* A middleware function for Connect, that filters requests based on method type */ +const method = (m, mw) => (req, res, next) => (req.method === m ? mw(req, res, next) : next()); + try { connect() + .use(bodyParser.json()) // Serves up the main built application to the root .use(serveStatic(`${__dirname}/dist`)) // During build, a custom page will be served before the app is available @@ -57,6 +63,14 @@ try { printWarning(`Error running ping check for ${req.url}\n`, e); } }) + .use('/api', method('GET', (req, res) => res.end('hi!'))) + // POST Endpoint used to save config, by writing conf.yml to disk + .use('/api/save', method('POST', (req, res) => { + saveConfig(req.body, async (results) => { + await res.end(results); + }); + // res.end('Will Save'); + })) // Finally, initialize the server then print welcome message .listen(port, () => { try { printWelcomeMessage(); } catch (e) { printWarning('Dashy is Starting...'); } diff --git a/services/save-config.js b/services/save-config.js new file mode 100644 index 00000000..4ce8ab2c --- /dev/null +++ b/services/save-config.js @@ -0,0 +1,77 @@ +const fs = require('fs').promises; + +/* Copies an existing file to a new file */ +async function backupConfig(fromPath, toPath, done) { + try { + fs.copyFile(fromPath, toPath, done({ success: true })); + } catch (error) { + done({ + success: false, + message: `Error backing up config file: ${error.message}`, + }); + } +} + +/* Creates a new file and writes content to it */ +async function saveNewConfig(writePath, fileContents, done) { + try { + fs.writeFile(writePath, fileContents, done({ success: true })); + } catch (error) { + done({ + success: false, + message: `Error writing changes to config file: ${error.message}`, + }); + } +} + +module.exports = (newConfig, render) => { + // Define constants for the config file + const settings = { + defaultLocation: './public/', + defaultFile: 'conf.yml', + filename: 'conf', + backupDenominator: '.backup.yml', + }; + + // Make the full file name and path to save the backup config file + const backupFilePath = `${settings.defaultLocation}${settings.filename}-` + + `${Math.round(new Date() / 1000)}${settings.backupDenominator}`; + + // The path where the main conf.yml should be read and saved to + const defaultFilePath = settings.defaultLocation + settings.defaultFile; + + // Returns a string confirming successful job + const getSuccessMessage = () => `Successfully backed up ${settings.defaultFile} to` + + ` ${backupFilePath}, and updated the contents of ${defaultFilePath}`; + + // Prepare the response returned by the API + const getRenderMessage = (success, errorMsg) => JSON.stringify({ + success, + message: !success ? errorMsg : getSuccessMessage(), + }); + + // Backs up the config, then writes new content to the existing config, and returns + backupConfig(defaultFilePath, backupFilePath, (backupResult) => { + if (!backupResult.success) { + render(getRenderMessage(false, backupResult.message)); + } else { + saveNewConfig(defaultFilePath, newConfig.config, (copyResult) => { + if (copyResult.failed) render(getRenderMessage(false, copyResult.message)); + render(getRenderMessage(true)); + }); + } + }); + + // Promise.resolve().then(() => { + // backupConfig(defaultFilePath, backupFilePath) + // .catch(error => thereWasAnError(error)); + // }).then(() => { + // saveNewConfig(defaultFilePath, newConfig) + // .catch(error => thereWasAnError(error)); + // }).then(() => { + // render(JSON.stringify({ + // success: !failed, + // message: failed ? errorMessage : 'Success!', + // })); + // }); +}; diff --git a/yarn.lock b/yarn.lock index e067356e..4978ef70 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2112,7 +2112,7 @@ bn.js@^5.0.0, bn.js@^5.1.1: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== -body-parser@1.19.0: +body-parser@1.19.0, body-parser@^1.19.0: version "1.19.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==