From 1ac904d6d6260a34d08a29249fb7cc15236ee787 Mon Sep 17 00:00:00 2001
From: OidaTiftla <chm.stephan@outlook.com>
Date: Sun, 23 Jan 2022 15:22:57 +0100
Subject: [PATCH 01/27] Introduce resend interval if down

---
 package-lock.json         |  4 ++--
 package.json              |  8 ++++----
 server/model/monitor.js   | 17 +++++++++++++++++
 server/server.js          |  7 +++++++
 src/pages/EditMonitor.vue |  8 ++++++++
 5 files changed, 38 insertions(+), 6 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index fc21a63f..5253c3af 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
     "name": "uptime-kuma",
-    "version": "1.11.3",
+    "version": "1.11.4",
     "lockfileVersion": 2,
     "requires": true,
     "packages": {
         "": {
             "name": "uptime-kuma",
-            "version": "1.11.3",
+            "version": "1.11.4",
             "license": "MIT",
             "dependencies": {
                 "@fortawesome/fontawesome-svg-core": "~1.2.36",
diff --git a/package.json b/package.json
index 048a5e0a..cd522a31 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
     "name": "uptime-kuma",
-    "version": "1.11.3",
+    "version": "1.11.4",
     "license": "MIT",
     "repository": {
         "type": "git",
@@ -30,13 +30,13 @@
         "build-docker": "npm run build && npm run build-docker-debian && npm run build-docker-alpine",
         "build-docker-alpine-base": "docker buildx build -f docker/alpine-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base-alpine . --push",
         "build-docker-debian-base": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base-debian . --push",
-        "build-docker-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:alpine -t louislam/uptime-kuma:1-alpine -t louislam/uptime-kuma:1.11.3-alpine --target release . --push",
-        "build-docker-debian": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:1.11.3 -t louislam/uptime-kuma:debian -t louislam/uptime-kuma:1-debian -t louislam/uptime-kuma:1.11.3-debian --target release . --push",
+        "build-docker-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:alpine -t louislam/uptime-kuma:1-alpine -t louislam/uptime-kuma:1.11.4-alpine --target release . --push",
+        "build-docker-debian": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:1.11.4 -t louislam/uptime-kuma:debian -t louislam/uptime-kuma:1-debian -t louislam/uptime-kuma:1.11.4-debian --target release . --push",
         "build-docker-nightly": "npm run build && docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly --target nightly . --push",
         "build-docker-nightly-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly-alpine --target nightly . --push",
         "build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain",
         "upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain",
-        "setup": "git checkout 1.11.3 && npm ci --production && npm run download-dist",
+        "setup": "git checkout 1.11.4 && npm ci --production && npm run download-dist",
         "download-dist": "node extra/download-dist.js",
         "update-version": "node extra/update-version.js",
         "mark-as-nightly": "node extra/mark-as-nightly.js",
diff --git a/server/model/monitor.js b/server/model/monitor.js
index c4441d63..eaba61ad 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -68,6 +68,7 @@ class Monitor extends BeanModel {
             type: this.type,
             interval: this.interval,
             retryInterval: this.retryInterval,
+            resendInterval: this.resendInterval,
             keyword: this.keyword,
             ignoreTls: this.getIgnoreTls(),
             upsideDown: this.isUpsideDown(),
@@ -135,6 +136,7 @@ class Monitor extends BeanModel {
             bean.monitor_id = this.id;
             bean.time = R.isoDateTime(dayjs.utc());
             bean.status = DOWN;
+            bean.lastNotifiedTime = previousBeat?.lastNotifiedTime || null; // after first update lastNotifiedTime will be undefined
 
             if (this.isUpsideDown()) {
                 bean.status = flipStatus(bean.status);
@@ -390,12 +392,27 @@ class Monitor extends BeanModel {
                 debug(`[${this.name}] sendNotification`);
                 await Monitor.sendNotification(isFirstBeat, this, bean);
 
+                // Set last notified time to now
+                bean.lastNotifiedTime = dayjs().valueOf();
+
                 // Clear Status Page Cache
                 debug(`[${this.name}] apicache clear`);
                 apicache.clear();
 
             } else {
                 bean.important = false;
+
+                if (bean.status === DOWN && this.resendInterval > 0) {
+                    timeSinceLastNotified = dayjs().valueOf() - (bean.lastNotifiedTime || 0);
+                    if (timeSinceLastNotified >= this.resendInterval) {
+                        // Send notification again, because we are still DOWN
+                        debug(`[${this.name}] sendNotification`);
+                        await Monitor.sendNotification(isFirstBeat, this, bean);
+
+                        // Set last notified time to now
+                        bean.lastNotifiedTime = dayjs().valueOf();
+                    }
+                }
             }
 
             if (bean.status === UP) {
diff --git a/server/server.js b/server/server.js
index 153cac4f..5a9cf944 100644
--- a/server/server.js
+++ b/server/server.js
@@ -588,6 +588,7 @@ exports.entryPage = "dashboard";
                 bean.basic_auth_pass = monitor.basic_auth_pass;
                 bean.interval = monitor.interval;
                 bean.retryInterval = monitor.retryInterval;
+                bean.resendInterval = monitor.resendInterval;
                 bean.hostname = monitor.hostname;
                 bean.maxretries = monitor.maxretries;
                 bean.port = monitor.port;
@@ -1082,6 +1083,7 @@ exports.entryPage = "dashboard";
                 let monitorListData = backupData.monitorList;
 
                 let version17x = compareVersions.compare(backupData.version, "1.7.0", ">=");
+                let version1114 = compareVersions.compare(backupData.version, "1.11.4", ">=");
 
                 // If the import option is "overwrite" it'll clear most of the tables, except "settings" and "user"
                 if (importHandle == "overwrite") {
@@ -1131,6 +1133,7 @@ exports.entryPage = "dashboard";
 
                             // Define default values
                             let retryInterval = 0;
+                            let resendInterval = 0;
 
                             /*
                             Only replace the default value with the backup file data for the specific version, where it appears the first time
@@ -1139,6 +1142,9 @@ exports.entryPage = "dashboard";
                             if (version17x) {
                                 retryInterval = monitorListData[i].retryInterval;
                             }
+                            if (version1114) {
+                                resendInterval = monitorListData[i].resendInterval;
+                            }
 
                             // --- End ---
 
@@ -1154,6 +1160,7 @@ exports.entryPage = "dashboard";
                                 basic_auth_pass: monitorListData[i].basic_auth_pass,
                                 interval: monitorListData[i].interval,
                                 retryInterval: retryInterval,
+                                resendInterval: resendInterval,
                                 hostname: monitorListData[i].hostname,
                                 maxretries: monitorListData[i].maxretries,
                                 port: monitorListData[i].port,
diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index 4b6a920c..b95c1098 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -137,6 +137,14 @@
                                 <input id="retry-interval" v-model="monitor.retryInterval" type="number" class="form-control" required min="20" step="1">
                             </div>
 
+                            <div class="my-3">
+                                <label for="resend-interval" class="form-label">
+                                    {{ $t("Notification resend Interval if Down") }}
+                                    <span>({{ $t("resendEverySecond", [ monitor.resendInterval ]) }})</span>
+                                </label>
+                                <input id="resend-interval" v-model="monitor.resendInterval" type="number" class="form-control" required min="20" step="1">
+                            </div>
+
                             <h2 v-if="monitor.type !== 'push'" class="mt-5 mb-2">{{ $t("Advanced") }}</h2>
 
                             <div v-if="monitor.type === 'http' || monitor.type === 'keyword' " class="my-3 form-check">

From b69a8b8493e095842bcf7daceaea21110106146a Mon Sep 17 00:00:00 2001
From: OidaTiftla <oidatiftla@oidatiftla.de>
Date: Sun, 23 Jan 2022 17:35:53 +0100
Subject: [PATCH 02/27] Fix formatting

Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
---
 server/server.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/server/server.js b/server/server.js
index 5a9cf944..0ad361ad 100644
--- a/server/server.js
+++ b/server/server.js
@@ -1142,6 +1142,7 @@ exports.entryPage = "dashboard";
                             if (version17x) {
                                 retryInterval = monitorListData[i].retryInterval;
                             }
+
                             if (version1114) {
                                 resendInterval = monitorListData[i].resendInterval;
                             }

From 65fc71e4858ae61b7e1fb639ca335b8ad4f22ca4 Mon Sep 17 00:00:00 2001
From: OidaTiftla <oidatiftla@oidatiftla.de>
Date: Sun, 23 Jan 2022 17:34:39 +0100
Subject: [PATCH 03/27] Revert version change

---
 package-lock.json | 4 ++--
 package.json      | 8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 5253c3af..fc21a63f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
     "name": "uptime-kuma",
-    "version": "1.11.4",
+    "version": "1.11.3",
     "lockfileVersion": 2,
     "requires": true,
     "packages": {
         "": {
             "name": "uptime-kuma",
-            "version": "1.11.4",
+            "version": "1.11.3",
             "license": "MIT",
             "dependencies": {
                 "@fortawesome/fontawesome-svg-core": "~1.2.36",
diff --git a/package.json b/package.json
index cd522a31..048a5e0a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
     "name": "uptime-kuma",
-    "version": "1.11.4",
+    "version": "1.11.3",
     "license": "MIT",
     "repository": {
         "type": "git",
@@ -30,13 +30,13 @@
         "build-docker": "npm run build && npm run build-docker-debian && npm run build-docker-alpine",
         "build-docker-alpine-base": "docker buildx build -f docker/alpine-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base-alpine . --push",
         "build-docker-debian-base": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base-debian . --push",
-        "build-docker-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:alpine -t louislam/uptime-kuma:1-alpine -t louislam/uptime-kuma:1.11.4-alpine --target release . --push",
-        "build-docker-debian": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:1.11.4 -t louislam/uptime-kuma:debian -t louislam/uptime-kuma:1-debian -t louislam/uptime-kuma:1.11.4-debian --target release . --push",
+        "build-docker-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:alpine -t louislam/uptime-kuma:1-alpine -t louislam/uptime-kuma:1.11.3-alpine --target release . --push",
+        "build-docker-debian": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:1.11.3 -t louislam/uptime-kuma:debian -t louislam/uptime-kuma:1-debian -t louislam/uptime-kuma:1.11.3-debian --target release . --push",
         "build-docker-nightly": "npm run build && docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly --target nightly . --push",
         "build-docker-nightly-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly-alpine --target nightly . --push",
         "build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain",
         "upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain",
-        "setup": "git checkout 1.11.4 && npm ci --production && npm run download-dist",
+        "setup": "git checkout 1.11.3 && npm ci --production && npm run download-dist",
         "download-dist": "node extra/download-dist.js",
         "update-version": "node extra/update-version.js",
         "mark-as-nightly": "node extra/mark-as-nightly.js",

From 11e9eee09d45996d476168d8c646962eb6104bd1 Mon Sep 17 00:00:00 2001
From: OidaTiftla <oidatiftla@oidatiftla.de>
Date: Sun, 23 Jan 2022 17:48:09 +0100
Subject: [PATCH 04/27] Change seconds to minutes

---
 server/model/monitor.js   | 2 +-
 src/languages/en.js       | 1 +
 src/pages/EditMonitor.vue | 3 ++-
 3 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/server/model/monitor.js b/server/model/monitor.js
index eaba61ad..f4803355 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -403,7 +403,7 @@ class Monitor extends BeanModel {
                 bean.important = false;
 
                 if (bean.status === DOWN && this.resendInterval > 0) {
-                    timeSinceLastNotified = dayjs().valueOf() - (bean.lastNotifiedTime || 0);
+                    timeSinceLastNotified = (dayjs().valueOf() - (bean.lastNotifiedTime || 0)) / 60; // divide by 60 to convert from seconds to minutes
                     if (timeSinceLastNotified >= this.resendInterval) {
                         // Send notification again, because we are still DOWN
                         debug(`[${this.name}] sendNotification`);
diff --git a/src/languages/en.js b/src/languages/en.js
index 47513466..21e215f7 100644
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -2,6 +2,7 @@ export default {
     languageName: "English",
     checkEverySecond: "Check every {0} seconds",
     retryCheckEverySecond: "Retry every {0} seconds",
+    resendEveryMinute: "Resend every {0} minutes if DOWN",
     retriesDescription: "Maximum retries before the service is marked as down and a notification is sent",
     ignoreTLSError: "Ignore TLS/SSL error for HTTPS websites",
     upsideDownModeDescription: "Flip the status upside down. If the service is reachable, it is DOWN.",
diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index b95c1098..3b4cbcf1 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -140,7 +140,7 @@
                             <div class="my-3">
                                 <label for="resend-interval" class="form-label">
                                     {{ $t("Notification resend Interval if Down") }}
-                                    <span>({{ $t("resendEverySecond", [ monitor.resendInterval ]) }})</span>
+                                    <span>({{ $t("resendEveryMinute", [ monitor.resendInterval ]) }})</span>
                                 </label>
                                 <input id="resend-interval" v-model="monitor.resendInterval" type="number" class="form-control" required min="20" step="1">
                             </div>
@@ -439,6 +439,7 @@ export default {
                     method: "GET",
                     interval: 60,
                     retryInterval: this.interval,
+                    resendInterval: 0,
                     maxretries: 0,
                     notificationIDList: {},
                     ignoreTls: false,

From f931e709e638f0720309d1c59cbf17fe34b622a1 Mon Sep 17 00:00:00 2001
From: OidaTiftla <oidatiftla@oidatiftla.de>
Date: Mon, 24 Jan 2022 09:18:12 +0100
Subject: [PATCH 05/27] Add database patch

---
 db/patch-monitor-add-resend-interval.sql | 7 +++++++
 server/database.js                       | 1 +
 2 files changed, 8 insertions(+)
 create mode 100644 db/patch-monitor-add-resend-interval.sql

diff --git a/db/patch-monitor-add-resend-interval.sql b/db/patch-monitor-add-resend-interval.sql
new file mode 100644
index 00000000..e8bb08b8
--- /dev/null
+++ b/db/patch-monitor-add-resend-interval.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 resend_interval INTEGER default 0 not null;
+
+COMMIT;
diff --git a/server/database.js b/server/database.js
index afcace70..ce4d5089 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-add-resend-interval.sql": true,
     }
 
     /**

From 8c4ab9d652d931c44ffbc434c4d8599ee2aa5cd3 Mon Sep 17 00:00:00 2001
From: OidaTiftla <oidatiftla@oidatiftla.de>
Date: Mon, 24 Jan 2022 09:18:22 +0100
Subject: [PATCH 06/27] Simplify

---
 src/languages/en.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/languages/en.js b/src/languages/en.js
index 21e215f7..33ad0a42 100644
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -2,7 +2,7 @@ export default {
     languageName: "English",
     checkEverySecond: "Check every {0} seconds",
     retryCheckEverySecond: "Retry every {0} seconds",
-    resendEveryMinute: "Resend every {0} minutes if DOWN",
+    resendEveryMinute: "Resend every {0} minutes",
     retriesDescription: "Maximum retries before the service is marked as down and a notification is sent",
     ignoreTLSError: "Ignore TLS/SSL error for HTTPS websites",
     upsideDownModeDescription: "Flip the status upside down. If the service is reachable, it is DOWN.",

From 30ce53f57c3ada92b700c14cba6be822f687ee55 Mon Sep 17 00:00:00 2001
From: OidaTiftla <oidatiftla@oidatiftla.de>
Date: Mon, 24 Jan 2022 09:18:38 +0100
Subject: [PATCH 07/27] Fix min value of resend interval

---
 src/pages/EditMonitor.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index 3b4cbcf1..a297c54b 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -142,7 +142,7 @@
                                     {{ $t("Notification resend Interval if Down") }}
                                     <span>({{ $t("resendEveryMinute", [ monitor.resendInterval ]) }})</span>
                                 </label>
-                                <input id="resend-interval" v-model="monitor.resendInterval" type="number" class="form-control" required min="20" step="1">
+                                <input id="resend-interval" v-model="monitor.resendInterval" type="number" class="form-control" required min="0" step="1">
                             </div>
 
                             <h2 v-if="monitor.type !== 'push'" class="mt-5 mb-2">{{ $t("Advanced") }}</h2>

From f390a8caf1fea00348a3245b4c79d4315c125f3a Mon Sep 17 00:00:00 2001
From: OidaTiftla <oidatiftla@oidatiftla.de>
Date: Mon, 24 Jan 2022 21:59:25 +0100
Subject: [PATCH 08/27] Fix missing DB patch and use DATETIME as column format

---
 db/patch-heartbeat-add-last-notified-time.sql |  7 +++++++
 server/database.js                            |  1 +
 server/model/monitor.js                       | 10 +++++-----
 3 files changed, 13 insertions(+), 5 deletions(-)
 create mode 100644 db/patch-heartbeat-add-last-notified-time.sql

diff --git a/db/patch-heartbeat-add-last-notified-time.sql b/db/patch-heartbeat-add-last-notified-time.sql
new file mode 100644
index 00000000..af9c21c0
--- /dev/null
+++ b/db/patch-heartbeat-add-last-notified-time.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 heartbeat
+    ADD last_notified_time DATETIME default null;
+
+COMMIT;
diff --git a/server/database.js b/server/database.js
index ce4d5089..0aae8ffc 100644
--- a/server/database.js
+++ b/server/database.js
@@ -54,6 +54,7 @@ class Database {
         "patch-notification_sent_history.sql": true,
         "patch-monitor-basic-auth.sql": true,
         "patch-monitor-add-resend-interval.sql": true,
+        "patch-heartbeat-add-last-notified-time.sql": true,
     }
 
     /**
diff --git a/server/model/monitor.js b/server/model/monitor.js
index f4803355..85a0e944 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -136,7 +136,7 @@ class Monitor extends BeanModel {
             bean.monitor_id = this.id;
             bean.time = R.isoDateTime(dayjs.utc());
             bean.status = DOWN;
-            bean.lastNotifiedTime = previousBeat?.lastNotifiedTime || null; // after first update lastNotifiedTime will be undefined
+            bean.lastNotifiedTime = previousBeat?.lastNotifiedTime;
 
             if (this.isUpsideDown()) {
                 bean.status = flipStatus(bean.status);
@@ -393,7 +393,7 @@ class Monitor extends BeanModel {
                 await Monitor.sendNotification(isFirstBeat, this, bean);
 
                 // Set last notified time to now
-                bean.lastNotifiedTime = dayjs().valueOf();
+                bean.lastNotifiedTime = R.isoDateTime(dayjs.utc());
 
                 // Clear Status Page Cache
                 debug(`[${this.name}] apicache clear`);
@@ -403,14 +403,14 @@ class Monitor extends BeanModel {
                 bean.important = false;
 
                 if (bean.status === DOWN && this.resendInterval > 0) {
-                    timeSinceLastNotified = (dayjs().valueOf() - (bean.lastNotifiedTime || 0)) / 60; // divide by 60 to convert from seconds to minutes
+                    let timeSinceLastNotified = (dayjs.utc().valueOf() - (bean.lastNotifiedTime == null ? 0 : dayjs.utc(bean.lastNotifiedTime).valueOf())) / 1000 / 60; // divide by 1000 to convert from milliseconds to seconds and divide by 60 to convert from seconds to minutes
                     if (timeSinceLastNotified >= this.resendInterval) {
                         // Send notification again, because we are still DOWN
-                        debug(`[${this.name}] sendNotification`);
+                        debug(`[${this.name}] sendNotification again: lastNotifiedTime: ${bean.lastNotifiedTime} | current time: ${R.isoDateTime(dayjs.utc())}`);
                         await Monitor.sendNotification(isFirstBeat, this, bean);
 
                         // Set last notified time to now
-                        bean.lastNotifiedTime = dayjs().valueOf();
+                        bean.lastNotifiedTime = R.isoDateTime(dayjs.utc());
                     }
                 }
             }

From 855b12f435ca87059c2797b8695418947fc9b73e Mon Sep 17 00:00:00 2001
From: OidaTiftla <oidatiftla@oidatiftla.de>
Date: Mon, 24 Jan 2022 22:20:38 +0100
Subject: [PATCH 09/27] Add text for resend disabled

---
 src/languages/en.js       | 1 +
 src/pages/EditMonitor.vue | 3 ++-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/languages/en.js b/src/languages/en.js
index 33ad0a42..17a58543 100644
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -3,6 +3,7 @@ export default {
     checkEverySecond: "Check every {0} seconds",
     retryCheckEverySecond: "Retry every {0} seconds",
     resendEveryMinute: "Resend every {0} minutes",
+    resendDisabled: "Resend disabled",
     retriesDescription: "Maximum retries before the service is marked as down and a notification is sent",
     ignoreTLSError: "Ignore TLS/SSL error for HTTPS websites",
     upsideDownModeDescription: "Flip the status upside down. If the service is reachable, it is DOWN.",
diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index a297c54b..a7bc4f78 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -140,7 +140,8 @@
                             <div class="my-3">
                                 <label for="resend-interval" class="form-label">
                                     {{ $t("Notification resend Interval if Down") }}
-                                    <span>({{ $t("resendEveryMinute", [ monitor.resendInterval ]) }})</span>
+                                    <span v-if="monitor.resendInterval > 0">({{ $t("resendEveryMinute", [ monitor.resendInterval ]) }})</span>
+                                    <span v-else>({{ $t("resendDisabled") }})</span>
                                 </label>
                                 <input id="resend-interval" v-model="monitor.resendInterval" type="number" class="form-control" required min="0" step="1">
                             </div>

From d446a57d42613490c6bd5a6bec075b289ff3caef Mon Sep 17 00:00:00 2001
From: OidaTiftla <oidatiftla@oidatiftla.de>
Date: Mon, 24 Jan 2022 22:20:48 +0100
Subject: [PATCH 10/27] Add german translation

---
 src/languages/de-DE.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/languages/de-DE.js b/src/languages/de-DE.js
index 48cdd2e3..9286a09b 100644
--- a/src/languages/de-DE.js
+++ b/src/languages/de-DE.js
@@ -164,6 +164,8 @@ export default {
     "Search...": "Suchen...",
     "Heartbeat Retry Interval": "Heartbeat-Wiederholungsintervall",
     retryCheckEverySecond: "Versuche alle {0} Sekunden",
+    resendEveryMinute: "Erneut versenden alle {0} Minuten",
+    resendDisabled: "Erneut versenden deaktiviert",
     "Import Backup": "Backup importieren",
     "Export Backup": "Backup exportieren",
     "Avg. Ping": "Durchschn. Ping",

From d8013f31e8906aeb0188725353392581621cd121 Mon Sep 17 00:00:00 2001
From: OidaTiftla <oidatiftla@oidatiftla.de>
Date: Sun, 27 Mar 2022 21:24:41 +0200
Subject: [PATCH 11/27] Update version after merging new master branch

---
 server/server.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/server/server.js b/server/server.js
index e10df8cc..36b8590f 100644
--- a/server/server.js
+++ b/server/server.js
@@ -1087,7 +1087,7 @@ exports.entryPage = "dashboard";
                 let monitorListData = backupData.monitorList;
 
                 let version17x = compareVersions.compare(backupData.version, "1.7.0", ">=");
-                let version1114 = compareVersions.compare(backupData.version, "1.11.4", ">=");
+                let version1132 = compareVersions.compare(backupData.version, "1.13.2", ">=");
 
                 // If the import option is "overwrite" it'll clear most of the tables, except "settings" and "user"
                 if (importHandle == "overwrite") {
@@ -1147,7 +1147,7 @@ exports.entryPage = "dashboard";
                                 retryInterval = monitorListData[i].retryInterval;
                             }
 
-                            if (version1114) {
+                            if (version1132) {
                                 resendInterval = monitorListData[i].resendInterval;
                             }
 

From 60f8ab7285fc0b3c1dfca5c8857807ba270e9956 Mon Sep 17 00:00:00 2001
From: OidaTiftla <oidatiftla@oidatiftla.de>
Date: Thu, 21 Apr 2022 12:09:59 +0200
Subject: [PATCH 12/27] Use new logging mechanism

---
 server/model/monitor.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/server/model/monitor.js b/server/model/monitor.js
index a7e0b82f..0ac2e33e 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -473,7 +473,7 @@ class Monitor extends BeanModel {
                     let timeSinceLastNotified = (dayjs.utc().valueOf() - (bean.lastNotifiedTime == null ? 0 : dayjs.utc(bean.lastNotifiedTime).valueOf())) / 1000 / 60; // divide by 1000 to convert from milliseconds to seconds and divide by 60 to convert from seconds to minutes
                     if (timeSinceLastNotified >= this.resendInterval) {
                         // Send notification again, because we are still DOWN
-                        debug(`[${this.name}] sendNotification again: lastNotifiedTime: ${bean.lastNotifiedTime} | current time: ${R.isoDateTime(dayjs.utc())}`);
+                        log.debug("monitor", `[${this.name}] sendNotification again: lastNotifiedTime: ${bean.lastNotifiedTime} | current time: ${R.isoDateTime(dayjs.utc())}`);
                         await Monitor.sendNotification(isFirstBeat, this, bean);
 
                         // Set last notified time to now

From 19933bbd99d7e11dba97e61183051dc876b9581e Mon Sep 17 00:00:00 2001
From: OidaTiftla <oidatiftla@oidatiftla.de>
Date: Thu, 21 Apr 2022 12:18:15 +0200
Subject: [PATCH 13/27] Improve backwards compatibility

---
 server/server.js | 8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)

diff --git a/server/server.js b/server/server.js
index d53fe696..58c08f9d 100644
--- a/server/server.js
+++ b/server/server.js
@@ -1168,7 +1168,6 @@ try {
                 let monitorListData = backupData.monitorList;
 
                 let version17x = compareVersions.compare(backupData.version, "1.7.0", ">=");
-                let version1132 = compareVersions.compare(backupData.version, "1.13.2", ">=");
 
                 // If the import option is "overwrite" it'll clear most of the tables, except "settings" and "user"
                 if (importHandle == "overwrite") {
@@ -1237,7 +1236,6 @@ try {
 
                             // Define default values
                             let retryInterval = 0;
-                            let resendInterval = 0;
 
                             /*
                             Only replace the default value with the backup file data for the specific version, where it appears the first time
@@ -1247,10 +1245,6 @@ try {
                                 retryInterval = monitorListData[i].retryInterval;
                             }
 
-                            if (version1132) {
-                                resendInterval = monitorListData[i].resendInterval;
-                            }
-
                             // --- End ---
 
                             let monitor = {
@@ -1265,7 +1259,7 @@ try {
                                 basic_auth_pass: monitorListData[i].basic_auth_pass,
                                 interval: monitorListData[i].interval,
                                 retryInterval: retryInterval,
-                                resendInterval: resendInterval,
+                                resendInterval: monitorListData[i].resendInterval || 0,
                                 hostname: monitorListData[i].hostname,
                                 maxretries: monitorListData[i].maxretries,
                                 port: monitorListData[i].port,

From d6b591a513cb928c9173dab0b9042922a4b3df49 Mon Sep 17 00:00:00 2001
From: OidaTiftla <chm.stephan@outlook.com>
Date: Thu, 21 Apr 2022 17:45:58 +0200
Subject: [PATCH 14/27] Make comment more readable

Co-authored-by: Matthew Nickson <mnickson@sidingsmedia.com>
---
 server/model/monitor.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/server/model/monitor.js b/server/model/monitor.js
index 0ac2e33e..1383153e 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -470,7 +470,8 @@ class Monitor extends BeanModel {
                 bean.important = false;
 
                 if (bean.status === DOWN && this.resendInterval > 0) {
-                    let timeSinceLastNotified = (dayjs.utc().valueOf() - (bean.lastNotifiedTime == null ? 0 : dayjs.utc(bean.lastNotifiedTime).valueOf())) / 1000 / 60; // divide by 1000 to convert from milliseconds to seconds and divide by 60 to convert from seconds to minutes
+                    // divide by 1000 to convert from milliseconds to seconds and divide by 60 to convert from seconds to minutes
+                    let timeSinceLastNotified = (dayjs.utc().valueOf() - (bean.lastNotifiedTime == null ? 0 : dayjs.utc(bean.lastNotifiedTime).valueOf())) / 1000 / 60; 
                     if (timeSinceLastNotified >= this.resendInterval) {
                         // Send notification again, because we are still DOWN
                         log.debug("monitor", `[${this.name}] sendNotification again: lastNotifiedTime: ${bean.lastNotifiedTime} | current time: ${R.isoDateTime(dayjs.utc())}`);

From 052fde5a24daa70855082c4ed9ba362c3785e463 Mon Sep 17 00:00:00 2001
From: OidaTiftla <chm.stephan@outlook.com>
Date: Thu, 21 Apr 2022 17:56:38 +0200
Subject: [PATCH 15/27] Fix casing of text label

Co-authored-by: Matthew Nickson <mnickson@sidingsmedia.com>
---
 src/pages/EditMonitor.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index 4338b4ea..661a89c4 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -172,7 +172,7 @@
 
                             <div class="my-3">
                                 <label for="resend-interval" class="form-label">
-                                    {{ $t("Notification resend Interval if Down") }}
+                                    {{ $t("Notification resend interval if down") }}
                                     <span v-if="monitor.resendInterval > 0">({{ $t("resendEveryMinute", [ monitor.resendInterval ]) }})</span>
                                     <span v-else>({{ $t("resendDisabled") }})</span>
                                 </label>

From c7ec9a07e248a730095e725c870f8396dfaa2296 Mon Sep 17 00:00:00 2001
From: OidaTiftla <oidatiftla@oidatiftla.de>
Date: Thu, 21 Apr 2022 17:59:38 +0200
Subject: [PATCH 16/27] Add translation for text label

---
 src/languages/de-DE.js | 1 +
 src/languages/en.js    | 1 +
 2 files changed, 2 insertions(+)

diff --git a/src/languages/de-DE.js b/src/languages/de-DE.js
index ae28bf5b..5af4c8a1 100644
--- a/src/languages/de-DE.js
+++ b/src/languages/de-DE.js
@@ -163,6 +163,7 @@ export default {
     Pink: "Pink",
     "Search...": "Suchen...",
     "Heartbeat Retry Interval": "Überprüfungsintervall",
+    "Notification resend interval if down": "Benachrichtigung erneut versenden wenn Inaktiv",
     retryCheckEverySecond: "Alle {0} Sekunden neu versuchen",
     resendEveryMinute: "Erneut versenden alle {0} Minuten",
     resendDisabled: "Erneut versenden deaktiviert",
diff --git a/src/languages/en.js b/src/languages/en.js
index 7726d12f..a3a375f3 100644
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -74,6 +74,7 @@ export default {
     "Heartbeat Interval": "Heartbeat Interval",
     Retries: "Retries",
     "Heartbeat Retry Interval": "Heartbeat Retry Interval",
+    "Notification resend interval if down": "Notification resend interval if down",
     Advanced: "Advanced",
     "Upside Down Mode": "Upside Down Mode",
     "Max. Redirects": "Max. Redirects",

From 7ed8ae9f7cc35e24c29bab087c5324d764bf67dc Mon Sep 17 00:00:00 2001
From: OidaTiftla <oidatiftla@oidatiftla.de>
Date: Thu, 21 Apr 2022 18:23:32 +0200
Subject: [PATCH 17/27] Fix trailing space warning

---
 server/model/monitor.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/server/model/monitor.js b/server/model/monitor.js
index 1383153e..84b211b8 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -471,7 +471,7 @@ class Monitor extends BeanModel {
 
                 if (bean.status === DOWN && this.resendInterval > 0) {
                     // divide by 1000 to convert from milliseconds to seconds and divide by 60 to convert from seconds to minutes
-                    let timeSinceLastNotified = (dayjs.utc().valueOf() - (bean.lastNotifiedTime == null ? 0 : dayjs.utc(bean.lastNotifiedTime).valueOf())) / 1000 / 60; 
+                    let timeSinceLastNotified = (dayjs.utc().valueOf() - (bean.lastNotifiedTime == null ? 0 : dayjs.utc(bean.lastNotifiedTime).valueOf())) / 1000 / 60;
                     if (timeSinceLastNotified >= this.resendInterval) {
                         // Send notification again, because we are still DOWN
                         log.debug("monitor", `[${this.name}] sendNotification again: lastNotifiedTime: ${bean.lastNotifiedTime} | current time: ${R.isoDateTime(dayjs.utc())}`);

From 98ee9caf2cdc54a2f5edb864906d620e84196317 Mon Sep 17 00:00:00 2001
From: OidaTiftla <chm.stephan@outlook.com>
Date: Thu, 5 May 2022 15:55:33 +0200
Subject: [PATCH 18/27] Add variable for currentTime

Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
---
 server/model/monitor.js | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/server/model/monitor.js b/server/model/monitor.js
index 15181af6..f29f6de5 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -492,11 +492,12 @@ class Monitor extends BeanModel {
                     let timeSinceLastNotified = (dayjs.utc().valueOf() - (bean.lastNotifiedTime == null ? 0 : dayjs.utc(bean.lastNotifiedTime).valueOf())) / 1000 / 60;
                     if (timeSinceLastNotified >= this.resendInterval) {
                         // Send notification again, because we are still DOWN
-                        log.debug("monitor", `[${this.name}] sendNotification again: lastNotifiedTime: ${bean.lastNotifiedTime} | current time: ${R.isoDateTime(dayjs.utc())}`);
+                        const currentTime = R.isoDateTime(dayjs.utc());
+                        log.debug("monitor", `[${this.name}] sendNotification again: lastNotifiedTime: ${bean.lastNotifiedTime} | current time: ${currentTime}`);
                         await Monitor.sendNotification(isFirstBeat, this, bean);
 
                         // Set last notified time to now
-                        bean.lastNotifiedTime = R.isoDateTime(dayjs.utc());
+                        bean.lastNotifiedTime = currentTime;
                     }
                 }
             }

From 93050208bbe9eb1c5678bf609c18e47953fb8485 Mon Sep 17 00:00:00 2001
From: OidaTiftla <oidatiftla@oidatiftla.de>
Date: Thu, 5 May 2022 16:01:10 +0200
Subject: [PATCH 19/27] Merge database changes into single patch file

---
 db/patch-heartbeat-add-last-notified-time.sql | 7 -------
 db/patch-monitor-add-resend-interval.sql      | 3 +++
 server/database.js                            | 1 -
 3 files changed, 3 insertions(+), 8 deletions(-)
 delete mode 100644 db/patch-heartbeat-add-last-notified-time.sql

diff --git a/db/patch-heartbeat-add-last-notified-time.sql b/db/patch-heartbeat-add-last-notified-time.sql
deleted file mode 100644
index af9c21c0..00000000
--- a/db/patch-heartbeat-add-last-notified-time.sql
+++ /dev/null
@@ -1,7 +0,0 @@
--- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
-BEGIN TRANSACTION;
-
-ALTER TABLE heartbeat
-    ADD last_notified_time DATETIME default null;
-
-COMMIT;
diff --git a/db/patch-monitor-add-resend-interval.sql b/db/patch-monitor-add-resend-interval.sql
index e8bb08b8..c31dd7a2 100644
--- a/db/patch-monitor-add-resend-interval.sql
+++ b/db/patch-monitor-add-resend-interval.sql
@@ -4,4 +4,7 @@ BEGIN TRANSACTION;
 ALTER TABLE monitor
     ADD resend_interval INTEGER default 0 not null;
 
+ALTER TABLE heartbeat
+    ADD last_notified_time DATETIME default null;
+
 COMMIT;
diff --git a/server/database.js b/server/database.js
index 8e3b188b..5dbfd676 100644
--- a/server/database.js
+++ b/server/database.js
@@ -59,7 +59,6 @@ class Database {
         "patch-status-page-footer-css.sql": true,
         "patch-added-mqtt-monitor.sql": true,
         "patch-monitor-add-resend-interval.sql": true,
-        "patch-heartbeat-add-last-notified-time.sql": true,
     };
 
     /**

From ac27e6e2af5dd8140b20c6f36a51a4af608b5a69 Mon Sep 17 00:00:00 2001
From: OidaTiftla <oidatiftla@oidatiftla.de>
Date: Wed, 15 Jun 2022 16:56:26 +0200
Subject: [PATCH 20/27] Rename feature to: Resend Notification if Down X times
 consequently

Co-authored-by: Louis Lam <louislam@users.noreply.github.com>
---
 db/patch-monitor-add-resend-interval.sql |  2 +-
 server/model/monitor.js                  | 20 +++++++++-----------
 src/languages/de-DE.js                   |  4 ++--
 src/languages/en.js                      |  4 ++--
 src/pages/EditMonitor.vue                |  4 ++--
 5 files changed, 16 insertions(+), 18 deletions(-)

diff --git a/db/patch-monitor-add-resend-interval.sql b/db/patch-monitor-add-resend-interval.sql
index c31dd7a2..8e28bf69 100644
--- a/db/patch-monitor-add-resend-interval.sql
+++ b/db/patch-monitor-add-resend-interval.sql
@@ -5,6 +5,6 @@ ALTER TABLE monitor
     ADD resend_interval INTEGER default 0 not null;
 
 ALTER TABLE heartbeat
-    ADD last_notified_time DATETIME default null;
+    ADD down_count INTEGER default 0 not null;
 
 COMMIT;
diff --git a/server/model/monitor.js b/server/model/monitor.js
index e1d02766..b3435f24 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -206,7 +206,7 @@ class Monitor extends BeanModel {
             bean.monitor_id = this.id;
             bean.time = R.isoDateTimeMillis(dayjs.utc());
             bean.status = DOWN;
-            bean.lastNotifiedTime = previousBeat?.lastNotifiedTime;
+            bean.downCount = previousBeat?.downCount || 0;
 
             if (this.isUpsideDown()) {
                 bean.status = flipStatus(bean.status);
@@ -523,8 +523,8 @@ class Monitor extends BeanModel {
                 log.debug("monitor", `[${this.name}] sendNotification`);
                 await Monitor.sendNotification(isFirstBeat, this, bean);
 
-                // Set last notified time to now
-                bean.lastNotifiedTime = R.isoDateTime(dayjs.utc());
+                // Reset down count
+                bean.downCount = 0;
 
                 // Clear Status Page Cache
                 log.debug("monitor", `[${this.name}] apicache clear`);
@@ -534,16 +534,14 @@ class Monitor extends BeanModel {
                 bean.important = false;
 
                 if (bean.status === DOWN && this.resendInterval > 0) {
-                    // divide by 1000 to convert from milliseconds to seconds and divide by 60 to convert from seconds to minutes
-                    let timeSinceLastNotified = (dayjs.utc().valueOf() - (bean.lastNotifiedTime == null ? 0 : dayjs.utc(bean.lastNotifiedTime).valueOf())) / 1000 / 60;
-                    if (timeSinceLastNotified >= this.resendInterval) {
+                    ++bean.downCount;
+                    if (bean.downCount >= this.resendInterval) {
                         // Send notification again, because we are still DOWN
-                        const currentTime = R.isoDateTime(dayjs.utc());
-                        log.debug("monitor", `[${this.name}] sendNotification again: lastNotifiedTime: ${bean.lastNotifiedTime} | current time: ${currentTime}`);
+                        log.debug("monitor", `[${this.name}] sendNotification again: Down Count: ${bean.downCount} | Resend Interval: ${this.resendInterval}`);
                         await Monitor.sendNotification(isFirstBeat, this, bean);
 
-                        // Set last notified time to now
-                        bean.lastNotifiedTime = currentTime;
+                        // Reset down count
+                        bean.downCount = 0;
                     }
                 }
             }
@@ -556,7 +554,7 @@ class Monitor extends BeanModel {
                 }
                 log.warn("monitor", `Monitor #${this.id} '${this.name}': Pending: ${bean.msg} | Max retries: ${this.maxretries} | Retry: ${retries} | Retry Interval: ${beatInterval} seconds | Type: ${this.type}`);
             } else {
-                log.warn("monitor", `Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Interval: ${beatInterval} seconds | Type: ${this.type}`);
+                log.warn("monitor", `Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Interval: ${beatInterval} seconds | Type: ${this.type} | Down Count: ${bean.downCount} | Resend Interval: ${this.resendInterval}`);
             }
 
             log.debug("monitor", `[${this.name}] Send to socket`);
diff --git a/src/languages/de-DE.js b/src/languages/de-DE.js
index f9f1e301..c4ef0b26 100644
--- a/src/languages/de-DE.js
+++ b/src/languages/de-DE.js
@@ -162,9 +162,9 @@ export default {
     Pink: "Pink",
     "Search...": "Suchen...",
     "Heartbeat Retry Interval": "Überprüfungsintervall",
-    "Notification resend interval if down": "Benachrichtigung erneut versenden wenn Inaktiv",
+    "Resend Notification if Down X times consequently": "Benachrichtigung erneut senden, wenn Inaktiv X mal hintereinander",
     retryCheckEverySecond: "Alle {0} Sekunden neu versuchen",
-    resendEveryMinute: "Erneut versenden alle {0} Minuten",
+    resendEveryXTimes: "Erneut versenden alle {0} mal",
     resendDisabled: "Erneut versenden deaktiviert",
     "Import Backup": "Backup importieren",
     "Export Backup": "Backup exportieren",
diff --git a/src/languages/en.js b/src/languages/en.js
index c3c3b740..49354a26 100644
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -2,7 +2,7 @@ export default {
     languageName: "English",
     checkEverySecond: "Check every {0} seconds",
     retryCheckEverySecond: "Retry every {0} seconds",
-    resendEveryMinute: "Resend every {0} minutes",
+    resendEveryXTimes: "Resend every {0} times",
     resendDisabled: "Resend disabled",
     retriesDescription: "Maximum retries before the service is marked as down and a notification is sent",
     ignoreTLSError: "Ignore TLS/SSL error for HTTPS websites",
@@ -74,7 +74,7 @@ export default {
     "Heartbeat Interval": "Heartbeat Interval",
     Retries: "Retries",
     "Heartbeat Retry Interval": "Heartbeat Retry Interval",
-    "Notification resend interval if down": "Notification resend interval if down",
+    "Resend Notification if Down X times consequently": "Resend Notification if Down X times consequently",
     Advanced: "Advanced",
     "Upside Down Mode": "Upside Down Mode",
     "Max. Redirects": "Max. Redirects",
diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index 55924952..87bf1996 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -204,8 +204,8 @@
 
                             <div class="my-3">
                                 <label for="resend-interval" class="form-label">
-                                    {{ $t("Notification resend interval if down") }}
-                                    <span v-if="monitor.resendInterval > 0">({{ $t("resendEveryMinute", [ monitor.resendInterval ]) }})</span>
+                                    {{ $t("Resend Notification if Down X times consequently") }}
+                                    <span v-if="monitor.resendInterval > 0">({{ $t("resendEveryXTimes", [ monitor.resendInterval ]) }})</span>
                                     <span v-else>({{ $t("resendDisabled") }})</span>
                                 </label>
                                 <input id="resend-interval" v-model="monitor.resendInterval" type="number" class="form-control" required min="0" step="1">

From 19d8761305ff9335385cd141e0586dace6b12e00 Mon Sep 17 00:00:00 2001
From: MrEddX <66828538+MrEddX@users.noreply.github.com>
Date: Tue, 2 Aug 2022 07:48:54 +0300
Subject: [PATCH 21/27] Update bg-BG.js

Just a typo
---
 src/languages/bg-BG.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/languages/bg-BG.js b/src/languages/bg-BG.js
index 56035295..1f531448 100644
--- a/src/languages/bg-BG.js
+++ b/src/languages/bg-BG.js
@@ -380,7 +380,7 @@ export default {
     deleteProxyMsg: "Сигурни ли сте, че желаете да изтриете това прокси за всички монитори?",
     proxyDescription: "За да функционират трябва да бъдат зададени към монитор.",
     enableProxyDescription: "Това прокси няма да има ефект върху заявките за мониторинг, докато не бъде активирано. Може да контролирате временното деактивиране на проксито от всички монитори чрез статуса на активиране.",
-    setAsDefaultProxyDescription: "Това проки ще бъде включено по подразбиране за новите монитори. Може да го изключите по отделно за всеки един монитор.",
+    setAsDefaultProxyDescription: "Това прокси ще бъде включено по подразбиране за новите монитори. Може да го изключите по отделно за всеки един монитор.",
     "Certificate Chain": "Верига на сертификата",
     Valid: "Валиден",
     Invalid: "Невалиден",

From a6007adce38755cb924d980fccf5250a9b040adc Mon Sep 17 00:00:00 2001
From: Louis Lam <louislam@users.noreply.github.com>
Date: Sat, 13 Aug 2022 13:32:16 +0800
Subject: [PATCH 22/27] Update to 1.18.0-beta.1

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index bac451d5..68a5863d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
     "name": "uptime-kuma",
-    "version": "1.17.1",
+    "version": "1.18.0-beta.1",
     "license": "MIT",
     "repository": {
         "type": "git",

From 728e811969a2ecc4c3550d37fb75e1c893750d3c Mon Sep 17 00:00:00 2001
From: Louis Lam <louislam@users.noreply.github.com>
Date: Sat, 13 Aug 2022 13:35:03 +0800
Subject: [PATCH 23/27] Update Apprise to 1.0.0

---
 docker/alpine-base.dockerfile | 2 +-
 docker/debian-base.dockerfile | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/docker/alpine-base.dockerfile b/docker/alpine-base.dockerfile
index cde65bb6..1d74de05 100644
--- a/docker/alpine-base.dockerfile
+++ b/docker/alpine-base.dockerfile
@@ -4,5 +4,5 @@ WORKDIR /app
 
 # Install apprise, iputils for non-root ping, setpriv
 RUN apk add --no-cache iputils setpriv dumb-init python3 py3-cryptography py3-pip py3-six py3-yaml py3-click py3-markdown py3-requests py3-requests-oauthlib && \
-    pip3 --no-cache-dir install apprise==0.9.9 && \
+    pip3 --no-cache-dir install apprise==1.0.0 && \
     rm -rf /root/.cache
diff --git a/docker/debian-base.dockerfile b/docker/debian-base.dockerfile
index f90968a8..20bef3dd 100644
--- a/docker/debian-base.dockerfile
+++ b/docker/debian-base.dockerfile
@@ -11,7 +11,7 @@ WORKDIR /app
 RUN apt update && \
     apt --yes --no-install-recommends install python3 python3-pip python3-cryptography python3-six python3-yaml python3-click python3-markdown python3-requests python3-requests-oauthlib \
         sqlite3 iputils-ping util-linux dumb-init && \
-    pip3 --no-cache-dir install apprise==0.9.9 && \
+    pip3 --no-cache-dir install apprise==1.0.0 && \
     rm -rf /var/lib/apt/lists/* && \
     apt --yes autoremove
 

From af944242839616ed0a990fdba2e2f43fd043d4c4 Mon Sep 17 00:00:00 2001
From: Louis Lam <louislam@users.noreply.github.com>
Date: Sat, 13 Aug 2022 14:04:17 +0800
Subject: [PATCH 24/27] Update to 1.18.0-beta.0

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 68a5863d..981ca191 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
     "name": "uptime-kuma",
-    "version": "1.18.0-beta.1",
+    "version": "1.18.0-beta.0",
     "license": "MIT",
     "repository": {
         "type": "git",

From b3712ee1ccb4715dc2464fc0a3cd821e80eab4d1 Mon Sep 17 00:00:00 2001
From: AnnAngela <naganjue@vip.qq.com>
Date: Sat, 13 Aug 2022 16:19:51 +0800
Subject: [PATCH 25/27] fix: Update en language file to match up newest
 development

---
 src/languages/en.js | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/src/languages/en.js b/src/languages/en.js
index 3bb02585..f281cade 100644
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -562,4 +562,18 @@ export default {
     disableCloudflaredNoAuthMsg: "You are in No Auth mode, password is not require.",
     trustProxyDescription: "Trust 'X-Forwarded-*' headers. If you want to get the correct client IP and your Uptime Kuma is behind such as Nginx or Apache, you should enable this.",
     wayToGetLineNotifyToken: "You can get an access token from {0}",
+    Examples: "Examples",
+    "Home Assistant URL": "Home Assistant URL",
+    "Long-Lived Access Token": "Long-Lived Access Token",
+    "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ",
+    "Notification Service": "Notification Service",
+    "default: notify all devices": "default: notify all devices",
+    "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.",
+    "Automations can optionally be triggered in Home Assistant:": "Automations can optionally be triggered in Home Assistant:",
+    "Trigger type:": "Trigger type:",
+    "Event type:": "Event type:",
+    "Event data:": "Event data:",
+    "Then choose an action, for example switch the scene to where an RGB light is red.": "Then choose an action, for example switch the scene to where an RGB light is red.",
+    "Frontend Version": "Frontend Version",
+    "Frontend Version do not match backend version!": "Frontend Version do not match backend version!",
 };

From 684d0a7eb8113ccdd85f8b8c7d9aa3ac902db33b Mon Sep 17 00:00:00 2001
From: AnnAngela <naganjue@vip.qq.com>
Date: Sat, 13 Aug 2022 16:23:51 +0800
Subject: [PATCH 26/27] feat: Update zh-CN languages file
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Due to no Radius server and Home Assistant device or server in my hands, the translation may be incorrect.\nRef:\n- Radius secret → Radius 共享机密: https://docs.microsoft.com/zh-cn/azure/active-directory/authentication/howto-mfaserver-dir-radius#:~:text=%E5%8F%AF%E9%80%89%EF%BC%89%E5%92%8C-,%E5%85%B1%E4%BA%AB%E6%9C%BA%E5%AF%86,-%E3%80%82\n- Called Station Id → NAS 网络访问服务器号码: https://docs.microsoft.com/zh-cn/windows-server/networking/technologies/nps/nps-plan-proxy#:~:text=Called%2DStation%2DID%E3%80%82-,NAS%20%E7%BD%91%E7%BB%9C%E8%AE%BF%E9%97%AE%E6%9C%8D%E5%8A%A1%E5%99%A8%20(%E7%94%B5%E8%AF%9D%E5%8F%B7%E7%A0%81),-%E3%80%82%20%E6%AD%A4%E5%B1%9E%E6%80%A7%E7%9A%84\n- Calling Station Id → 呼叫方号码: https://docs.microsoft.com/zh-cn/windows-server/networking/technologies/nps/nps-plan-proxy#:~:text=Calling%2DStation%2DID%E3%80%82-,%E5%91%BC%E5%8F%AB%E6%96%B9%E4%BD%BF%E7%94%A8%E7%9A%84%E7%94%B5%E8%AF%9D%E5%8F%B7%E7%A0%81,-%E3%80%82%20%E6%AD%A4%E5%B1%9E%E6%80%A7%E7%9A%84
---
 src/languages/zh-CN.js | 43 ++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 41 insertions(+), 2 deletions(-)

diff --git a/src/languages/zh-CN.js b/src/languages/zh-CN.js
index 8dbe05f0..a37d4ae4 100644
--- a/src/languages/zh-CN.js
+++ b/src/languages/zh-CN.js
@@ -540,6 +540,45 @@ export default {
     settingsCertificateExpiry: "TLS 证书过期通知",
     certificationExpiryDescription: "HTTPS 监控项发现被监控目标的 TLS 证书剩余有效期少于以下天数时将发出通知:",
     "ntfy Topic": "ntfy 主题",
-    "Domain": "域名",
-    "Workstation": "工作站",
+    Domain: "域名",
+    Workstation: "工作站",
+    resendEveryXTimes: "每 {0} 次失败则重复发送一次",
+    resendDisabled: "为 0 时禁用重复发送",
+    "Resend Notification if Down X times consequently": "连续失败时重复发送通知的间隔次数",
+    "HTTP Headers": "HTTP 头",
+    "Trust Proxy": "可信的代理类字段",
+    HomeAssistant: "Home Assistant",
+    RadiusSecret: "Radius 共享机密",
+    RadiusSecretDescription: "客户端和服务器之间共享的密钥",
+    RadiusCalledStationId: "NAS 网络访问服务器号码(Called Station Id)",
+    RadiusCalledStationIdDescription: "所访问的服务器的标识",
+    RadiusCallingStationId: "呼叫方号码(Calling Station Id)",
+    RadiusCallingStationIdDescription: "发出请求的设备的标识",
+    "Setup Docker Host": "配置 Docker 宿主信息",
+    "Connection Type": "连接方式",
+    "Docker Daemon": "Docker 守护进程",
+    deleteDockerHostMsg: "您确定您要删除此 Docker 宿主设置吗?这会影响所有 Docker 监控项",
+    socket: "Socket",
+    tcp: "TCP / HTTP",
+    "Docker Container": "Docker 容器",
+    "Container Name / ID": "容器名称 / ID",
+    "Docker Host": "Docker 宿主",
+    "Docker Hosts": "Docker 宿主",
+    disableCloudflaredNoAuthMsg: "您现在正处于 No Auth 模式,无需输入密码",
+    trustProxyDescription: "信任 'X-Forwarded-*' 头。如果您的 Uptime Kuma 是通过 Nginx 或 Apache 等反代服务对外提供访问的话,则您应当启用本功能以获取正确的客户端 IP。",
+    wayToGetLineNotifyToken: "您可以在 {0} 获取 Access token",
+    Examples: "例如",
+    "Home Assistant URL": "Home Assistant 地址",
+    "Long-Lived Access Token": "长期访问令牌",
+    "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "长期访问令牌可通过点击左下角您的用户名,滚动到页面底部并点击 Create Token 按钮获取。",
+    "Notification Service": "Notification Service",
+    "default: notify all devices": "默认:通知所有设备",
+    "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "通知服务的列表可在 Home Assistant 中的 Developer Tools > Services 通过搜索您的设备或手机的名称来获得。",
+    "Automations can optionally be triggered in Home Assistant:": "可以在 Home Assistant 使用下列模板设置自动化操作的触发条件:",
+    "Trigger type:": "触发类型:",
+    "Event type:": "事件类型:",
+    "Event data:": "事件数据:",
+    "Then choose an action, for example switch the scene to where an RGB light is red.": "然后您可以选择关联操作,例如切换到 RGB 灯发出红光的场景",
+    "Frontend Version": "前端版本",
+    "Frontend Version do not match backend version!": "前端版本与后端版本不符!",
 };

From fac2f1cbc65811f2c52dda005dea7bcbc69c958b Mon Sep 17 00:00:00 2001
From: MrEddX <66828538+MrEddX@users.noreply.github.com>
Date: Sun, 14 Aug 2022 08:52:53 +0300
Subject: [PATCH 27/27] Update bg-BG.js

Translated new fields for the upcoming 1.18.0 release.
---
 src/languages/bg-BG.js | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/src/languages/bg-BG.js b/src/languages/bg-BG.js
index 1f531448..a234e56f 100644
--- a/src/languages/bg-BG.js
+++ b/src/languages/bg-BG.js
@@ -537,4 +537,29 @@ export default {
     Workstation: "Работна станция",
     disableCloudflaredNoAuthMsg: "Тъй като сте в режим \"No Auth mode\", парола не се изисква.",
     wayToGetLineNotifyToken: "Може да получите токен код за достъп от {0}",
+    resendEveryXTimes: "Изпращай повторно на всеки {0} пъти",
+    resendDisabled: "Повторното изпращане е изключено",
+    "Resend Notification if Down X times consequently": "Повторно изпращане на известие, ако е недостъпен X пъти последователно",
+    "Bark Group": "Bark група",
+    "Bark Sound": "Bark звук",
+    "HTTP Headers": "HTTP хедъри",
+    "Trust Proxy": "Trust Proxy",
+    HomeAssistant: "Home Assistant",
+    RadiusSecret: "Radius таен код",
+    RadiusSecretDescription: "Споделен таен код между клиент и сървър",
+    RadiusCalledStationId: "Повиквана станция ID",
+    RadiusCalledStationIdDescription: "Идентификатор на повикваното устройство",
+    RadiusCallingStationId: "Повикваща станция ID",
+    RadiusCallingStationIdDescription: "Идентификатор на повикващото устройство",
+    "Setup Docker Host": "Настройка на Docker хост",
+    "Connection Type": "Тип свързване",
+    "Docker Daemon": "Docker демон",
+    deleteDockerHostMsg: "Сигурни ли сте, че желаете да изтриете този Docker хост за всички монитори?",
+    socket: "Сокет",
+    tcp: "TCP / HTTP",
+    "Docker Container": "Docker контейнер",
+    "Container Name / ID": "Име на контейнер / ID",
+    "Docker Host": "Docker хост",
+    "Docker Hosts": "Docker хостове",
+    trustProxyDescription: "Trust 'X-Forwarded-*' headers.  Ако искате да получавате правилния IP адрес на клиента, а Uptime Kuma е зад системи като Nginx или Apache, трябва да разрешите тази опция.",
 };