From fd95d41d9f2f4f8dd9d012f8d6aa3d647996deed Mon Sep 17 00:00:00 2001 From: LouisLam Date: Sun, 19 Sep 2021 19:04:51 +0800 Subject: [PATCH] [status page] many update and save group list --- .eslintrc.js | 1 + db/patch-group-table.sql | 30 ++++ db/patch-incident-table.sql | 1 + db/patch-monitor-public-weight.sql | 6 - db/patch-monitor-public.sql | 6 - server/check-version.js | 2 +- server/database.js | 34 ++-- server/model/group.js | 33 ++++ server/model/incident.js | 1 + server/model/monitor.js | 6 +- server/routers/api-router.js | 17 +- .../status-page-socket-handler.js | 72 ++++++++- src/mixins/public.js | 8 +- src/pages/StatusPage.vue | 44 +++++- src/util-frontend.js | 8 +- src/util.js | 146 +++++++++--------- 16 files changed, 288 insertions(+), 127 deletions(-) create mode 100644 db/patch-group-table.sql delete mode 100644 db/patch-monitor-public-weight.sql delete mode 100644 db/patch-monitor-public.sql create mode 100644 server/model/group.js diff --git a/.eslintrc.js b/.eslintrc.js index 35c8b5c7..8b45337f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -17,6 +17,7 @@ module.exports = { requireConfigFile: false, }, rules: { + "linebreak-style": ["error", "unix"], "camelcase": ["warn", { "properties": "never", "ignoreImports": true diff --git a/db/patch-group-table.sql b/db/patch-group-table.sql new file mode 100644 index 00000000..46b74660 --- /dev/null +++ b/db/patch-group-table.sql @@ -0,0 +1,30 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +create table `group` +( + id INTEGER not null + constraint group_pk + primary key autoincrement, + name VARCHAR(255) not null, + created_date DATETIME default (DATETIME('now')) not null, + public BOOLEAN default 0 not null, + active BOOLEAN default 1 not null, + weight BOOLEAN NOT NULL DEFAULT 1000 +); + +CREATE TABLE [monitor_group] +( + [id] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + [monitor_id] INTEGER NOT NULL REFERENCES [group] ([id]) ON DELETE CASCADE ON UPDATE CASCADE REFERENCES [monitor] ([id]) ON DELETE CASCADE ON UPDATE CASCADE, + [group_id] INTEGER NOT NULL, + weight BOOLEAN NOT NULL DEFAULT 1000 +); + +CREATE INDEX [fk] + ON [monitor_group] ( + [monitor_id], + [group_id]); + + +COMMIT; diff --git a/db/patch-incident-table.sql b/db/patch-incident-table.sql index a0598e4a..531cfb38 100644 --- a/db/patch-incident-table.sql +++ b/db/patch-incident-table.sql @@ -10,6 +10,7 @@ create table incident content TEXT not null, style VARCHAR(30) default 'warning' not null, created_date DATETIME default (DATETIME('now')) not null, + last_updated_date DATETIME, pin BOOLEAN default 1 not null, active BOOLEAN default 1 not null ); diff --git a/db/patch-monitor-public-weight.sql b/db/patch-monitor-public-weight.sql deleted file mode 100644 index a24e30cb..00000000 --- a/db/patch-monitor-public-weight.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN TRANSACTION; - -alter table monitor - add public_weight BOOLEAN default 1000 not null; - -COMMIT; diff --git a/db/patch-monitor-public.sql b/db/patch-monitor-public.sql deleted file mode 100644 index d554547c..00000000 --- a/db/patch-monitor-public.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN TRANSACTION; - -alter table monitor - add public BOOLEAN default 0 not null; - -COMMIT; diff --git a/server/check-version.js b/server/check-version.js index 96e8aecf..3ac2eee4 100644 --- a/server/check-version.js +++ b/server/check-version.js @@ -18,7 +18,7 @@ exports.startInterval = () => { // For debug if (process.env.TEST_CHECK_VERSION === "1") { - res.data.version = "1000.0.0" + res.data.version = "1000.0.0"; } exports.latestVersion = res.data.version; diff --git a/server/database.js b/server/database.js index 847203ca..39118bb8 100644 --- a/server/database.js +++ b/server/database.js @@ -30,11 +30,10 @@ class Database { static patchList = { "patch-setting-value-type.sql": true, "patch-improve-performance.sql": true, - "patch-monitor-public.sql": true, "patch-2fa.sql": true, "patch-add-retry-interval-monitor.sql": true, - "patch-monitor-public-weight.sql": true, "patch-incident-table.sql": true, + "patch-group-table.sql": true, } /** @@ -65,7 +64,7 @@ class Database { } // Auto map the model to a bean object - R.freeze(true) + R.freeze(true); await R.autoloadModels("./server/model"); // Change to WAL @@ -92,7 +91,7 @@ class Database { } else if (version > this.latestVersion) { console.info("Warning: Database version is newer than expected"); } else { - console.info("Database patch is needed") + console.info("Database patch is needed"); this.backup(version); @@ -107,11 +106,12 @@ class Database { } } catch (ex) { await Database.close(); - this.restore(); - console.error(ex) - console.error("Start Uptime-Kuma failed due to patch db failed") - console.error("Please submit the bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues") + console.error(ex); + console.error("Start Uptime-Kuma failed due to patch db failed"); + console.error("Please submit the bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues"); + + this.restore(); process.exit(1); } } @@ -136,7 +136,7 @@ class Database { try { for (let sqlFilename in this.patchList) { - await this.patch2Recursion(sqlFilename, databasePatchedFiles) + await this.patch2Recursion(sqlFilename, databasePatchedFiles); } if (this.patched) { @@ -145,11 +145,13 @@ class Database { } catch (ex) { await Database.close(); - this.restore(); - console.error(ex) + console.error(ex); console.error("Start Uptime-Kuma failed due to patch db failed"); console.error("Please submit the bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues"); + + this.restore(); + process.exit(1); } @@ -189,7 +191,7 @@ class Database { console.log(sqlFilename + " is patched successfully"); } else { - console.log(sqlFilename + " is already patched, skip"); + debug(sqlFilename + " is already patched, skip"); } } @@ -207,12 +209,12 @@ class Database { // Remove all comments (--) let lines = text.split("\n"); lines = lines.filter((line) => { - return ! line.startsWith("--") + return ! line.startsWith("--"); }); // Split statements by semicolon // Filter out empty line - text = lines.join("\n") + text = lines.join("\n"); let statements = text.split(";") .map((statement) => { @@ -220,7 +222,7 @@ class Database { }) .filter((statement) => { return statement !== ""; - }) + }); for (let statement of statements) { await R.exec(statement); @@ -266,7 +268,7 @@ class Database { */ static backup(version) { if (! this.backupPath) { - console.info("Backup the db") + console.info("Backup the db"); this.backupPath = this.dataDir + "kuma.db.bak" + version; fs.copyFileSync(Database.path, this.backupPath); diff --git a/server/model/group.js b/server/model/group.js new file mode 100644 index 00000000..5bde6721 --- /dev/null +++ b/server/model/group.js @@ -0,0 +1,33 @@ +const { BeanModel } = require("redbean-node/dist/bean-model"); +const { R } = require("redbean-node"); + +class Group extends BeanModel { + + async toPublicJSON() { + + let monitorBeanList = R.convertToBeans("monitor", await R.getAll(` + SELECT * FROM monitor, monitor_group + WHERE monitor.id = monitor_group.monitor_id + AND group_id = ? + `, [ + this.id, + ])); + + console.log(monitorBeanList); + + let monitorList = []; + + for (let bean of monitorBeanList) { + monitorList.push(await bean.toPublicJSON()); + } + + return { + id: this.id, + name: this.name, + weight: this.weight, + monitorList, + }; + } +} + +module.exports = Group; diff --git a/server/model/incident.js b/server/model/incident.js index 5f232751..89c117e9 100644 --- a/server/model/incident.js +++ b/server/model/incident.js @@ -10,6 +10,7 @@ class Incident extends BeanModel { content: this.content, pin: this.pin, createdDate: this.createdDate, + lastUpdatedDate: this.lastUpdatedDate, }; } } diff --git a/server/model/monitor.js b/server/model/monitor.js index 2920ce7c..48c31714 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -26,8 +26,10 @@ class Monitor extends BeanModel { * Only show necessary data to public */ async toPublicJSON() { - // TODO Only show necessary - return this.toJSON(); + return { + id: this.id, + name: this.name, + }; } /** diff --git a/server/routers/api-router.js b/server/routers/api-router.js index 1d02e710..f8f36bbd 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -41,7 +41,7 @@ router.get("/api/status-page/incident", async (_, response) => { response.json({ ok: true, incident: (await R.findOne("incident", " pin = 1 AND active = 1")).toPublicJSON(), - }) + }); } catch (error) { send403(response, error.message); @@ -55,15 +55,14 @@ router.get("/api/status-page/monitor-list", async (_request, response) => { try { await checkPublished(); - const monitorList = {}; - let list = await R.find("monitor", " public = 1 ORDER BY weight DESC, name ", [ - ]); + const publicGroupList = []; + let list = await R.find("group", " public = 1 ORDER BY weight, name "); - for (let monitor of list) { - monitorList[monitor.id] = await monitor.toJSON(); + for (let groupBean of list) { + publicGroupList.push(await groupBean.toPublicJSON()); } - response.json(monitorList); + response.json(publicGroupList); } catch (error) { send403(response, error.message); @@ -79,7 +78,7 @@ router.get("/api/status-page/heartbeat", async (_request, response) => { const monitorList = {}; let list = await R.find("", " ", [ - ]) + ]); for (let monitor of list) { monitorList[monitor.id] = await monitor.toJSON(); @@ -126,7 +125,7 @@ function send403(res, msg = "") { res.status(403).json({ "status": "fail", "msg": msg, - }) + }); } module.exports = router; diff --git a/server/socket-handlers/status-page-socket-handler.js b/server/socket-handlers/status-page-socket-handler.js index 27c8846f..34f9b6c6 100644 --- a/server/socket-handlers/status-page-socket-handler.js +++ b/server/socket-handlers/status-page-socket-handler.js @@ -1,6 +1,7 @@ const { R } = require("redbean-node"); const { checkLogin } = require("../util-server"); const dayjs = require("dayjs"); +const { debug } = require("../../src/util"); module.exports.statusPageSocketHandler = (socket) => { @@ -27,7 +28,13 @@ module.exports.statusPageSocketHandler = (socket) => { incidentBean.content = incident.content; incidentBean.style = incident.style; incidentBean.pin = true; - incidentBean.createdDate = R.isoDateTime(dayjs.utc()); + + if (incident.id) { + incidentBean.lastUpdatedDate = R.isoDateTime(dayjs.utc()); + } else { + incidentBean.createdDate = R.isoDateTime(dayjs.utc()); + } + await R.store(incidentBean); callback({ @@ -58,4 +65,67 @@ module.exports.statusPageSocketHandler = (socket) => { }); } }); + + // Save Status Page + socket.on("saveStatusPage", async (publicGroupList, callback) => { + + try { + checkLogin(socket); + + await R.transaction(async (trx) => { + const groupIDList = []; + let groupOrder = 1; + + for (let group of publicGroupList) { + let groupBean; + if (group.id) { + groupBean = await trx.findOne("group", " id = ? AND public = 1 ", [ + group.id + ]); + } else { + groupBean = R.dispense("group"); + } + + groupBean.name = group.name; + groupBean.public = true; + groupBean.weight = groupOrder++; + + await trx.store(groupBean); + + await trx.exec("DELETE FROM monitor_group WHERE group_id = ? ", [ + groupBean.id + ]); + + let monitorOrder = 1; + for (let monitor of group.monitorList) { + let relationBean = R.dispense("monitor_group"); + relationBean.weight = monitorOrder++; + relationBean.group_id = groupBean.id; + relationBean.monitor_id = monitor.id; + await trx.store(relationBean); + } + + groupIDList.push(groupBean.id); + group.id = groupBean.id; + } + + // Delete groups that not in the list + debug("Delete groups that not in the list"); + const slots = groupIDList.map(() => "?").join(","); + await trx.exec(`DELETE FROM \`group\` WHERE id NOT IN (${slots})`, groupIDList); + + callback({ + ok: true, + publicGroupList, + }); + }); + } catch (error) { + + callback({ + ok: false, + msg: error.message, + }); + } + }); + }; diff --git a/src/mixins/public.js b/src/mixins/public.js index 333010da..c0fffced 100644 --- a/src/mixins/public.js +++ b/src/mixins/public.js @@ -11,12 +11,14 @@ export default { data() { return { publicGroupList: [], - } + }; }, computed: { publicMonitorList() { let result = {}; + console.log(this.publicGroupList); + for (let group of this.publicGroupList) { for (let monitor of group.monitorList) { result[monitor.id] = monitor; @@ -26,7 +28,7 @@ export default { }, publicLastHeartbeatList() { - let result = {} + let result = {}; for (let monitorID in this.publicMonitorList) { if (this.lastHeartbeatList[monitorID]) { @@ -37,4 +39,4 @@ export default { return result; }, } -} +}; diff --git a/src/pages/StatusPage.vue b/src/pages/StatusPage.vue index 5644523a..4c02edca 100644 --- a/src/pages/StatusPage.vue +++ b/src/pages/StatusPage.vue @@ -37,7 +37,7 @@
- @@ -52,6 +52,7 @@ {{ $t("Create Incident") }} + @@ -84,6 +85,14 @@ {{ $t("Content") }}: + +
+ Created: {{ incident.createdDate }} ({{ createdDateFromNow }})
+ + Last Updated: {{ incident.lastUpdatedDate }} ({{ lastUpdatedDateFromNow }}) + +
+