diff --git a/app/database/sqlite.database.js b/app/database/sqlite.database.js
new file mode 100644
index 0000000..c5443da
--- /dev/null
+++ b/app/database/sqlite.database.js
@@ -0,0 +1,15 @@
+const Database = require("better-sqlite3");
+const logger = require("../utils/logger.util");
+const db_conf = require("../config/db.conf");
+
+var db;
+
+try {
+ logger.info("Connecting to Database...");
+ db = new Database(db_conf.path, { fileMustExist: true });
+ logger.info("Connected to Database.");
+} catch (error) {
+ logger.error(error.message);
+}
+
+module.exports = db;
\ No newline at end of file
diff --git a/app/routes/private_routes.js b/app/routes/private_routes.js
new file mode 100644
index 0000000..c92eff9
--- /dev/null
+++ b/app/routes/private_routes.js
@@ -0,0 +1,292 @@
+const db = require("../database/sqlite.database.js");
+const logger = require("../utils/logger.util.js");
+const Database = require("better-sqlite3");
+const path = require("path");
+const fs = require("fs");
+const app_conf = require("../config/app.conf.js");
+const db_conf = require("../config/db.conf.js");
+
+const express = require('express');
+const router = express.Router();
+
+function errorPageRenderer(res, emoji, message) {
+ res.render("error", {
+ page_title: app_conf.name,
+ css_path: path.join(app_conf.paths.css.web, "stylesheet.css"),
+ error_emoji: emoji,
+ error_message: message
+ })
+}
+
+router.delete("/remove", async (req, res) => {
+ if (req.query["element"]) {
+ try {
+ const remove = db.prepare(`DELETE FROM ${db_conf.table_name} WHERE Name='${req.query["element"]}';`);
+ if (remove.run()["changes"]) {
+ res.status(200).send();
+ } else {
+ res.status(404).send();
+ }
+ } catch (error) {
+ res.status(500).send();
+ logger.error(error.message);
+ }
+ } else {
+ res.status(400).send();
+ }
+});
+
+router.post("/upload", async (req, res) => {
+ if (!req.files || !req.files.file) {
+ res.status(422).send();
+ } else {
+ const uploadedFile = req.files.file;
+
+ db.close();
+ fs.copyFile(uploadedFile.tempFilePath, db_conf.path, (error) => {
+ if (error) {
+ logger.error(error);
+ } else {
+ try {
+ db = new Database(db_conf.path, { fileMustExist: true });
+ } catch (error) {
+ logger.error(error);
+ shutting_down = true;
+ shutdown();
+ }
+ }
+ });
+ }
+ res.status(201).send();
+});
+
+router.get("/download", async (req, res) => {
+ res.download(db_conf.path);
+});
+
+router.post("/add", async (req, res) => {
+ if (req.query["element"] && req.body) {
+ try {
+ let keys = Object.keys(req.body);
+ let values = `'${req.query["element"]}',`;
+
+ for (const k of keys) {
+ if (req.body[k] === "null") {
+ values += `NULL,`
+ } else {
+ values += `'${req.body[k]}',`;
+ }
+ }
+
+ if (values) {
+ values = values.slice(0, -1);
+ const update = db.prepare(`INSERT INTO ${db_conf.table_name} VALUES (${values});`);
+ update.run();
+ res.status(200).send();
+ } else {
+ res.status(400).send();
+ }
+ } catch (error) {
+ res.status(500).send();
+ logger.error(error.message);
+ }
+ } else {
+ res.status(400).send();
+ }
+});
+
+router.post("/edit", async (req, res) => {
+ if (req.query["element"] && req.body) {
+ try {
+ let keys = Object.keys(req.body);
+ let values = "";
+
+ for (const k of keys) {
+ if (req.body[k] === "null") {
+ values += `${k}=NULL,`
+ } else {
+ values += `${k}='${req.body[k]}',`;
+ }
+ }
+
+ if (values) {
+ values = values.slice(0, -1);
+ const update = db.prepare(`UPDATE ${db_conf.table_name} SET ${values} WHERE Name='${req.query["element"]}'`);
+ update.run();
+ res.status(200).send();
+ } else {
+ res.status(400).send();
+ }
+ } catch (error) {
+ res.status(500).send();
+ logger.error(error.message);
+ }
+ } else {
+ res.status(400).send();
+ }
+});
+
+router.get("/add", async (req, res) => {
+ try {
+ const select_get = db.prepare(`SELECT * FROM ${db_conf.table_name};`);
+ const row = select_get.get();
+
+ const sql_header = Object.keys(row);
+ var table_header = `
${sql_header[0]} | \n`;
+ sql_header.splice(0, 1);
+
+ if (row) {
+ var column_options = {};
+ for (const key of sql_header) {
+ table_header += `${key} | \n`;
+
+ const select_distinct = db.prepare(`
+ SELECT DISTINCT ${key}
+ FROM ${db_conf.table_name};
+ `);
+ values = select_distinct.all();
+
+ for (v of values) {
+ if (column_options[key] == null) {
+ column_options[key] = Object.values(v);
+ } else {
+ column_options[key] = column_options[key].concat(Object.values(v));
+ }
+ }
+ }
+
+ var table_data = "";
+ const sql_row_values = Object.values(row);
+
+ table_data += ` | \n`;
+ for (let i = 1; i < sql_row_values.length; i++) {
+ table_data += ` | \n`;
+ }
+
+ table_data += "
";
+ res.render("add", {
+ page_title: app_conf.name,
+ css_path: path.join(app_conf.paths.css.web, "stylesheet.css"),
+ js_path: path.join(app_conf.paths.js.web, "add.js"),
+ column_options: JSON.stringify(column_options),
+ table_header: table_header,
+ table_data: table_data
+ });
+ } else {
+ errorPageRenderer(res, "❓", "The database is empty.");
+ }
+ } catch (error) {
+ logger.error(error.message);
+ }
+});
+
+router.get("/edit", async (req, res) => {
+ if (req.query["element"]) {
+ try {
+ const select_get = db.prepare(`SELECT * FROM ${db_conf.table_name} WHERE Name='${req.query["element"]}';`);
+ const row = select_get.get();
+
+ if (row) {
+ const sql_header = Object.keys(row);
+ var table_header = `${sql_header[0]} | \n`;
+ sql_header.splice(0, 1);
+
+ var column_options = {};
+ for (const key of sql_header) {
+ table_header += `${key} | \n`;
+
+ const select_distinct = db.prepare(`
+ SELECT DISTINCT ${key}
+ FROM ${db_conf.table_name}
+ WHERE ${key} IS NOT NULL
+ AND ${key} NOT IN (
+ SELECT ${key}
+ FROM ${db_conf.table_name}
+ WHERE Name='${req.query["element"]}'
+ AND ${key} IS NOT NULL);
+ `);
+ values = select_distinct.all();
+
+ for (v of values) {
+ if (column_options[key] == null) {
+ column_options[key] = Object.values(v);
+ } else {
+ column_options[key] = column_options[key].concat(Object.values(v));
+ }
+ }
+ }
+
+ var table_data = "";
+ const sql_row_values = Object.values(row);
+ table_data += `| ${sql_row_values[0]} | \n`;
+ sql_row_values.splice(0, 1);
+
+ for (const v of sql_row_values) {
+ table_data += ` | \n`;
+ }
+
+ table_data += "
";
+
+ res.render("edit", {
+ page_title: app_conf.name,
+ css_path: path.join(app_conf.paths.css.web, "stylesheet.css"),
+ js_path: path.join(app_conf.paths.js.web, "edit.js"),
+ column_options: JSON.stringify(column_options),
+ table_header: table_header,
+ table_data: table_data
+ });
+ } else {
+ errorPageRenderer(res, "🔍❌", "Not found.");
+ }
+ } catch (error) {
+ logger.error(error.message);
+ }
+ } else {
+ errorPageRenderer(res, "✋⛔", "Bad request.");
+ }
+});
+
+router.get("", async (req, res) => {
+ logger.info(`${req.method}: "${req.url}" => ${req.get("User-Agent")}`);
+
+ try {
+ const select = db.prepare(`SELECT * FROM ${db_conf.table_name}`);
+ const rows = select.all();
+
+ if (rows[0]) {
+ const sql_header = Object.keys(rows[0]);
+
+ var table_header = "";
+ for (const key of sql_header) {
+ table_header += `${key} | \n`;
+ }
+
+ var table_data = "";
+ for (const row of rows) {
+ const sql_row_values = Object.values(row);
+ table_data += "";
+
+ for (const v of sql_row_values) {
+ table_data += `| ${v} | \n`;
+ }
+
+ table_data += "
";
+ }
+
+ res.render("index", {
+ page_title: app_conf.name,
+ css_path: path.join(app_conf.paths.css.web, "stylesheet.css"),
+ js_path: path.join(app_conf.paths.js.web, "index.js"),
+ table_header: table_header,
+ table_data: table_data
+ });
+ } else {
+ errorPageRenderer(res, "❓", "The database is empty.");
+ }
+ } catch (error) {
+ errorPageRenderer(res, "🌋💥", "Internal server error. Check logs for more information.");
+ logger.error(error.message);
+ }
+});
+
+module.exports = router;
\ No newline at end of file
diff --git a/app/routes/public_routes.js b/app/routes/public_routes.js
new file mode 100644
index 0000000..4bd8cbc
--- /dev/null
+++ b/app/routes/public_routes.js
@@ -0,0 +1,22 @@
+const path = require("path");
+const app_conf = require("../config/app.conf.js");
+
+const express = require('express');
+const router = express.Router();
+
+router.use(app_conf.paths.css.web, express.static(path.join(path.dirname(require.main.filename), app_conf.paths.css.src)));
+router.use(app_conf.paths.js.web, express.static(path.join(path.dirname(require.main.filename), app_conf.paths.js.src)));
+
+/*
+ * Main endpoint - might implement authentication step here...
+ */
+router.get("/", (req, res) => {
+ res.render("auth", {
+ page_title: app_conf.name,
+ css_path: path.join(app_conf.paths.css.web, "stylesheet.css"),
+ protocol: app_conf.protocol,
+ host: app_conf.host
+ });
+});
+
+module.exports = router;
\ No newline at end of file
diff --git a/app/samples/app.conf.sample b/app/samples/app.conf.sample
deleted file mode 100644
index 4cb0088..0000000
--- a/app/samples/app.conf.sample
+++ /dev/null
@@ -1,18 +0,0 @@
-module.exports = {
- name: "CRTelemetry",
- port: 80,
- username: "admin",
- password: "password",
- logger: {
- level: "info",
- error_path: "/app/logs/error.log",
- full_path: "/app/logs/combined.log"
- },
- timestamp_format: "HH:mm:ss DD-MM-YYYY",
- views_directory: "/app/views/",
- temp_directory: "/app/temp/",
- web_paths: {
- css: "/app/src/css",
- js: "/app/src/javascript"
- }
-};
\ No newline at end of file
diff --git a/app/utils/logger.util.js b/app/utils/logger.util.js
new file mode 100644
index 0000000..e828584
--- /dev/null
+++ b/app/utils/logger.util.js
@@ -0,0 +1,24 @@
+const winston = require("winston");
+const app_conf = require("../config/app.conf");
+const path = require("path");
+
+/*
+ * Initialize console and file logger
+ */
+const logger = winston.createLogger({
+ levels: winston.config.syslog.levels,
+ level: process.env.LOG_LEVEL || app_conf.logger.level,
+ format: winston.format.combine(
+ winston.format.timestamp({
+ format: app_conf.timestamp_format,
+ }),
+ winston.format.printf((info) => `[${info.timestamp}][${info.level}]: ${info.message}`)
+ ),
+ transports: [
+ new winston.transports.File({ filename: path.join(path.dirname(require.main.filename), app_conf.logger.error_path), level: "error", }),
+ new winston.transports.File({ filename: path.join(path.dirname(require.main.filename), app_conf.logger.full_path) }),
+ new winston.transports.Console()
+ ],
+});
+
+module.exports = logger;
\ No newline at end of file
diff --git a/app/views/auth.ejs b/app/views/auth.ejs
new file mode 100644
index 0000000..bbd239e
--- /dev/null
+++ b/app/views/auth.ejs
@@ -0,0 +1,31 @@
+
+
+
+
+
+ <%= page_title %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Enter
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/app.conf.sample b/samples/app.conf.sample
new file mode 100644
index 0000000..a5d999e
--- /dev/null
+++ b/samples/app.conf.sample
@@ -0,0 +1,27 @@
+module.exports = {
+ name: "CRTelemetry",
+ host: "localhost",
+ protocol: "http",
+ port: 80,
+ username: "admin",
+ password: "password",
+ token_ttl: 1800,
+ logger: {
+ level: "info",
+ error_path: "/app/logs/error.log",
+ full_path: "/app/logs/combined.log"
+ },
+ timestamp_format: "HH:mm:ss DD-MM-YYYY",
+ views_directory: "/app/views",
+ temp_directory: "/app/temp",
+ paths: {
+ css: {
+ web: "/css",
+ src: "/src/css"
+ },
+ js: {
+ web: "/javascript",
+ src: "/src/javascript"
+ }
+ }
+};
\ No newline at end of file
diff --git a/app/samples/db.conf.sample b/samples/db.conf.sample
similarity index 100%
rename from app/samples/db.conf.sample
rename to samples/db.conf.sample
diff --git a/server.js b/server.js
index 222ab54..bb94ecc 100644
--- a/server.js
+++ b/server.js
@@ -1,346 +1,37 @@
const express = require("express");
-const Database = require("better-sqlite3");
-const winston = require("winston");
const path = require("path");
const fileUpload = require("express-fileupload");
const basicAuth = require("express-basic-auth");
const fs = require("fs");
const app_conf = require("./app/config/app.conf.js");
-const db_conf = require("./app/config/db.conf.js");
+
+const public_routes = require('./app/routes/public_routes');
+const private_routes = require('./app/routes/private_routes');
+
+const logger = require("./app/utils/logger.util.js");
+const db = require("./app/database/sqlite.database.js");
const app = express();
-var db;
var shutting_down = false;
/*
* Static middleware - serve css and js files to public
*/
-app.use(app_conf.web_paths.css, express.static(path.join(__dirname, app_conf.web_paths.css)));
-app.use(app_conf.web_paths.js, express.static(path.join(__dirname, app_conf.web_paths.js)));
app.use(express.json());
app.use(fileUpload({
limits: { fileSize: 102400 },
useTempFiles: true,
tempFileDir: path.join(__dirname, app_conf.temp_directory)
}));
-app.use(basicAuth({
+
+app.use("/", public_routes);
+
+app.use("/app", basicAuth({
users: {
[app_conf.username]: app_conf.password
},
challenge: true
-}));
-
-/*
- * Initialize console and file logger
- */
-const logger = winston.createLogger({
- levels: winston.config.syslog.levels,
- level: process.env.LOG_LEVEL || app_conf.logger.level,
- format: winston.format.combine(
- winston.format.timestamp({
- format: app_conf.timestamp_format,
- }),
- winston.format.printf((info) => `[${info.timestamp}][${info.level}]: ${info.message}`)
- ),
- transports: [
- new winston.transports.File({ filename: path.join(__dirname, app_conf.logger.error_path), level: "error", }),
- new winston.transports.File({ filename: path.join(__dirname, app_conf.logger.full_path) }),
- new winston.transports.Console()
- ],
-});
-
-app.delete("/app/remove", async (req, res) => {
- if (req.query["element"]) {
- try {
- const remove = db.prepare(`DELETE FROM ${db_conf.table_name} WHERE Name='${req.query["element"]}';`);
- if (remove.run()["changes"]) {
- res.status(200).send();
- } else {
- res.status(404).send();
- }
- } catch (error) {
- res.status(500).send();
- logger.error(error.message);
- }
- } else {
- res.status(400).send();
- }
-});
-
-app.post("/app/upload", async (req, res) => {
- if (!req.files || !req.files.file) {
- res.status(422).send();
- } else {
- const uploadedFile = req.files.file;
-
- db.close();
- fs.copyFile(uploadedFile.tempFilePath, db_conf.path, (error) => {
- if (error) {
- logger.error(error);
- } else {
- try {
- db = new Database(db_conf.path, { fileMustExist: true });
- } catch (error) {
- logger.error(error);
- shutting_down = true;
- shutdown();
- }
- }
- });
- }
- res.status(201).send();
-});
-
-app.get("/app/download", async (req, res) => {
- res.download(db_conf.path);
-});
-
-app.post("/app/add", async (req, res) => {
- if (req.query["element"] && req.body) {
- try {
- let keys = Object.keys(req.body);
- let values = `'${req.query["element"]}',`;
-
- for (const k of keys) {
- if (req.body[k] === "null") {
- values += `NULL,`
- } else {
- values += `'${req.body[k]}',`;
- }
- }
-
- if (values) {
- values = values.slice(0, -1);
- const update = db.prepare(`INSERT INTO ${db_conf.table_name} VALUES (${values});`);
- update.run();
- res.status(200).send();
- } else {
- res.status(400).send();
- }
- } catch (error) {
- res.status(500).send();
- logger.error(error.message);
- }
- } else {
- res.status(400).send();
- }
-});
-
-app.post("/app/edit", async (req, res) => {
- if (req.query["element"] && req.body) {
- try {
- let keys = Object.keys(req.body);
- let values = "";
-
- for (const k of keys) {
- if (req.body[k] === "null") {
- values += `${k}=NULL,`
- } else {
- values += `${k}='${req.body[k]}',`;
- }
- }
-
- if (values) {
- values = values.slice(0, -1);
- const update = db.prepare(`UPDATE ${db_conf.table_name} SET ${values} WHERE Name='${req.query["element"]}'`);
- update.run();
- res.status(200).send();
- } else {
- res.status(400).send();
- }
- } catch (error) {
- res.status(500).send();
- logger.error(error.message);
- }
- } else {
- res.status(400).send();
- }
-});
-
-/*
- * Main endpoint - might implement authentication step here...
- */
-app.get("/", (req, res) => {
- res.redirect("app/");
-});
-
-function errorPageRenderer(res, emoji, message) {
- res.render("error", {
- page_title: app_conf.name,
- css_path: path.join(app_conf.web_paths.css, "stylesheet.css"),
- error_emoji: emoji,
- error_message: message
- })
-}
-
-app.get("/app/add", async (req, res) => {
- try {
- const select_get = db.prepare(`SELECT * FROM ${db_conf.table_name};`);
- const row = select_get.get();
-
- const sql_header = Object.keys(row);
- var table_header = `${sql_header[0]} | \n`;
- sql_header.splice(0, 1);
-
- if (row) {
- var column_options = {};
- for (const key of sql_header) {
- table_header += `${key} | \n`;
-
- const select_distinct = db.prepare(`
- SELECT DISTINCT ${key}
- FROM ${db_conf.table_name};
- `);
- values = select_distinct.all();
-
- for (v of values) {
- if (column_options[key] == null) {
- column_options[key] = Object.values(v);
- } else {
- column_options[key] = column_options[key].concat(Object.values(v));
- }
- }
- }
-
- var table_data = "";
- const sql_row_values = Object.values(row);
-
- table_data += ` | \n`;
- for (let i = 1; i < sql_row_values.length; i++) {
- table_data += ` | \n`;
- }
-
- table_data += "
";
- res.render("add", {
- page_title: app_conf.name,
- css_path: path.join(app_conf.web_paths.css, "stylesheet.css"),
- js_path: path.join(app_conf.web_paths.js, "add.js"),
- column_options: JSON.stringify(column_options),
- table_header: table_header,
- table_data: table_data
- });
- } else {
- errorPageRenderer(res, "❓", "The database is empty.");
- }
- } catch (error) {
- logger.error(error.message);
- }
-});
-
-/*
- * Edit endpoint - edits a specific row
- */
-app.get("/app/edit", async (req, res) => {
- if (req.query["element"]) {
- try {
- const select_get = db.prepare(`SELECT * FROM ${db_conf.table_name} WHERE Name='${req.query["element"]}';`);
- const row = select_get.get();
-
- if (row) {
- const sql_header = Object.keys(row);
- var table_header = `${sql_header[0]} | \n`;
- sql_header.splice(0, 1);
-
- var column_options = {};
- for (const key of sql_header) {
- table_header += `${key} | \n`;
-
- const select_distinct = db.prepare(`
- SELECT DISTINCT ${key}
- FROM ${db_conf.table_name}
- WHERE ${key} IS NOT NULL
- AND ${key} NOT IN (
- SELECT ${key}
- FROM ${db_conf.table_name}
- WHERE Name='${req.query["element"]}'
- AND ${key} IS NOT NULL);
- `);
- values = select_distinct.all();
-
- for (v of values) {
- if (column_options[key] == null) {
- column_options[key] = Object.values(v);
- } else {
- column_options[key] = column_options[key].concat(Object.values(v));
- }
- }
- }
-
- var table_data = "";
- const sql_row_values = Object.values(row);
- table_data += `| ${sql_row_values[0]} | \n`;
- sql_row_values.splice(0, 1);
-
- for (const v of sql_row_values) {
- table_data += ` | \n`;
- }
-
- table_data += "
";
-
- res.render("edit", {
- page_title: app_conf.name,
- css_path: path.join(app_conf.web_paths.css, "stylesheet.css"),
- js_path: path.join(app_conf.web_paths.js, "edit.js"),
- column_options: JSON.stringify(column_options),
- table_header: table_header,
- table_data: table_data
- });
- } else {
- errorPageRenderer(res, "🔍❌", "Not found.");
- }
- } catch (error) {
- logger.error(error.message);
- }
- } else {
- errorPageRenderer(res, "✋⛔", "Bad request.");
- }
-});
-
-/*
- * App endpoint - shows the database
- */
-app.get("/app", async (req, res) => {
- logger.info(`${req.method}: "${req.url}" => ${req.get("User-Agent")}`);
-
- try {
- const select = db.prepare(`SELECT * FROM ${db_conf.table_name}`);
- const rows = select.all();
-
- if (rows[0]) {
- const sql_header = Object.keys(rows[0]);
-
- var table_header = "";
- for (const key of sql_header) {
- table_header += `${key} | \n`;
- }
-
- var table_data = "";
- for (const row of rows) {
- const sql_row_values = Object.values(row);
- table_data += "";
-
- for (const v of sql_row_values) {
- table_data += `| ${v} | \n`;
- }
-
- table_data += "
";
- }
-
- res.render("index", {
- page_title: app_conf.name,
- css_path: path.join(app_conf.web_paths.css, "stylesheet.css"),
- js_path: path.join(app_conf.web_paths.js, "index.js"),
- table_header: table_header,
- table_data: table_data
- });
- } else {
- errorPageRenderer(res, "❓", "The database is empty.");
- }
- } catch (error) {
- errorPageRenderer(res, "🌋💥", "Internal server error. Check logs for more information.");
- logger.error(error.message);
- }
-})
+}), private_routes);
/*
* Initialise ExpressJS
@@ -350,14 +41,7 @@ app.set("view engine", "ejs");
app.set("views", path.join(__dirname, app_conf.views_directory));
const server = app.listen(app_conf.port, () => {
logger.info("Starting Database Editor.");
- logger.info("Connecting to Database...");
- try {
- db = new Database(db_conf.path, { fileMustExist: true });
- logger.info("Connected to Database.");
- } catch (error) {
- logger.error(error.message);
- shutdown();
- }
+
fs.rm(path.join(__dirname, app_conf.temp_directory, "/."), { recursive: true }, (error) => {
if (error == null) {
logger.info("Removed temp directory.");
diff --git a/app/src/css/stylesheet.css b/src/css/stylesheet.css
similarity index 100%
rename from app/src/css/stylesheet.css
rename to src/css/stylesheet.css
diff --git a/app/src/javascript/add.js b/src/javascript/add.js
similarity index 100%
rename from app/src/javascript/add.js
rename to src/javascript/add.js
diff --git a/app/src/javascript/edit.js b/src/javascript/edit.js
similarity index 100%
rename from app/src/javascript/edit.js
rename to src/javascript/edit.js
diff --git a/app/src/javascript/index.js b/src/javascript/index.js
similarity index 94%
rename from app/src/javascript/index.js
rename to src/javascript/index.js
index 9b8e368..889e85e 100644
--- a/app/src/javascript/index.js
+++ b/src/javascript/index.js
@@ -49,7 +49,7 @@ setEventListeners(download, () => {
});
setEventListeners(logout, () => {
- window.location.href = `${window.location.protocol}//log:out@${window.location.host}`;
+ window.location.href = `${window.location.protocol}//logout@${window.location.host}/`;
});
for (let i = 1; i < sql_rows.length; i++) {