From 5e609da0d5772bfae9b3898bdde8d8595af7c3a2 Mon Sep 17 00:00:00 2001 From: LLeny Date: Fri, 11 Feb 2022 14:25:07 +0800 Subject: [PATCH] Implementing #928 with a cron expression. Limited code and workflow changes. --- db/patch-monitor-active-cron.sql | 7 +++++++ server/database.js | 1 + server/model/monitor.js | 22 ++++++++++++++++++++-- server/server.js | 2 ++ src/languages/en.js | 2 ++ src/languages/fr-FR.js | 2 ++ src/languages/zh-HK.js | 2 ++ src/pages/EditMonitor.vue | 14 +++++++++++++- 8 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 db/patch-monitor-active-cron.sql diff --git a/db/patch-monitor-active-cron.sql b/db/patch-monitor-active-cron.sql new file mode 100644 index 0000000000..f9e08e79de --- /dev/null +++ b/db/patch-monitor-active-cron.sql @@ -0,0 +1,7 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD cron VARCHAR(99) DEFAULT NULL; + +COMMIT; diff --git a/server/database.js b/server/database.js index afcace705b..99c5c8fb8b 100644 --- a/server/database.js +++ b/server/database.js @@ -53,6 +53,7 @@ class Database { "patch-2fa-invalidate-used-token.sql": true, "patch-notification_sent_history.sql": true, "patch-monitor-basic-auth.sql": true, + "patch-monitor-active-cron.sql": true, } /** diff --git a/server/model/monitor.js b/server/model/monitor.js index c4441d63e7..8e10c969ba 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -14,6 +14,7 @@ const { Notification } = require("../notification"); const { demoMode } = require("../config"); const version = require("../../package.json").version; const apicache = require("../modules/apicache"); +const later = require("@breejs/later"); /** * status: @@ -79,6 +80,7 @@ class Monitor extends BeanModel { pushToken: this.pushToken, notificationIDList, tags: tags, + cron: this.cron, }; } @@ -377,7 +379,7 @@ class Monitor extends BeanModel { } } - let beatInterval = this.interval; + let beatInterval = this.getNextInterval(); debug(`[${this.name}] Check isImportant`); let isImportant = Monitor.isImportantBeat(isFirstBeat, previousBeat?.status, bean.status); @@ -457,12 +459,28 @@ class Monitor extends BeanModel { if (this.type === "push") { setTimeout(() => { safeBeat(); - }, this.interval * 1000); + }, this.getNextInterval() * 1000); } else { safeBeat(); } } + getNextInterval() { + if (!this.cron || /^\s*$/.test(this.cron)) { + return this.interval; + } + const laterCron = later.parse.cron(this.cron); + const nextOccurences = later.schedule(laterCron).next(2); + + let calculatedInterval = Math.floor((nextOccurences[0].getTime() - Date.now()) / 1000); + if (calculatedInterval < 20) { //beat interval too low, using next interval + calculatedInterval = Math.floor((nextOccurences[1].getTime() - Date.now()) / 1000); + } + + console.log(`Monitor #${this.id} '${this.name}': getNextInterval - calculatedInterval: ${calculatedInterval} `); + return calculatedInterval; + } + stop() { clearTimeout(this.heartbeatInterval); this.isStop = true; diff --git a/server/server.js b/server/server.js index 153cac4fdf..ae56ee75a7 100644 --- a/server/server.js +++ b/server/server.js @@ -599,6 +599,7 @@ exports.entryPage = "dashboard"; bean.dns_resolve_type = monitor.dns_resolve_type; bean.dns_resolve_server = monitor.dns_resolve_server; bean.pushToken = monitor.pushToken; + bean.cron = monitor.cron; await R.store(bean); @@ -1165,6 +1166,7 @@ exports.entryPage = "dashboard"; dns_resolve_type: monitorListData[i].dns_resolve_type, dns_resolve_server: monitorListData[i].dns_resolve_server, notificationIDList: {}, + cron: monitorListData[i].cron, }; if (monitorListData[i].pushToken) { diff --git a/src/languages/en.js b/src/languages/en.js index 47513466c7..f6d01ea13f 100644 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -361,4 +361,6 @@ export default { smtpDkimHashAlgo: "Hash Algorithm (Optional)", smtpDkimheaderFieldNames: "Header Keys to sign (Optional)", smtpDkimskipFields: "Header Keys not to sign (Optional)", + Cron: "Cron scheduler", + cronDescription: "Schedule the monitor using a cron expression, overrides Heartbeat Interval", }; diff --git a/src/languages/fr-FR.js b/src/languages/fr-FR.js index 04dede1b50..c6b92848b5 100644 --- a/src/languages/fr-FR.js +++ b/src/languages/fr-FR.js @@ -304,4 +304,6 @@ export default { steamApiKeyDescription: "Pour surveiller un serveur Steam, vous avez besoin d'une clé Steam Web-API. Vous pouvez enregistrer votre clé ici : ", "Current User": "Utilisateur actuel", recent: "Récent", + Cron: "Cron planificateur", + cronDescription: "Planifie le monitor en utilisant une expression cron, outrepasse Intervalle de vérification", }; diff --git a/src/languages/zh-HK.js b/src/languages/zh-HK.js index 7f5e2259b9..658307537b 100644 --- a/src/languages/zh-HK.js +++ b/src/languages/zh-HK.js @@ -198,4 +198,6 @@ export default { pushbullet: "Pushbullet", line: "Line Messenger", mattermost: "Mattermost", + Cron: "Cron 時間表", + cronDescription: "用cron表達式設定監測器時間表,覆載間距", }; diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 4b6a920c82..1ef846d02e 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -121,6 +121,15 @@ + +
+ + +
+ {{ $t("cronDescription") }} +
+
+
@@ -321,7 +330,9 @@ export default { // Source: https://digitalfortress.tech/tips/top-15-commonly-used-regex/ ipRegexPattern: "((^\\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\\s*$)|(^\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*$))", // Source: https://stackoverflow.com/questions/106179/regular-expression-to-match-dns-hostname-or-ip-address - hostnameRegexPattern: "^(([a-zA-Z0-9_]|[a-zA-Z0-9_][a-zA-Z0-9\\-_]*[a-zA-Z0-9_])\\.)*([A-Za-z0-9_]|[A-Za-z0-9_][A-Za-z0-9\\-_]*[A-Za-z0-9_])$" + hostnameRegexPattern: "^(([a-zA-Z0-9_]|[a-zA-Z0-9_][a-zA-Z0-9\\-_]*[a-zA-Z0-9_])\\.)*([A-Za-z0-9_]|[A-Za-z0-9_][A-Za-z0-9\\-_]*[A-Za-z0-9_])$", + // Source: https://stackoverflow.com/questions/14203122/create-a-regular-expression-for-cron-statement/19046939 + cronRegex: "(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\\d+(ns|us|µs|ms|s|m|h))+)|((((\\d+,)+\\d+|(\\d+(\\/|-)\\d+)|\\d+|\\*) ?){5,7})", }; }, @@ -439,6 +450,7 @@ export default { accepted_statuscodes: ["200-299"], dns_resolve_type: "A", dns_resolve_server: "1.1.1.1", + cron: "", }; for (let i = 0; i < this.$root.notificationList.length; i++) {