From 645fd94bba62990e3031c9571c92fcb4e1cd481e Mon Sep 17 00:00:00 2001 From: Peace Date: Sat, 28 Jan 2023 02:58:03 +0100 Subject: [PATCH 01/18] feat: add ability to group monitors in dashboard --- db/patch-add-parent-monitor.sql | 6 + server/database.js | 1 + server/model/monitor.js | 93 ++++++++++++++ server/server.js | 1 + src/components/MonitorList.vue | 55 +++----- src/components/MonitorListItem.vue | 196 +++++++++++++++++++++++++++++ src/lang/de-DE.json | 4 +- src/lang/en.json | 4 +- src/pages/Details.vue | 8 ++ src/pages/EditMonitor.vue | 56 +++++++++ src/pages/StatusPage.vue | 31 ++++- 11 files changed, 411 insertions(+), 44 deletions(-) create mode 100644 db/patch-add-parent-monitor.sql create mode 100644 src/components/MonitorListItem.vue diff --git a/db/patch-add-parent-monitor.sql b/db/patch-add-parent-monitor.sql new file mode 100644 index 000000000..756ac5be2 --- /dev/null +++ b/db/patch-add-parent-monitor.sql @@ -0,0 +1,6 @@ +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD parent INTEGER REFERENCES [monitor] ([id]) ON DELETE SET NULL ON UPDATE CASCADE; + +COMMIT diff --git a/server/database.js b/server/database.js index 5ca88cac5..eef35799c 100644 --- a/server/database.js +++ b/server/database.js @@ -68,6 +68,7 @@ class Database { "patch-ping-packet-size.sql": true, "patch-maintenance-table2.sql": true, "patch-add-gamedig-monitor.sql": true, + "patch-add-parent-monitor.sql": true, }; /** diff --git a/server/model/monitor.js b/server/model/monitor.js index e06c15e2a..f2a1c40e5 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -72,6 +72,9 @@ class Monitor extends BeanModel { let data = { id: this.id, name: this.name, + pathName: await this.getPathName(), + parent: this.parent, + childrenIDs: await Monitor.getAllChildrenIDs(this.id), url: this.url, method: this.method, hostname: this.hostname, @@ -253,6 +256,25 @@ class Monitor extends BeanModel { if (await Monitor.isUnderMaintenance(this.id)) { bean.msg = "Monitor under maintenance"; bean.status = MAINTENANCE; + } else if (this.type === "group") { + const children = await Monitor.getChildren(this.id); + + bean.status = UP; + bean.msg = "All childs up and running"; + for (const child of children) { + const lastBeat = await Monitor.getPreviousHeartbeat(child.id); + + // Only change state if the monitor is in worse conditions then the ones before + if (bean.status === UP && (lastBeat.status === PENDING || lastBeat.status === DOWN)) { + bean.status = lastBeat.status; + } else if (bean.status === PENDING && lastBeat.status === DOWN) { + bean.status = lastBeat.status; + } + } + + if (bean.status !== UP) { + bean.msg = "Child inaccessible"; + } } else if (this.type === "http" || this.type === "keyword") { // Do not do any queries/high loading things before the "bean.ping" let startTime = dayjs().valueOf(); @@ -1283,6 +1305,77 @@ class Monitor extends BeanModel { throw new Error(`Interval cannot be less than ${MIN_INTERVAL_SECOND} seconds`); } } + + /** + * Gets Parent of the monitor + * @param {number} monitorID ID of monitor to get + * @returns {Promise>} + */ + static async getParent(monitorID) { + return await R.getRow(` + SELECT parent.* FROM monitor parent + LEFT JOIN monitor child + ON child.parent = parent.id + WHERE child.id = ? + `, [ + monitorID, + ]); + } + + /** + * Gets all Children of the monitor + * @param {number} monitorID ID of monitor to get + * @returns {Promise>} + */ + static async getChildren(monitorID) { + return await R.getAll(` + SELECT * FROM monitor + WHERE parent = ? + `, [ + monitorID, + ]); + } + + /** + * Gets Full Path-Name (Groups and Name) + * @returns {Promise} + */ + async getPathName() { + let path = this.name; + + if (this.parent === null) { + return path; + } + + let parent = await Monitor.getParent(this.id); + while (parent !== null) { + path = `${parent.name} / ${path}`; + parent = await Monitor.getParent(parent.id); + } + + return path; + } + + /** + * Gets recursive all child ids + * @returns {Promise} + */ + static async getAllChildrenIDs(monitorID) { + const childs = await Monitor.getChildren(monitorID); + + if (childs === null) { + return []; + } + + let childrenIDs = []; + + for (const child of childs) { + childrenIDs.push(child.id); + childrenIDs = childrenIDs.concat(await Monitor.getAllChildrenIDs(child.id)); + } + + return childrenIDs; + } } module.exports = Monitor; diff --git a/server/server.js b/server/server.js index 9c18fcd1d..2d02a430a 100644 --- a/server/server.js +++ b/server/server.js @@ -678,6 +678,7 @@ let needSetup = false; server.monitorList[monitor.id]?.prometheus()?.remove(); bean.name = monitor.name; + bean.parent = monitor.parent; bean.type = monitor.type; bean.url = monitor.url; bean.method = monitor.method; diff --git a/src/components/MonitorList.vue b/src/components/MonitorList.vue index 115660a5e..f9ec1d4e3 100644 --- a/src/components/MonitorList.vue +++ b/src/components/MonitorList.vue @@ -19,43 +19,18 @@ {{ $t("No Monitors, please") }} {{ $t("add one") }} - -
-
-
- - {{ item.name }} -
-
- -
-
-
- -
-
- -
-
- -
-
-
+ + + diff --git a/src/lang/de-DE.json b/src/lang/de-DE.json index 45b5ae56a..1da18dab5 100644 --- a/src/lang/de-DE.json +++ b/src/lang/de-DE.json @@ -643,5 +643,7 @@ "Custom": "Benutzerdefiniert", "Enable DNS Cache": "DNS Cache aktivieren", "Enable": "Aktivieren", - "Disable": "Deaktivieren" + "Disable": "Deaktivieren", + "Group": "Gruppe", + "Monitor Group": "Monitor Gruppe" } diff --git a/src/lang/en.json b/src/lang/en.json index f2f166938..c332e86b4 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -682,5 +682,7 @@ "onebotUserOrGroupId": "Group/User ID", "onebotSafetyTips": "For safety, must set access token", "PushDeer Key": "PushDeer Key", - "wayToGetClickSendSMSToken": "You can get API Username and API Key from {0} ." + "wayToGetClickSendSMSToken": "You can get API Username and API Key from {0} .", + "Group": "Group", + "Monitor Group": "Monitor Group" } diff --git a/src/pages/Details.vue b/src/pages/Details.vue index 8325caa45..b4aae8fc9 100644 --- a/src/pages/Details.vue +++ b/src/pages/Details.vue @@ -1,6 +1,7 @@