From 3dda5938f2180c4af2b9edd6f165c196c707da1c Mon Sep 17 00:00:00 2001
From: Raphael Bernhart <mail@raphaelbernhart.at>
Date: Fri, 21 Jan 2022 15:39:49 +0100
Subject: [PATCH 01/25] =?UTF-8?q?=F0=9F=92=84=20Add=20condition=20to=20div?=
 =?UTF-8?q?=20tag=20for=20styling=20reasons?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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

diff --git a/src/components/PublicGroupList.vue b/src/components/PublicGroupList.vue
index f30edcef..bd68e94e 100644
--- a/src/components/PublicGroupList.vue
+++ b/src/components/PublicGroupList.vue
@@ -41,7 +41,7 @@
                                             <Uptime :monitor="monitor.element" type="24" :pill="true" />
                                             {{ monitor.element.name }}
                                         </div>
-                                        <div class="tags">
+                                        <div v-if="monitor.element.tags.length" class="tags">
                                             <Tag v-for="tag in monitor.element.tags" :key="tag" :item="tag" :size="'sm'" />
                                         </div>
                                     </div>

From c57b2c4d287ac1ac9242ca3bc99a72236bc0606c Mon Sep 17 00:00:00 2001
From: Raphael Bernhart <mail@raphaelbernhart.at>
Date: Fri, 21 Jan 2022 17:13:24 +0100
Subject: [PATCH 02/25] =?UTF-8?q?=F0=9F=92=84=20Fix=20spacing=20to=20get?=
 =?UTF-8?q?=20pixel=20perfectness?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/assets/app.scss                | 2 +-
 src/components/PublicGroupList.vue | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/assets/app.scss b/src/assets/app.scss
index cec64467..f4d99462 100644
--- a/src/assets/app.scss
+++ b/src/assets/app.scss
@@ -339,7 +339,7 @@ textarea.form-control {
     .item {
         display: block;
         text-decoration: none;
-        padding: 13px 15px 10px 15px;
+        padding: 15px;
         border-radius: 10px;
         transition: all ease-in-out 0.15s;
 
diff --git a/src/components/PublicGroupList.vue b/src/components/PublicGroupList.vue
index bd68e94e..334282d8 100644
--- a/src/components/PublicGroupList.vue
+++ b/src/components/PublicGroupList.vue
@@ -33,8 +33,8 @@
                         <template #item="monitor">
                             <div class="item">
                                 <div class="row">
-                                    <div class="col-9 col-md-8 small-padding">
-                                        <div class="info">
+                                    <div class="col-9 col-md-8 small-padding d-flex align-items-center flex-wrap">
+                                        <div class="info d-flex align-items-center gap-3 w-100">
                                             <font-awesome-icon v-if="editMode" icon="arrows-alt-v" class="action drag me-3" />
                                             <font-awesome-icon v-if="editMode" icon="times" class="action remove me-3" @click="removeMonitor(group.index, monitor.index)" />
 
@@ -45,7 +45,7 @@
                                             <Tag v-for="tag in monitor.element.tags" :key="tag" :item="tag" :size="'sm'" />
                                         </div>
                                     </div>
-                                    <div :key="$root.userHeartbeatBar" class="col-3 col-md-4">
+                                    <div :key="$root.userHeartbeatBar" class="col-3 col-md-4 d-flex align-items-center">
                                         <HeartbeatBar size="small" :monitor-id="monitor.element.id" />
                                     </div>
                                 </div>

From cd19b9fc49965732a4dd1def35fd695587101165 Mon Sep 17 00:00:00 2001
From: Raphael Bernhart <mail@raphaelbernhart.at>
Date: Fri, 21 Jan 2022 17:13:38 +0100
Subject: [PATCH 03/25] =?UTF-8?q?=F0=9F=92=AB=20Improve=20hearbeat=20anima?=
 =?UTF-8?q?tion?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/components/HeartbeatBar.vue | 47 +++++++++++++++++++++++++++++----
 1 file changed, 42 insertions(+), 5 deletions(-)

diff --git a/src/components/HeartbeatBar.vue b/src/components/HeartbeatBar.vue
index be0b122e..7143e716 100644
--- a/src/components/HeartbeatBar.vue
+++ b/src/components/HeartbeatBar.vue
@@ -1,6 +1,6 @@
 <template>
     <div ref="wrap" class="wrap" :style="wrapStyle">
-        <div class="hp-bar-big" :style="barStyle">
+        <div class="hp-bar-big d-flex" :style="barStyle">
             <div
                 v-for="(beat, index) in shortBeatList"
                 :key="index"
@@ -8,7 +8,11 @@
                 :class="{ 'empty' : (beat === 0), 'down' : (beat.status === 0), 'pending' : (beat.status === 2) }"
                 :style="beatStyle"
                 :title="getBeatTitle(beat)"
-            />
+                @mouseenter="toggleActivateSibling"
+                @mouseleave="toggleActivateSibling"
+            >
+                <div class="beat-inner" />
+            </div>
         </div>
     </div>
 </template>
@@ -168,9 +172,30 @@ export default {
 
         getBeatTitle(beat) {
             return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : ``);
+        },
+        // Toggling the activeSibling class on hover over the current hover item
+        toggleActivateSibling(e) {
+            // Variable definition
+            const element = e.target;
+            const previous = element.previousSibling;
+            const next = element.nextSibling;
+
+            // Return if the hovered element has empty class
+            if (element.classList.contains("empty")) {
+                return;
+            }
+
+            // Check if Previous Sibling is heartbar element and doesn't have the empty class
+            if (previous.children && !previous.classList.contains("empty")) {
+                previous.classList.toggle("active-sibling");
+            }
+            // Check if Next Sibling is heartbar element and doesn't have the empty class
+            if (next.children && next.classList.contains("empty")) {
+                next.classList.toggle("active-sibling");
+            }
         }
     },
-}
+};
 </script>
 
 <style lang="scss" scoped>
@@ -184,9 +209,10 @@ export default {
 
 .hp-bar-big {
     .beat {
-        display: inline-block;
         background-color: $primary;
         border-radius: $border-radius;
+        display: inline-block;
+        transition: all ease 0.6s;
 
         &.empty {
             background-color: aliceblue;
@@ -200,11 +226,22 @@ export default {
             background-color: $warning;
         }
 
+        .beat-inner {
+            border-radius: $border-radius;
+            display: inline-block;
+            height: 100%;
+            width: 5px;
+        }
+
         &:not(.empty):hover {
-            transition: all ease-in-out 0.15s;
+            transition: all ease 0.15s;
             opacity: 0.8;
             transform: scale(var(--hover-scale));
         }
+        &.active-sibling {
+            transform: scale(1.3);
+            transition: all ease 0.15s;
+        }
     }
 }
 

From 0313acd4c5bef4fce5c01cc6a7e7d6fe27439150 Mon Sep 17 00:00:00 2001
From: Raphael Bernhart <mail@raphaelbernhart.at>
Date: Fri, 21 Jan 2022 17:22:30 +0100
Subject: [PATCH 04/25] =?UTF-8?q?=F0=9F=90=9B=20Fix=20bug=20where=20a=20co?=
 =?UTF-8?q?ndition=20was=20wrong-false?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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

diff --git a/src/components/HeartbeatBar.vue b/src/components/HeartbeatBar.vue
index 7143e716..d7f352cc 100644
--- a/src/components/HeartbeatBar.vue
+++ b/src/components/HeartbeatBar.vue
@@ -190,7 +190,7 @@ export default {
                 previous.classList.toggle("active-sibling");
             }
             // Check if Next Sibling is heartbar element and doesn't have the empty class
-            if (next.children && next.classList.contains("empty")) {
+            if (next.children && !next.classList.contains("empty")) {
                 next.classList.toggle("active-sibling");
             }
         }

From dd3992063eb680ba6233b146a6b1c7a879d6e031 Mon Sep 17 00:00:00 2001
From: Raphael Bernhart <48283236+raphaelbernhart@users.noreply.github.com>
Date: Sat, 22 Jan 2022 13:59:36 +0100
Subject: [PATCH 05/25] Apply suggestions from code review

Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
---
 src/components/HeartbeatBar.vue | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/components/HeartbeatBar.vue b/src/components/HeartbeatBar.vue
index d7f352cc..bbc7a40f 100644
--- a/src/components/HeartbeatBar.vue
+++ b/src/components/HeartbeatBar.vue
@@ -173,6 +173,7 @@ export default {
         getBeatTitle(beat) {
             return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : ``);
         },
+
         // Toggling the activeSibling class on hover over the current hover item
         toggleActivateSibling(e) {
             // Variable definition
@@ -189,6 +190,7 @@ export default {
             if (previous.children && !previous.classList.contains("empty")) {
                 previous.classList.toggle("active-sibling");
             }
+
             // Check if Next Sibling is heartbar element and doesn't have the empty class
             if (next.children && !next.classList.contains("empty")) {
                 next.classList.toggle("active-sibling");
@@ -238,6 +240,7 @@ export default {
             opacity: 0.8;
             transform: scale(var(--hover-scale));
         }
+
         &.active-sibling {
             transform: scale(1.3);
             transition: all ease 0.15s;

From ce7d8c38c522a6f6b77a5bd19ce5e800f4769b5d Mon Sep 17 00:00:00 2001
From: jordanbertasso <36979824+jordanbertasso@users.noreply.github.com>
Date: Sun, 10 Apr 2022 21:43:52 +1000
Subject: [PATCH 06/25] [empty commit] pull request for issue #1448


From 0961c6d9b3b73db5446faa77bbd1173be6b4e979 Mon Sep 17 00:00:00 2001
From: jordanbertasso <36979824+jordanbertasso@users.noreply.github.com>
Date: Sun, 10 Apr 2022 21:45:07 +1000
Subject: [PATCH 07/25] Check for ping and port type in discord notifs

---
 server/notification-providers/discord.js | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/server/notification-providers/discord.js b/server/notification-providers/discord.js
index 881ad211..39eeef5a 100644
--- a/server/notification-providers/discord.js
+++ b/server/notification-providers/discord.js
@@ -24,14 +24,17 @@ class Discord extends NotificationProvider {
 
             let url;
 
-            if (monitorJSON["type"] === "port") {
-                url = monitorJSON["hostname"];
-                if (monitorJSON["port"]) {
-                    url += ":" + monitorJSON["port"];
-                }
-
-            } else {
-                url = monitorJSON["url"];
+            switch (monitorJSON["type"]) {
+                case "port":
+                case "ping":
+                    url = monitorJSON["hostname"];
+                    if (monitorJSON["port"]) {
+                        url += ":" + monitorJSON["port"];
+                    }
+                    break;
+                default:
+                    url = monitorJSON["url"];
+                    break;
             }
 
             // If heartbeatJSON is not null, we go into the normal alerting loop.

From 1b1e0f6dd942d25c4b97a549ef6f1b28870b95d5 Mon Sep 17 00:00:00 2001
From: jordanbertasso <36979824+jordanbertasso@users.noreply.github.com>
Date: Sun, 10 Apr 2022 21:45:19 +1000
Subject: [PATCH 08/25] Add Discord tests

---
 test/backend.spec.js | 44 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 43 insertions(+), 1 deletion(-)

diff --git a/test/backend.spec.js b/test/backend.spec.js
index bbfc6897..7c02d342 100644
--- a/test/backend.spec.js
+++ b/test/backend.spec.js
@@ -1,5 +1,9 @@
-const { genSecret, sleep } = require("../src/util");
+const { genSecret, sleep, DOWN } = require("../src/util");
 const utilServerRewire = require("../server/util-server");
+const Discord = require("../server/notification-providers/discord");
+const axios = require("axios");
+
+jest.mock("axios");
 
 describe("Test parseCertificateInfo", () => {
     it("should handle undefined", async () => {
@@ -164,3 +168,41 @@ describe("Test reset-password", () => {
     }, 120000);
 });
 
+describe("Test Discord Notification Provider", () => {
+    const sendNotification = async (type) => {
+        const discordProvider = new Discord();
+
+        axios.post.mockResolvedValue({});
+
+        await discordProvider.send(
+            {
+                discordUsername: "Uptime Kuma",
+                discordWebhookUrl: "https://discord.com",
+            },
+            "test message",
+            {
+                type,
+                hostname: "discord.com" + (type === "port" ? ":1337" : ""),
+            },
+            {
+                status: DOWN,
+            }
+        );
+    };
+
+    it("should send hostname for ping monitors", async () => {
+        await sendNotification("ping");
+
+        expect(axios.post.mock.lastCall[1].embeds[0].fields[1].value).toBe(
+            "discord.com"
+        );
+    });
+
+    it("should send hostname for port monitors", async () => {
+        await sendNotification("port");
+
+        expect(axios.post.mock.lastCall[1].embeds[0].fields[1].value).toBe(
+            "discord.com:1337"
+        );
+    });
+});

From 2638d68c97d4c06a2d4f2545e4bc2a5b74eef9da Mon Sep 17 00:00:00 2001
From: jordanbertasso <36979824+jordanbertasso@users.noreply.github.com>
Date: Tue, 12 Apr 2022 09:52:07 +1000
Subject: [PATCH 09/25] Cover dns and steam types in Discord notifs

---
 server/notification-providers/discord.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/server/notification-providers/discord.js b/server/notification-providers/discord.js
index 39eeef5a..c5b95c60 100644
--- a/server/notification-providers/discord.js
+++ b/server/notification-providers/discord.js
@@ -25,8 +25,10 @@ class Discord extends NotificationProvider {
             let url;
 
             switch (monitorJSON["type"]) {
-                case "port":
+                case "dns":
                 case "ping":
+                case "port":
+                case "steam":
                     url = monitorJSON["hostname"];
                     if (monitorJSON["port"]) {
                         url += ":" + monitorJSON["port"];

From 0765f0509065692085becffd506f65349d35175f Mon Sep 17 00:00:00 2001
From: jordanbertasso <36979824+jordanbertasso@users.noreply.github.com>
Date: Tue, 12 Apr 2022 09:52:16 +1000
Subject: [PATCH 10/25] Update Discord tests

---
 test/backend.spec.js | 37 ++++++++++++++++++++++++++++++-------
 1 file changed, 30 insertions(+), 7 deletions(-)

diff --git a/test/backend.spec.js b/test/backend.spec.js
index 7c02d342..27aaf9e2 100644
--- a/test/backend.spec.js
+++ b/test/backend.spec.js
@@ -169,7 +169,7 @@ describe("Test reset-password", () => {
 });
 
 describe("Test Discord Notification Provider", () => {
-    const sendNotification = async (type) => {
+    const sendNotification = async (hostname, port, type) => {
         const discordProvider = new Discord();
 
         axios.post.mockResolvedValue({});
@@ -182,7 +182,8 @@ describe("Test Discord Notification Provider", () => {
             "test message",
             {
                 type,
-                hostname: "discord.com" + (type === "port" ? ":1337" : ""),
+                hostname,
+                port,
             },
             {
                 status: DOWN,
@@ -190,19 +191,41 @@ describe("Test Discord Notification Provider", () => {
         );
     };
 
-    it("should send hostname for ping monitors", async () => {
-        await sendNotification("ping");
+    it("should send hostname for dns monitors", async () => {
+        const hostname = "discord.com";
+        await sendNotification(hostname, null, "dns");
 
         expect(axios.post.mock.lastCall[1].embeds[0].fields[1].value).toBe(
-            "discord.com"
+            hostname
+        );
+    });
+
+    it("should send hostname for ping monitors", async () => {
+        const hostname = "discord.com";
+        await sendNotification(hostname, null, "ping");
+
+        expect(axios.post.mock.lastCall[1].embeds[0].fields[1].value).toBe(
+            hostname
         );
     });
 
     it("should send hostname for port monitors", async () => {
-        await sendNotification("port");
+        const hostname = "discord.com";
+        const port = 1337;
+        await sendNotification(hostname, port, "port");
 
         expect(axios.post.mock.lastCall[1].embeds[0].fields[1].value).toBe(
-            "discord.com:1337"
+            `${hostname}:${port}`
+        );
+    });
+
+    it("should send hostname for steam monitors", async () => {
+        const hostname = "discord.com";
+        const port = 1337;
+        await sendNotification(hostname, port, "steam");
+
+        expect(axios.post.mock.lastCall[1].embeds[0].fields[1].value).toBe(
+            `${hostname}:${port}`
         );
     });
 });

From 45da7c543193d6a355305830b1c9c8122b030941 Mon Sep 17 00:00:00 2001
From: AnnAngela-work <naganjue@vip.qq.com>
Date: Fri, 29 Apr 2022 20:17:15 +0800
Subject: [PATCH 11/25] Improve translation work

---
 src/components/notifications/ClickSendSMS.vue | 12 ++---
 src/components/notifications/Line.vue         |  2 +-
 src/components/notifications/Octopush.vue     | 10 ++--
 src/components/notifications/PromoSMS.vue     |  4 +-
 src/components/notifications/Pushover.vue     | 45 +++++++++--------
 src/components/notifications/Pushy.vue        |  4 +-
 src/components/notifications/TechulusPush.vue |  2 +-
 src/components/settings/About.vue             |  4 +-
 src/languages/en.js                           | 50 +++++++++++++++++++
 src/languages/zh-CN.js                        | 50 +++++++++++++++++++
 src/layouts/Layout.vue                        |  2 +-
 src/pages/EditMonitor.vue                     |  2 +-
 src/pages/NotFound.vue                        | 12 ++---
 src/pages/StatusPage.vue                      |  2 +-
 14 files changed, 151 insertions(+), 50 deletions(-)

diff --git a/src/components/notifications/ClickSendSMS.vue b/src/components/notifications/ClickSendSMS.vue
index 4fbb2f41..09d3201c 100644
--- a/src/components/notifications/ClickSendSMS.vue
+++ b/src/components/notifications/ClickSendSMS.vue
@@ -1,12 +1,12 @@
 <template>
     <div class="mb-3">
-        <label for="clicksendsms-login" class="form-label">API Username</label>
+        <label for="clicksendsms-login" class="form-label">{{ $t("API Username") }}</label>
         <div class="form-text">
             {{ $t("apiCredentials") }}
             <a href="http://dashboard.clicksend.com/account/subaccounts" target="_blank">{{ $t("here") }}</a>
         </div>
         <input id="clicksendsms-login" v-model="$parent.notification.clicksendsmsLogin" type="text" class="form-control" required>
-        <label for="clicksendsms-key" class="form-label">API Key</label>
+        <label for="clicksendsms-key" class="form-label">{{ $t("API Key") }}</label>
         <HiddenInput id="clicksendsms-key" v-model="$parent.notification.clicksendsmsPassword" :required="true" autocomplete="one-time-code"></HiddenInput>
     </div>
     <div class="mb-3">
@@ -16,15 +16,15 @@
         </div>
     </div>
     <div class="mb-3">
-        <label for="clicksendsms-to-number" class="form-label">Recipient Number</label>
+        <label for="clicksendsms-to-number" class="form-label">{{ $t("Recipient Number") }}</label>
         <input id="clicksendsms-to-number" v-model="$parent.notification.clicksendsmsToNumber" type="text" minlength="8" maxlength="14" class="form-control" required>
     </div>
     <div class="mb-3">
-        <label for="clicksendsms-sender-name" class="form-label">From Name/Number -
-            <a href="https://help.clicksend.com/article/4kgj7krx00-what-is-a-sender-id-or-sender-number" target="_blank">More Info</a>
+        <label for="clicksendsms-sender-name" class="form-label">{{ $t("From Name/Number") }} -
+            <i18n-t tag="span" keypath="Read more:"><a href="https://help.clicksend.com/article/4kgj7krx00-what-is-a-sender-id-or-sender-number" target="_blank">https://help.clicksend.com/article/4kgj7krx00-what-is-a-sender-id-or-sender-number</a></i18n-t>
         </label>
         <input id="clicksendsms-sender-name" v-model="$parent.notification.clicksendsmsSenderName" type="text" minlength="3" maxlength="11" class="form-control">
-        <div class="form-text">Leave blank to use a shared sender number.</div>
+        <div class="form-text">{{ $t("Leave blank to use a shared sender number.") }}</div>
     </div>
 </template>
 <script>
diff --git a/src/components/notifications/Line.vue b/src/components/notifications/Line.vue
index cb52c0c1..34ceb4ac 100644
--- a/src/components/notifications/Line.vue
+++ b/src/components/notifications/Line.vue
@@ -7,7 +7,7 @@
         <b>{{ $t("Basic Settings") }}</b>
     </i18n-t>
     <div class="mb-3" style="margin-top: 12px;">
-        <label for="line-user-id" class="form-label">User ID</label>
+        <label for="line-user-id" class="form-label">{{ $t("User ID") }}</label>
         <input id="line-user-id" v-model="$parent.notification.lineUserID" type="text" class="form-control" required>
     </div>
     <i18n-t tag="div" keypath="lineDevConsoleTo" class="form-text">
diff --git a/src/components/notifications/Octopush.vue b/src/components/notifications/Octopush.vue
index 37629d39..7d5fe469 100644
--- a/src/components/notifications/Octopush.vue
+++ b/src/components/notifications/Octopush.vue
@@ -1,18 +1,18 @@
 <template>
     <div class="mb-3">
-        <label for="octopush-version" class="form-label">Octopush API Version</label>
+        <label for="octopush-version" class="form-label">{{ $t("Octopush API Version") }}</label>
         <select id="octopush-version" v-model="$parent.notification.octopushVersion" class="form-select">
-            <option value="2">Octopush (endpoint: api.octopush.com)</option>
-            <option value="1">Legacy Octopush-DM (endpoint: www.octopush-dm.com)</option>
+            <option value="2">{{ $t("octopush") }} ({{ $t("endpoint") }}: api.octopush.com)</option>
+            <option value="1">{{ $t("Legacy Octopush-DM") }} ({{ $t("endpoint") }}: www.octopush-dm.com)</option>
         </select>
         <div class="form-text">
             {{ $t("octopushLegacyHint") }}
         </div>
     </div>
     <div class="mb-3">
-        <label for="octopush-key" class="form-label">API KEY</label>
+        <label for="octopush-key" class="form-label">{{ $t("octopushAPIKey") }}</label>
         <HiddenInput id="octopush-key" v-model="$parent.notification.octopushAPIKey" :required="true" autocomplete="one-time-code"></HiddenInput>
-        <label for="octopush-login" class="form-label">API LOGIN</label>
+        <label for="octopush-login" class="form-label">{{ $t("octopushLogin") }}</label>
         <input id="octopush-login" v-model="$parent.notification.octopushLogin" type="text" class="form-control" required>
     </div>
     <div class="mb-3">
diff --git a/src/components/notifications/PromoSMS.vue b/src/components/notifications/PromoSMS.vue
index 61e61a93..c11ed559 100644
--- a/src/components/notifications/PromoSMS.vue
+++ b/src/components/notifications/PromoSMS.vue
@@ -1,8 +1,8 @@
 <template>
     <div class="mb-3">
-        <label for="promosms-login" class="form-label">API LOGIN</label>
+        <label for="promosms-login" class="form-label">{{$("promosmsLogin")}}</label>
         <input id="promosms-login" v-model="$parent.notification.promosmsLogin" type="text" class="form-control" required>
-        <label for="promosms-key" class="form-label">API PASSWORD</label>
+        <label for="promosms-key" class="form-label">{{$("promosmsPassword")}}</label>
         <HiddenInput id="promosms-key" v-model="$parent.notification.promosmsPassword" :required="true" autocomplete="one-time-code"></HiddenInput>
     </div>
     <div class="mb-3">
diff --git a/src/components/notifications/Pushover.vue b/src/components/notifications/Pushover.vue
index ee6276dd..83261deb 100644
--- a/src/components/notifications/Pushover.vue
+++ b/src/components/notifications/Pushover.vue
@@ -18,28 +18,29 @@
         </select>
         <label for="pushover-sound" class="form-label">{{ $t("Notification Sound") }}</label>
         <select id="pushover-sound" v-model="$parent.notification.pushoversounds" class="form-select">
-            <option>pushover</option>
-            <option>bike</option>
-            <option>bugle</option>
-            <option>cashregister</option>
-            <option>classical</option>
-            <option>cosmic</option>
-            <option>falling</option>
-            <option>gamelan</option>
-            <option>incoming</option>
-            <option>intermission</option>
-            <option>mechanical</option>
-            <option>pianobar</option>
-            <option>siren</option>
-            <option>spacealarm</option>
-            <option>tugboat</option>
-            <option>alien</option>
-            <option>climb</option>
-            <option>persistent</option>
-            <option>echo</option>
-            <option>updown</option>
-            <option>vibrate</option>
-            <option>none</option>
+            <option>{{ $t("pushoversounds pushover") }}</option>
+            <option>{{ $t("pushoversounds bike") }}</option>
+            <option>{{ $t("pushoversounds bugle") }}</option>
+            <option>{{ $t("pushoversounds cashregister") }}</option>
+            <option>{{ $t("pushoversounds classical") }}</option>
+            <option>{{ $t("pushoversounds cosmic") }}</option>
+            <option>{{ $t("pushoversounds falling") }}</option>
+            <option>{{ $t("pushoversounds gamelan") }}</option>
+            <option>{{ $t("pushoversounds incoming") }}</option>
+            <option>{{ $t("pushoversounds intermission") }}</option>
+            <option>{{ $t("pushoversounds magic") }}</option>
+            <option>{{ $t("pushoversounds mechanical") }}</option>
+            <option>{{ $t("pushoversounds pianobar") }}</option>
+            <option>{{ $t("pushoversounds siren") }}</option>
+            <option>{{ $t("pushoversounds spacealarm") }}</option>
+            <option>{{ $t("pushoversounds tugboat") }}</option>
+            <option>{{ $t("pushoversounds alien") }}</option>
+            <option>{{ $t("pushoversounds climb") }}</option>
+            <option>{{ $t("pushoversounds persistent") }}</option>
+            <option>{{ $t("pushoversounds echo") }}</option>
+            <option>{{ $t("pushoversounds updown") }}</option>
+            <option>{{ $t("pushoversounds vibrate") }}</option>
+            <option>{{ $t("pushoversounds none") }}</option>
         </select>
         <div class="form-text">
             <span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
diff --git a/src/components/notifications/Pushy.vue b/src/components/notifications/Pushy.vue
index 26f404d2..3537eb4f 100644
--- a/src/components/notifications/Pushy.vue
+++ b/src/components/notifications/Pushy.vue
@@ -1,11 +1,11 @@
 <template>
     <div class="mb-3">
-        <label for="pushy-app-token" class="form-label">API_KEY</label>
+        <label for="pushy-app-token" class="form-label">{{ $t("pushyAPIKey") }}</label>
         <HiddenInput id="pushy-app-token" v-model="$parent.notification.pushyAPIKey" :required="true" autocomplete="one-time-code"></HiddenInput>
     </div>
 
     <div class="mb-3">
-        <label for="pushy-user-key" class="form-label">USER_TOKEN</label>
+        <label for="pushy-user-key" class="form-label">{{ $t("pushyToken") }}</label>
         <div class="input-group mb-3">
             <HiddenInput id="pushy-user-key" v-model="$parent.notification.pushyToken" :required="true" autocomplete="one-time-code"></HiddenInput>
         </div>
diff --git a/src/components/notifications/TechulusPush.vue b/src/components/notifications/TechulusPush.vue
index 918f8be6..86d4e5fe 100644
--- a/src/components/notifications/TechulusPush.vue
+++ b/src/components/notifications/TechulusPush.vue
@@ -1,6 +1,6 @@
 <template>
     <div class="mb-3">
-        <label for="push-api-key" class="form-label">API_KEY</label>
+        <label for="push-api-key" class="form-label">{{ $t("API Key") }}</label>
         <HiddenInput id="push-api-key" v-model="$parent.notification.pushAPIKey" :required="true" autocomplete="one-time-code"></HiddenInput>
     </div>
 
diff --git a/src/components/settings/About.vue b/src/components/settings/About.vue
index b4d7b659..a71e38af 100644
--- a/src/components/settings/About.vue
+++ b/src/components/settings/About.vue
@@ -9,11 +9,11 @@
 
             <div class="mt-1">
                 <div class="form-check">
-                    <label><input v-model="settings.checkUpdate" type="checkbox" @change="saveSettings()" /> Show update if available</label>
+                    <label><input v-model="settings.checkUpdate" type="checkbox" @change="saveSettings()" /> {{ $t("Show update if available") }}</label>
                 </div>
 
                 <div class="form-check">
-                    <label><input v-model="settings.checkBeta" type="checkbox" :disabled="!settings.checkUpdate" @change="saveSettings()" /> Also check beta release</label>
+                    <label><input v-model="settings.checkBeta" type="checkbox" :disabled="!settings.checkUpdate" @change="saveSettings()" /> {{ $t("Also check beta release") }}</label>
                 </div>
             </div>
         </div>
diff --git a/src/languages/en.js b/src/languages/en.js
index ab73ce35..ad97cbef 100644
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -464,4 +464,54 @@ export default {
     "Domain Names": "Domain Names",
     signedInDisp: "Signed in as {0}",
     signedInDispDisabled: "Auth Disabled.",
+    "Certificate Expiry Notification": "Certificate Expiry Notification",
+    "API Username": "API Username",
+    "API Key": "API Key",
+    "Recipient Number": "Recipient Number",
+    "From Name/Number": "From Name/Number",
+    "Leave blank to use a shared sender number.": "Leave blank to use a shared sender number.",
+    "Octopush API Version": "Octopush API Version",
+    "Legacy Octopush-DM": "Legacy Octopush-DM",
+    "endpoint": "endpoint",
+    octopushAPIKey: "\"API key\" from HTTP API credentials in control panel",
+    octopushLogin: "\"Login\" from HTTP API credentials in control panel",
+    promosmsLogin: "API Login Name",
+    promosmsPassword: "API Password",
+    "pushoversounds pushover": "Pushover (default)",
+    "pushoversounds bike": "Bike",
+    "pushoversounds bugle": "Bugle",
+    "pushoversounds cashregister": "Cash Register",
+    "pushoversounds classical": "Classical",
+    "pushoversounds cosmic": "Cosmic",
+    "pushoversounds falling": "Falling",
+    "pushoversounds gamelan": "Gamelan",
+    "pushoversounds incoming": "Incoming",
+    "pushoversounds intermission": "Intermission",
+    "pushoversounds magic": "Magic",
+    "pushoversounds mechanical": "Mechanical",
+    "pushoversounds pianobar": "Piano Bar",
+    "pushoversounds siren": "Siren",
+    "pushoversounds spacealarm": "Space Alarm",
+    "pushoversounds tugboat": "Tug Boat",
+    "pushoversounds alien": "Alien Alarm (long)",
+    "pushoversounds climb": "Climb (long)",
+    "pushoversounds persistent": "Persistent (long)",
+    "pushoversounds echo": "Pushover Echo (long)",
+    "pushoversounds updown": "Up Down (long)",
+    "pushoversounds vibrate": "Vibrate Only",
+    "pushoversounds none": "None (silent)",
+    pushyAPIKey: "Secret API Key",
+    pushyToken: "Device token",
+    "Show update if available": "Show update if available",
+    "Also check beta release": "Also check beta release",
+    "Using a Reverse Proxy?": "Using a Reverse Proxy?",
+    "Check how to config it for WebSocket": "Check how to config it for WebSocket",
+    "Steam Game Server": "Steam Game Server",
+    "Most likely causes:": "Most likely causes:",
+    "The resource is no longer available.": "The resource is no longer available.",
+    "There might be a typing error in the address.": "There might be a typing error in the address.",
+    "What you can try:": "What you can try:",
+    "Retype the address.": "Retype the address.",
+    "Go back to the previous page.": "Go back to the previous page.",
+    "Coming Soon": "Coming Soon",
 };
diff --git a/src/languages/zh-CN.js b/src/languages/zh-CN.js
index f50f50f1..9e5fcba1 100644
--- a/src/languages/zh-CN.js
+++ b/src/languages/zh-CN.js
@@ -469,4 +469,54 @@ export default {
     "Footer Text": "底部自定义文本",
     "Show Powered By": "显示 Powered By",
     "Domain Names": "域名",
+    "Certificate Expiry Notification": "证书到期时通知",
+    "API Username": "API 凭证 Username",
+    "API Key": "API 凭证 Key",
+    "Recipient Number": "收件人手机号码",
+    "From Name/Number": "发件人名称/手机号码",
+    "Leave blank to use a shared sender number.": "留空以使用平台共享的发件人手机号码",
+    "Octopush API Version": "Octopush API 版本",
+    "Legacy Octopush-DM": "旧版本 Octopush-DM",
+    "endpoint": "接入点",
+    octopushAPIKey: "控制台 HTTP API credentials 里的 \"API key\"",
+    octopushLogin: "控制台 HTTP API credentials 里的 \"Login\"",
+    promosmsLogin: "API 登录名",
+    promosmsPassword: "API 密码",
+    "pushoversounds pushover": "Pushover(默认)",
+    "pushoversounds bike": "Bike",
+    "pushoversounds bugle": "Bugle",
+    "pushoversounds cashregister": "Cash Register",
+    "pushoversounds classical": "Classical",
+    "pushoversounds cosmic": "Cosmic",
+    "pushoversounds falling": "Falling",
+    "pushoversounds gamelan": "Gamelan",
+    "pushoversounds incoming": "Incoming",
+    "pushoversounds intermission": "Intermission",
+    "pushoversounds magic": "Magic",
+    "pushoversounds mechanical": "Mechanical",
+    "pushoversounds pianobar": "Piano Bar",
+    "pushoversounds siren": "Siren",
+    "pushoversounds spacealarm": "Space Alarm",
+    "pushoversounds tugboat": "Tug Boat",
+    "pushoversounds alien": "Alien Alarm(长铃声)",
+    "pushoversounds climb": "Climb(长铃声)",
+    "pushoversounds persistent": "Persistent(长铃声)",
+    "pushoversounds echo": "Pushover Echo(长铃声)",
+    "pushoversounds updown": "Up Down(长铃声)",
+    "pushoversounds vibrate": "仅震动",
+    "pushoversounds none": "无(禁音)",
+    pushyAPIKey: "API 密钥",
+    pushyToken: "设备 Token",
+    "Show update if available": "有更新时通知",
+    "Also check beta release": "一并检查 Beta 版更新",
+    "Using a Reverse Proxy?": "正在使用反向代理?",
+    "Check how to config it for WebSocket": "查看如何将反向代理与 WebSocket 一起使用",
+    "Steam Game Server": "Steam 游戏服务器",
+    "Most likely causes:": "最可能的原因:",
+    "The resource is no longer available.": "您所请求的资源已不再可用;",
+    "There might be a typing error in the address.": "您输入的地址可能有误。",
+    "What you can try:": "您可以尝试以下操作:",
+    "Retype the address.": "重新输入地址;",
+    "Go back to the previous page.": "返回到上一页面。",
+    "Coming Soon": "即将推出",
 };
diff --git a/src/layouts/Layout.vue b/src/layouts/Layout.vue
index 7f9598bf..38addb37 100644
--- a/src/layouts/Layout.vue
+++ b/src/layouts/Layout.vue
@@ -4,7 +4,7 @@
             <div class="container-fluid">
                 {{ $root.connectionErrorMsg }}
                 <div v-if="$root.showReverseProxyGuide">
-                    Using a Reverse Proxy? <a href="https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy" target="_blank">Check how to config it for WebSocket</a>
+                    {{ $t("Using a Reverse Proxy?") }} <a href="https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy" target="_blank">{{ $t("Check how to config it for WebSocket") }}</a>
                 </div>
             </div>
         </div>
diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index 39c114ad..0a753410 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -30,7 +30,7 @@
                                         Push
                                     </option>
                                     <option value="steam">
-                                        Steam Game Server
+                                        {{ $t("Steam Game Server") }}
                                     </option>
                                     <option value="mqtt">
                                         MQTT
diff --git a/src/pages/NotFound.vue b/src/pages/NotFound.vue
index 16ba8a55..410c16a8 100644
--- a/src/pages/NotFound.vue
+++ b/src/pages/NotFound.vue
@@ -22,16 +22,16 @@
             </div>
 
             <div class="guide">
-                Most likely causes:
+                {{ $t("Most likely causes:") }}
                 <ul>
-                    <li>The resource is no longer available.</li>
-                    <li>There might be a typing error in the address.</li>
+                    <li>{{ $t("The resource is no longer available.") }}</li>
+                    <li>{{ $t("There might be a typing error in the address.") }}</li>
                 </ul>
 
-                What you can try:<br />
+                {{ $t("What you can try:") }}<br />
                 <ul>
-                    <li>Retype the address.</li>
-                    <li><a href="#" class="go-back" @click="goBack()">Go back to the previous page.</a></li>
+                    <li>{{ $t("Retype the address.") }}</li>
+                    <li><a href="#" class="go-back" @click="goBack()">{{ $t("Go back to the previous page.") }}</a></li>
                 </ul>
             </div>
         </div>
diff --git a/src/pages/StatusPage.vue b/src/pages/StatusPage.vue
index 8cda7ebb..546aa942 100644
--- a/src/pages/StatusPage.vue
+++ b/src/pages/StatusPage.vue
@@ -45,7 +45,7 @@
                 </div>
 
                 <div v-if="false" class="my-3">
-                    <label for="password" class="form-label">{{ $t("Password") }} <sup>Coming Soon</sup></label>
+                    <label for="password" class="form-label">{{ $t("Password") }} <sup>{{ $t("Coming Soon") }}</sup></label>
                     <input id="password" v-model="config.password" disabled type="password" autocomplete="new-password" class="form-control">
                 </div>
 

From 7dd5f5ea0dad64b7b65628a8f44b4fe2bbad9845 Mon Sep 17 00:00:00 2001
From: AnnAngela-work <naganjue@vip.qq.com>
Date: Fri, 29 Apr 2022 20:26:56 +0800
Subject: [PATCH 12/25] Improve translation

---
 src/components/notifications/ClickSendSMS.vue | 7 +++----
 src/languages/en.js                           | 1 +
 src/languages/zh-CN.js                        | 3 ++-
 3 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/src/components/notifications/ClickSendSMS.vue b/src/components/notifications/ClickSendSMS.vue
index 09d3201c..dbd4d0aa 100644
--- a/src/components/notifications/ClickSendSMS.vue
+++ b/src/components/notifications/ClickSendSMS.vue
@@ -1,10 +1,9 @@
 <template>
     <div class="mb-3">
         <label for="clicksendsms-login" class="form-label">{{ $t("API Username") }}</label>
-        <div class="form-text">
-            {{ $t("apiCredentials") }}
+        <i18n-t tag="div" class="form-text" keypath="wayToGetClickSendSMSToken">
             <a href="http://dashboard.clicksend.com/account/subaccounts" target="_blank">{{ $t("here") }}</a>
-        </div>
+        </i18n-t>
         <input id="clicksendsms-login" v-model="$parent.notification.clicksendsmsLogin" type="text" class="form-control" required>
         <label for="clicksendsms-key" class="form-label">{{ $t("API Key") }}</label>
         <HiddenInput id="clicksendsms-key" v-model="$parent.notification.clicksendsmsPassword" :required="true" autocomplete="one-time-code"></HiddenInput>
@@ -21,7 +20,7 @@
     </div>
     <div class="mb-3">
         <label for="clicksendsms-sender-name" class="form-label">{{ $t("From Name/Number") }} -
-            <i18n-t tag="span" keypath="Read more:"><a href="https://help.clicksend.com/article/4kgj7krx00-what-is-a-sender-id-or-sender-number" target="_blank">https://help.clicksend.com/article/4kgj7krx00-what-is-a-sender-id-or-sender-number</a></i18n-t>
+            <a href="https://help.clicksend.com/article/4kgj7krx00-what-is-a-sender-id-or-sender-number" target="_blank">{{ $t("Read more") }}</a>
         </label>
         <input id="clicksendsms-sender-name" v-model="$parent.notification.clicksendsmsSenderName" type="text" minlength="3" maxlength="11" class="form-control">
         <div class="form-text">{{ $t("Leave blank to use a shared sender number.") }}</div>
diff --git a/src/languages/en.js b/src/languages/en.js
index ad97cbef..af6ec32b 100644
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -514,4 +514,5 @@ export default {
     "Retype the address.": "Retype the address.",
     "Go back to the previous page.": "Go back to the previous page.",
     "Coming Soon": "Coming Soon",
+    wayToGetClickSendSMSToken: "You can get API Username and API Key from {0} .",
 };
diff --git a/src/languages/zh-CN.js b/src/languages/zh-CN.js
index 9e5fcba1..691ec120 100644
--- a/src/languages/zh-CN.js
+++ b/src/languages/zh-CN.js
@@ -441,7 +441,7 @@ export default {
     "No Proxy": "无代理",
     "HTTP Basic Auth": "HTTP 基础身份验证",
     "New Status Page": "新的状态页",
-    "Page Not Found": "状态页未找到",
+    "Page Not Found": "未找到该页面",
     "Reverse Proxy": "反向代理",
     "Subject:": "颁发给:",
     "Valid To:": "有效期至:",
@@ -519,4 +519,5 @@ export default {
     "Retype the address.": "重新输入地址;",
     "Go back to the previous page.": "返回到上一页面。",
     "Coming Soon": "即将推出",
+    wayToGetClickSendSMSToken: "您可以从 {0} 获取 API 凭证 Username 和 凭证 Key。",
 };

From e82fc1df61f800098315f5937e667ae810e523b9 Mon Sep 17 00:00:00 2001
From: AnnAngela-work <naganjue@vip.qq.com>
Date: Fri, 29 Apr 2022 20:31:36 +0800
Subject: [PATCH 13/25] Match up en lang file

---
 src/languages/zh-CN.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/languages/zh-CN.js b/src/languages/zh-CN.js
index 691ec120..52050a46 100644
--- a/src/languages/zh-CN.js
+++ b/src/languages/zh-CN.js
@@ -88,7 +88,7 @@ export default {
     Dark: "黑暗",
     Auto: "自动",
     "Theme - Heartbeat Bar": "主题 - 心跳栏",
-    Normal: "正常", // 此处还供 Gorush 的通知优先级功能使用,不应翻译为“正常显示”
+    Normal: "正常",
     Bottom: "靠下",
     None: "不显示",
     Timezone: "时区",
@@ -398,11 +398,9 @@ export default {
     Invalid: "无效",
     AccessKeyId: "AccessKey ID",
     SecretAccessKey: "AccessKey Secret",
-    /* 以下为阿里云短信服务 API Dysms#SendSms 的参数 */
     PhoneNumbers: "PhoneNumbers",
     TemplateCode: "TemplateCode",
     SignName: "SignName",
-    /* 以上为阿里云短信服务 API Dysms#SendSms 的参数 */
     "Bark Endpoint": "Bark 接入点",
     "Device Token": "Apple Device Token",
     Platform: "平台",
@@ -477,7 +475,7 @@ export default {
     "Leave blank to use a shared sender number.": "留空以使用平台共享的发件人手机号码",
     "Octopush API Version": "Octopush API 版本",
     "Legacy Octopush-DM": "旧版本 Octopush-DM",
-    "endpoint": "接入点",
+    endpoint: "接入点",
     octopushAPIKey: "控制台 HTTP API credentials 里的 \"API key\"",
     octopushLogin: "控制台 HTTP API credentials 里的 \"Login\"",
     promosmsLogin: "API 登录名",
@@ -520,4 +518,6 @@ export default {
     "Go back to the previous page.": "返回到上一页面。",
     "Coming Soon": "即将推出",
     wayToGetClickSendSMSToken: "您可以从 {0} 获取 API 凭证 Username 和 凭证 Key。",
+    signedInDisp: "当前用户: {0}",
+    signedInDispDisabled: "已禁用身份验证",
 };

From 65ea2e6aeb1ecb1bb626be36f176e48a78e9408d Mon Sep 17 00:00:00 2001
From: AnnAngela <naganjue@vip.qq.com>
Date: Fri, 29 Apr 2022 21:42:48 +0800
Subject: [PATCH 14/25] Update src/components/notifications/PromoSMS.vue

Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
---
 src/components/notifications/PromoSMS.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/notifications/PromoSMS.vue b/src/components/notifications/PromoSMS.vue
index c11ed559..29192aab 100644
--- a/src/components/notifications/PromoSMS.vue
+++ b/src/components/notifications/PromoSMS.vue
@@ -1,6 +1,6 @@
 <template>
     <div class="mb-3">
-        <label for="promosms-login" class="form-label">{{$("promosmsLogin")}}</label>
+        <label for="promosms-login" class="form-label">{{ $("promosmsLogin") }}</label>
         <input id="promosms-login" v-model="$parent.notification.promosmsLogin" type="text" class="form-control" required>
         <label for="promosms-key" class="form-label">{{$("promosmsPassword")}}</label>
         <HiddenInput id="promosms-key" v-model="$parent.notification.promosmsPassword" :required="true" autocomplete="one-time-code"></HiddenInput>

From 5a069b278deafc766e37b1ef989920e993bf1c58 Mon Sep 17 00:00:00 2001
From: AnnAngela <naganjue@vip.qq.com>
Date: Fri, 29 Apr 2022 21:42:54 +0800
Subject: [PATCH 15/25] Update src/components/notifications/PromoSMS.vue

Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
---
 src/components/notifications/PromoSMS.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/notifications/PromoSMS.vue b/src/components/notifications/PromoSMS.vue
index 29192aab..91b4d96d 100644
--- a/src/components/notifications/PromoSMS.vue
+++ b/src/components/notifications/PromoSMS.vue
@@ -2,7 +2,7 @@
     <div class="mb-3">
         <label for="promosms-login" class="form-label">{{ $("promosmsLogin") }}</label>
         <input id="promosms-login" v-model="$parent.notification.promosmsLogin" type="text" class="form-control" required>
-        <label for="promosms-key" class="form-label">{{$("promosmsPassword")}}</label>
+        <label for="promosms-key" class="form-label">{{ $("promosmsPassword") }}</label>
         <HiddenInput id="promosms-key" v-model="$parent.notification.promosmsPassword" :required="true" autocomplete="one-time-code"></HiddenInput>
     </div>
     <div class="mb-3">

From 9f493bbec7a00c66295ca58e274c951dd465470d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Karel=20Kr=C3=BDda?= <karel.kryda@gmail.com>
Date: Mon, 9 May 2022 21:02:29 +0200
Subject: [PATCH 16/25] clone master branch

---
 CNAME           | 2 +-
 public/icon.svg | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/CNAME b/CNAME
index a5348b07..40f0b545 100644
--- a/CNAME
+++ b/CNAME
@@ -1 +1 @@
-git.kuma.pet
\ No newline at end of file
+git.kuma.pet;
diff --git a/public/icon.svg b/public/icon.svg
index 825c344e..9c0bc6ca 100644
--- a/public/icon.svg
+++ b/public/icon.svg
@@ -1,3 +1,3 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" viewBox="0 0 640 640" width="640" height="640"><defs><path d="M407.55 916.24C471.25 916.24 522.89 967.88 522.89 1031.57C522.89 1113.88 522.89 1245.44 522.89 1327.74C522.89 1391.44 471.25 1443.08 407.55 1443.08C325.25 1443.08 193.68 1443.08 111.38 1443.08C47.69 1443.08 -3.95 1391.44 -3.95 1327.74C-3.95 1245.44 -3.95 1113.88 -3.95 1031.57C-3.95 967.88 47.69 916.24 111.38 916.24C193.68 916.24 325.25 916.24 407.55 916.24Z" id="a1LdTs1gvU"></path><linearGradient id="gradientcoH7TNh19" gradientUnits="userSpaceOnUse" x1="256.07" y1="1132.14" x2="609.11" y2="1480.42"><stop style="stop-color: #c2efd2;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #8ff0e5;stop-opacity: 1" offset="100%"></stop></linearGradient><path d="M-467.41 394.63C-467.41 554.76 -597.42 684.76 -757.55 684.76C-917.68 684.76 -1047.69 554.76 -1047.69 394.63C-1047.69 234.5 -917.68 104.49 -757.55 104.49C-597.42 104.49 -467.41 234.5 -467.41 394.63Z" id="a1uaEBd4xM"></path><path d="M-96.99 -586.14C-57.24 -619.85 -5.79 -604.75 19.26 -580.46C31.43 -568.66 56.57 -546.36 40.97 -491.67C32.76 -462.87 10.41 -436.4 -26.05 -412.27C-15.07 -377.85 -5.6 -344.76 2.36 -313C14.29 -265.36 13.55 -189.67 -26.05 -155.4C-67.27 -119.73 -166.91 -104.09 -234.24 -103.09C-301.57 -102.1 -406.19 -113.09 -461.6 -155.4C-517.01 -197.7 -512.24 -257.07 -498.04 -313C-488.58 -350.28 -476.43 -383.38 -461.6 -412.27C-505.54 -441.3 -530.54 -467.76 -536.6 -491.67C-545.68 -527.54 -530.93 -565.61 -501.12 -586.14C-471.31 -606.67 -435.18 -606.9 -400.45 -586.14C-377.3 -572.3 -354.79 -542.13 -332.92 -495.62C-287.85 -505.25 -254.96 -509.57 -234.24 -508.6C-214.74 -507.68 -186.57 -503.36 -149.72 -495.62C-135.81 -537.95 -118.23 -568.12 -96.99 -586.14Z" id="f8p7QlEjN3"></path><linearGradient id="gradienta4Tg99ZOOp" gradientUnits="userSpaceOnUse" x1="-440.25" y1="-388.59" x2="-100.49" y2="-147.33"><stop style="stop-color: #5cdd8b;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #7ae6a1;stop-opacity: 1" offset="100%"></stop></linearGradient><path d="M-86.03 -10.69C-61.35 -10.69 -41.34 9.32 -41.34 34.01C-41.34 119.07 -41.34 329.58 -41.34 414.65C-41.34 439.33 -61.35 459.34 -86.03 459.34C-136.01 459.34 -241.25 459.34 -291.23 459.34C-315.92 459.34 -335.93 439.33 -335.93 414.65C-335.93 329.58 -335.93 119.07 -335.93 34.01C-335.93 9.32 -315.92 -10.69 -291.23 -10.69C-241.25 -10.69 -136.01 -10.69 -86.03 -10.69Z" id="d32ZZRxd1S"></path><linearGradient id="gradientb1JxIe4xUm" gradientUnits="userSpaceOnUse" x1="-791.65" y1="-33.27" x2="892.1" y2="418.94"><stop style="stop-color: #5cdd8b;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #5ae98f;stop-opacity: 1" offset="100%"></stop></linearGradient><path d="M-257.95 458.12C-247.92 449.62 -234.93 453.43 -228.61 459.56C-225.54 462.54 -219.19 468.17 -223.13 481.97C-225.2 489.24 -230.84 495.92 -240.05 502.01C-237.27 510.7 -234.88 519.06 -232.88 527.07C-229.86 539.1 -230.05 558.21 -240.05 566.86C-250.45 575.86 -275.6 579.81 -292.6 580.06C-309.6 580.31 -336.01 577.54 -349.99 566.86C-363.98 556.18 -362.77 541.19 -359.19 527.07C-356.8 517.66 -353.73 509.31 -349.99 502.01C-361.08 494.69 -367.39 488.01 -368.92 481.97C-371.22 472.92 -367.49 463.31 -359.97 458.12C-352.44 452.94 -343.32 452.88 -334.56 458.12C-328.71 461.62 -323.03 469.23 -317.51 480.97C-306.13 478.54 -297.83 477.45 -292.6 477.7C-287.68 477.93 -280.56 479.02 -271.26 480.97C-267.75 470.29 -263.32 462.67 -257.95 458.12Z" id="b19LRRbPrG"></path><path d="M490.4 235.64C544.09 358.38 544.09 435.34 490.4 466.5C409.85 513.24 199.96 527.49 139.54 455.64C99.26 407.74 99.26 334.4 139.54 235.64C180.5 168.18 238.71 134.45 314.17 134.45C389.64 134.45 448.38 168.18 490.4 235.64Z" id="bN5StdyPU"></path><linearGradient id="gradientb1HT15TsY0" gradientUnits="userSpaceOnUse" x1="259.78" y1="261.15" x2="463.85" y2="456.49"><stop style="stop-color: #5cdd8b;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #86e6a9;stop-opacity: 1" offset="100%"></stop></linearGradient><path d="M393.81 -775.89C428.26 -748.09 439.99 -725.54 429 -708.22C412.51 -682.24 353.16 -646.07 324.5 -657.93C305.39 -665.83 294.22 -687.32 290.97 -722.41C292.69 -748.43 304.61 -767.19 326.73 -778.69C348.85 -790.19 371.21 -789.26 393.81 -775.89Z" id="arh6miPP2"></path><linearGradient id="gradientc2g6rBSAiq" gradientUnits="userSpaceOnUse" x1="330.1" y1="-733.26" x2="419.69" y2="-707.1"><stop style="stop-color: #5cdd8b;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #86e6a9;stop-opacity: 1" offset="100%"></stop></linearGradient><path d="M675.36 -369.24C669.97 -325.31 657.02 -303.43 636.51 -303.61C605.74 -303.87 543.67 -335.15 538.59 -365.74C535.2 -386.14 547.54 -406.99 575.61 -428.29C598.61 -440.58 620.83 -440.37 642.29 -427.67C663.74 -414.97 674.77 -395.49 675.36 -369.24Z" id="a2VENFzCvL"></path><linearGradient id="gradientc18GuJy4sZ" gradientUnits="userSpaceOnUse" x1="605.5" y1="-400.8" x2="630.64" y2="-310.92"><stop style="stop-color: #5cdd8b;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #86e6a9;stop-opacity: 1" offset="100%"></stop></linearGradient></defs><g><g><g><use xlink:href="#a1LdTs1gvU" opacity="1" fill="url(#gradientcoH7TNh19)"></use></g><g><use xlink:href="#a1uaEBd4xM" opacity="1" fill="#ebf0ed" fill-opacity="1"></use></g><g><use xlink:href="#f8p7QlEjN3" opacity="1" fill="url(#gradienta4Tg99ZOOp)"></use><g><use xlink:href="#f8p7QlEjN3" opacity="1" fill-opacity="0" stroke="#ffffff" stroke-width="98" stroke-opacity="0.57"></use></g></g><g><use xlink:href="#d32ZZRxd1S" opacity="1" fill="url(#gradientb1JxIe4xUm)"></use><g><use xlink:href="#d32ZZRxd1S" opacity="1" fill-opacity="0" stroke="#f2f2f2" stroke-width="60" stroke-opacity="0.51"></use></g></g><g><use xlink:href="#b19LRRbPrG" opacity="1" fill="#d8ad9a" fill-opacity="1"></use><g><use xlink:href="#b19LRRbPrG" opacity="1" fill-opacity="0" stroke="#ffffff" stroke-width="17" stroke-opacity="1"></use></g></g><g><use xlink:href="#bN5StdyPU" opacity="1" fill="url(#gradientb1HT15TsY0)"></use><g><use xlink:href="#bN5StdyPU" opacity="1" fill-opacity="0" stroke="#f2f2f2" stroke-width="200" stroke-opacity="0.51"></use></g></g><g><use xlink:href="#arh6miPP2" opacity="1" fill="url(#gradientc2g6rBSAiq)"></use></g><g><use xlink:href="#a2VENFzCvL" opacity="1" fill="url(#gradientc18GuJy4sZ)"></use></g></g></g></svg>
\ No newline at end of file
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" viewBox="0 0 640 640" width="640" height="640"><defs><path d="M407.55 916.24C471.25 916.24 522.89 967.88 522.89 1031.57C522.89 1113.88 522.89 1245.44 522.89 1327.74C522.89 1391.44 471.25 1443.08 407.55 1443.08C325.25 1443.08 193.68 1443.08 111.38 1443.08C47.69 1443.08 -3.95 1391.44 -3.95 1327.74C-3.95 1245.44 -3.95 1113.88 -3.95 1031.57C-3.95 967.88 47.69 916.24 111.38 916.24C193.68 916.24 325.25 916.24 407.55 916.24Z" id="a1LdTs1gvU"></path><linearGradient id="gradientcoH7TNh19" gradientUnits="userSpaceOnUse" x1="256.07" y1="1132.14" x2="609.11" y2="1480.42"><stop style="stop-color: #c2efd2;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #8ff0e5;stop-opacity: 1" offset="100%"></stop></linearGradient><path d="M-467.41 394.63C-467.41 554.76 -597.42 684.76 -757.55 684.76C-917.68 684.76 -1047.69 554.76 -1047.69 394.63C-1047.69 234.5 -917.68 104.49 -757.55 104.49C-597.42 104.49 -467.41 234.5 -467.41 394.63Z" id="a1uaEBd4xM"></path><path d="M-96.99 -586.14C-57.24 -619.85 -5.79 -604.75 19.26 -580.46C31.43 -568.66 56.57 -546.36 40.97 -491.67C32.76 -462.87 10.41 -436.4 -26.05 -412.27C-15.07 -377.85 -5.6 -344.76 2.36 -313C14.29 -265.36 13.55 -189.67 -26.05 -155.4C-67.27 -119.73 -166.91 -104.09 -234.24 -103.09C-301.57 -102.1 -406.19 -113.09 -461.6 -155.4C-517.01 -197.7 -512.24 -257.07 -498.04 -313C-488.58 -350.28 -476.43 -383.38 -461.6 -412.27C-505.54 -441.3 -530.54 -467.76 -536.6 -491.67C-545.68 -527.54 -530.93 -565.61 -501.12 -586.14C-471.31 -606.67 -435.18 -606.9 -400.45 -586.14C-377.3 -572.3 -354.79 -542.13 -332.92 -495.62C-287.85 -505.25 -254.96 -509.57 -234.24 -508.6C-214.74 -507.68 -186.57 -503.36 -149.72 -495.62C-135.81 -537.95 -118.23 -568.12 -96.99 -586.14Z" id="f8p7QlEjN3"></path><linearGradient id="gradienta4Tg99ZOOp" gradientUnits="userSpaceOnUse" x1="-440.25" y1="-388.59" x2="-100.49" y2="-147.33"><stop style="stop-color: #5cdd8b;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #7ae6a1;stop-opacity: 1" offset="100%"></stop></linearGradient><path d="M-86.03 -10.69C-61.35 -10.69 -41.34 9.32 -41.34 34.01C-41.34 119.07 -41.34 329.58 -41.34 414.65C-41.34 439.33 -61.35 459.34 -86.03 459.34C-136.01 459.34 -241.25 459.34 -291.23 459.34C-315.92 459.34 -335.93 439.33 -335.93 414.65C-335.93 329.58 -335.93 119.07 -335.93 34.01C-335.93 9.32 -315.92 -10.69 -291.23 -10.69C-241.25 -10.69 -136.01 -10.69 -86.03 -10.69Z" id="d32ZZRxd1S"></path><linearGradient id="gradientb1JxIe4xUm" gradientUnits="userSpaceOnUse" x1="-791.65" y1="-33.27" x2="892.1" y2="418.94"><stop style="stop-color: #5cdd8b;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #5ae98f;stop-opacity: 1" offset="100%"></stop></linearGradient><path d="M-257.95 458.12C-247.92 449.62 -234.93 453.43 -228.61 459.56C-225.54 462.54 -219.19 468.17 -223.13 481.97C-225.2 489.24 -230.84 495.92 -240.05 502.01C-237.27 510.7 -234.88 519.06 -232.88 527.07C-229.86 539.1 -230.05 558.21 -240.05 566.86C-250.45 575.86 -275.6 579.81 -292.6 580.06C-309.6 580.31 -336.01 577.54 -349.99 566.86C-363.98 556.18 -362.77 541.19 -359.19 527.07C-356.8 517.66 -353.73 509.31 -349.99 502.01C-361.08 494.69 -367.39 488.01 -368.92 481.97C-371.22 472.92 -367.49 463.31 -359.97 458.12C-352.44 452.94 -343.32 452.88 -334.56 458.12C-328.71 461.62 -323.03 469.23 -317.51 480.97C-306.13 478.54 -297.83 477.45 -292.6 477.7C-287.68 477.93 -280.56 479.02 -271.26 480.97C-267.75 470.29 -263.32 462.67 -257.95 458.12Z" id="b19LRRbPrG"></path><path d="M490.4 235.64C544.09 358.38 544.09 435.34 490.4 466.5C409.85 513.24 199.96 527.49 139.54 455.64C99.26 407.74 99.26 334.4 139.54 235.64C180.5 168.18 238.71 134.45 314.17 134.45C389.64 134.45 448.38 168.18 490.4 235.64Z" id="bN5StdyPU"></path><linearGradient id="gradientb1HT15TsY0" gradientUnits="userSpaceOnUse" x1="259.78" y1="261.15" x2="463.85" y2="456.49"><stop style="stop-color: #5cdd8b;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #86e6a9;stop-opacity: 1" offset="100%"></stop></linearGradient><path d="M393.81 -775.89C428.26 -748.09 439.99 -725.54 429 -708.22C412.51 -682.24 353.16 -646.07 324.5 -657.93C305.39 -665.83 294.22 -687.32 290.97 -722.41C292.69 -748.43 304.61 -767.19 326.73 -778.69C348.85 -790.19 371.21 -789.26 393.81 -775.89Z" id="arh6miPP2"></path><linearGradient id="gradientc2g6rBSAiq" gradientUnits="userSpaceOnUse" x1="330.1" y1="-733.26" x2="419.69" y2="-707.1"><stop style="stop-color: #5cdd8b;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #86e6a9;stop-opacity: 1" offset="100%"></stop></linearGradient><path d="M675.36 -369.24C669.97 -325.31 657.02 -303.43 636.51 -303.61C605.74 -303.87 543.67 -335.15 538.59 -365.74C535.2 -386.14 547.54 -406.99 575.61 -428.29C598.61 -440.58 620.83 -440.37 642.29 -427.67C663.74 -414.97 674.77 -395.49 675.36 -369.24Z" id="a2VENFzCvL"></path><linearGradient id="gradientc18GuJy4sZ" gradientUnits="userSpaceOnUse" x1="605.5" y1="-400.8" x2="630.64" y2="-310.92"><stop style="stop-color: #5cdd8b;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #86e6a9;stop-opacity: 1" offset="100%"></stop></linearGradient></defs><g><g><g><use xlink:href="#a1LdTs1gvU" opacity="1" fill="url(#gradientcoH7TNh19)"></use></g><g><use xlink:href="#a1uaEBd4xM" opacity="1" fill="#ebf0ed" fill-opacity="1"></use></g><g><use xlink:href="#f8p7QlEjN3" opacity="1" fill="url(#gradienta4Tg99ZOOp)"></use><g><use xlink:href="#f8p7QlEjN3" opacity="1" fill-opacity="0" stroke="#ffffff" stroke-width="98" stroke-opacity="0.57"></use></g></g><g><use xlink:href="#d32ZZRxd1S" opacity="1" fill="url(#gradientb1JxIe4xUm)"></use><g><use xlink:href="#d32ZZRxd1S" opacity="1" fill-opacity="0" stroke="#f2f2f2" stroke-width="60" stroke-opacity="0.51"></use></g></g><g><use xlink:href="#b19LRRbPrG" opacity="1" fill="#d8ad9a" fill-opacity="1"></use><g><use xlink:href="#b19LRRbPrG" opacity="1" fill-opacity="0" stroke="#ffffff" stroke-width="17" stroke-opacity="1"></use></g></g><g><use xlink:href="#bN5StdyPU" opacity="1" fill="url(#gradientb1HT15TsY0)"></use><g><use xlink:href="#bN5StdyPU" opacity="1" fill-opacity="0" stroke="#f2f2f2" stroke-width="200" stroke-opacity="0.51"></use></g></g><g><use xlink:href="#arh6miPP2" opacity="1" fill="url(#gradientc2g6rBSAiq)"></use></g><g><use xlink:href="#a2VENFzCvL" opacity="1" fill="url(#gradientc18GuJy4sZ)"></use></g></g></g></svg>

From 7acbfd24749e00c5a17ccb3af74408baf0c1e85a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Karel=20Kr=C3=BDda?= <karel.kryda@gmail.com>
Date: Mon, 9 May 2022 21:05:10 +0200
Subject: [PATCH 17/25] eslint fixes too much

---
 CNAME | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CNAME b/CNAME
index 40f0b545..44250516 100644
--- a/CNAME
+++ b/CNAME
@@ -1 +1 @@
-git.kuma.pet;
+git.kuma.pet

From 7da9f139c1bd91c80dd880c8b97c0981576c0749 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Karel=20Kr=C3=BDda?= <karel.kryda@gmail.com>
Date: Mon, 9 May 2022 21:10:12 +0200
Subject: [PATCH 18/25] Bug fix

---
 server/model/monitor.js | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/server/model/monitor.js b/server/model/monitor.js
index f2d16524..eaafb775 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -182,7 +182,7 @@ class Monitor extends BeanModel {
             // undefined if not https
             let tlsInfo = undefined;
 
-            if (!previousBeat) {
+            if (!previousBeat || this.type === "push") {
                 previousBeat = await R.findOne("heartbeat", " monitor_id = ? ORDER BY time DESC", [
                     this.id,
                 ]);
@@ -377,9 +377,6 @@ class Monitor extends BeanModel {
                     log.debug("monitor", "heartbeatCount" + heartbeatCount + " " + time);
 
                     if (heartbeatCount <= 0) {
-                        // Fix #922, since previous heartbeat could be inserted by api, it should get from database
-                        previousBeat = await Monitor.getPreviousHeartbeat(this.id);
-
                         throw new Error("No heartbeat in the time window");
                     } else {
                         // No need to insert successful heartbeat for push type, so end here

From 0cf395dfc342a350295cea73e3584bd14671b64d Mon Sep 17 00:00:00 2001
From: Louis Lam <louislam@users.noreply.github.com>
Date: Sat, 14 May 2022 14:06:35 +0800
Subject: [PATCH 19/25] Fix merge issue

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

diff --git a/src/components/HeartbeatBar.vue b/src/components/HeartbeatBar.vue
index a36c5d1c..deb2bc23 100644
--- a/src/components/HeartbeatBar.vue
+++ b/src/components/HeartbeatBar.vue
@@ -171,7 +171,7 @@ export default {
         },
 
         getBeatTitle(beat) {
-            return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : ``);
+            return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : "");
         },
 
         // Toggling the activeSibling class on hover over the current hover item

From 92a43e1f305b86e58f61ebc91cf8bc3380581520 Mon Sep 17 00:00:00 2001
From: Louis Lam <louislam@users.noreply.github.com>
Date: Sat, 14 May 2022 14:11:43 +0800
Subject: [PATCH 20/25] Make the sibling beats a bit smaller

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

diff --git a/src/components/HeartbeatBar.vue b/src/components/HeartbeatBar.vue
index deb2bc23..10b1f761 100644
--- a/src/components/HeartbeatBar.vue
+++ b/src/components/HeartbeatBar.vue
@@ -242,7 +242,7 @@ export default {
         }
 
         &.active-sibling {
-            transform: scale(1.3);
+            transform: scale(1.15);
             transition: all ease 0.15s;
         }
     }

From cec052183434f98edac6da6c81232a680050addc Mon Sep 17 00:00:00 2001
From: Louis Lam <louislam@users.noreply.github.com>
Date: Sat, 14 May 2022 14:36:40 +0800
Subject: [PATCH 21/25] [Discord] Fix ping type should no port, update better
 naming

---
 server/notification-providers/discord.js | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/server/notification-providers/discord.js b/server/notification-providers/discord.js
index c981d59d..77b04d9d 100644
--- a/server/notification-providers/discord.js
+++ b/server/notification-providers/discord.js
@@ -22,20 +22,22 @@ class Discord extends NotificationProvider {
                 return okMsg;
             }
 
-            let url;
+            let address;
 
             switch (monitorJSON["type"]) {
-                case "dns":
                 case "ping":
+                    address = monitorJSON["hostname"];
+                    break;
                 case "port":
+                case "dns":
                 case "steam":
-                    url = monitorJSON["hostname"];
+                    address = monitorJSON["hostname"];
                     if (monitorJSON["port"]) {
-                        url += ":" + monitorJSON["port"];
+                        address += ":" + monitorJSON["port"];
                     }
                     break;
                 default:
-                    url = monitorJSON["url"];
+                    address = monitorJSON["url"];
                     break;
             }
 
@@ -53,8 +55,8 @@ class Discord extends NotificationProvider {
                                 value: monitorJSON["name"],
                             },
                             {
-                                name: "Service URL",
-                                value: url,
+                                name: "Service URL / Address",
+                                value: address,
                             },
                             {
                                 name: "Time (UTC)",
@@ -89,7 +91,7 @@ class Discord extends NotificationProvider {
                             },
                             {
                                 name: "Service URL",
-                                value: url.startsWith("http") ? "[Visit Service](" + url + ")" : url,
+                                value: address.startsWith("http") ? "[Visit Service](" + address + ")" : address,
                             },
                             {
                                 name: "Time (UTC)",

From 4178b003a2fbd2cfea07cc990e610ba006c747ca Mon Sep 17 00:00:00 2001
From: AnnAngela <naganjue@vip.qq.com>
Date: Sat, 14 May 2022 15:27:55 +0800
Subject: [PATCH 22/25] fix `value`

---
 src/components/notifications/Pushover.vue | 46 +++++++++++------------
 1 file changed, 23 insertions(+), 23 deletions(-)

diff --git a/src/components/notifications/Pushover.vue b/src/components/notifications/Pushover.vue
index 83261deb..4bf6edb3 100644
--- a/src/components/notifications/Pushover.vue
+++ b/src/components/notifications/Pushover.vue
@@ -18,29 +18,29 @@
         </select>
         <label for="pushover-sound" class="form-label">{{ $t("Notification Sound") }}</label>
         <select id="pushover-sound" v-model="$parent.notification.pushoversounds" class="form-select">
-            <option>{{ $t("pushoversounds pushover") }}</option>
-            <option>{{ $t("pushoversounds bike") }}</option>
-            <option>{{ $t("pushoversounds bugle") }}</option>
-            <option>{{ $t("pushoversounds cashregister") }}</option>
-            <option>{{ $t("pushoversounds classical") }}</option>
-            <option>{{ $t("pushoversounds cosmic") }}</option>
-            <option>{{ $t("pushoversounds falling") }}</option>
-            <option>{{ $t("pushoversounds gamelan") }}</option>
-            <option>{{ $t("pushoversounds incoming") }}</option>
-            <option>{{ $t("pushoversounds intermission") }}</option>
-            <option>{{ $t("pushoversounds magic") }}</option>
-            <option>{{ $t("pushoversounds mechanical") }}</option>
-            <option>{{ $t("pushoversounds pianobar") }}</option>
-            <option>{{ $t("pushoversounds siren") }}</option>
-            <option>{{ $t("pushoversounds spacealarm") }}</option>
-            <option>{{ $t("pushoversounds tugboat") }}</option>
-            <option>{{ $t("pushoversounds alien") }}</option>
-            <option>{{ $t("pushoversounds climb") }}</option>
-            <option>{{ $t("pushoversounds persistent") }}</option>
-            <option>{{ $t("pushoversounds echo") }}</option>
-            <option>{{ $t("pushoversounds updown") }}</option>
-            <option>{{ $t("pushoversounds vibrate") }}</option>
-            <option>{{ $t("pushoversounds none") }}</option>
+            <option value="pushover">{{ $t("pushoversounds pushover") }}</option>
+            <option value="bike">{{ $t("pushoversounds bike") }}</option>
+            <option value="bugle">{{ $t("pushoversounds bugle") }}</option>
+            <option value="cashregister">{{ $t("pushoversounds cashregister") }}</option>
+            <option value="classical">{{ $t("pushoversounds classical") }}</option>
+            <option value="cosmic">{{ $t("pushoversounds cosmic") }}</option>
+            <option value="falling">{{ $t("pushoversounds falling") }}</option>
+            <option value="gamelan">{{ $t("pushoversounds gamelan") }}</option>
+            <option value="incoming">{{ $t("pushoversounds incoming") }}</option>
+            <option value="intermission">{{ $t("pushoversounds intermission") }}</option>
+            <option value="magic">{{ $t("pushoversounds magic") }}</option>
+            <option value="mechanical">{{ $t("pushoversounds mechanical") }}</option>
+            <option value="pianobar">{{ $t("pushoversounds pianobar") }}</option>
+            <option value="siren">{{ $t("pushoversounds siren") }}</option>
+            <option value="spacealarm">{{ $t("pushoversounds spacealarm") }}</option>
+            <option value="tugboat">{{ $t("pushoversounds tugboat") }}</option>
+            <option value="alien">{{ $t("pushoversounds alien") }}</option>
+            <option value="climb">{{ $t("pushoversounds climb") }}</option>
+            <option value="persistent">{{ $t("pushoversounds persistent") }}</option>
+            <option value="echo">{{ $t("pushoversounds echo") }}</option>
+            <option value="updown">{{ $t("pushoversounds updown") }}</option>
+            <option value="vibrate">{{ $t("pushoversounds vibrate") }}</option>
+            <option value="none">{{ $t("pushoversounds none") }}</option>
         </select>
         <div class="form-text">
             <span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}

From 398219f847571619bbb3e7124bf27e7915874a69 Mon Sep 17 00:00:00 2001
From: Louis Lam <louislam@users.noreply.github.com>
Date: Tue, 17 May 2022 01:03:51 +0800
Subject: [PATCH 23/25] Update to 1.16.0-beta.0

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

diff --git a/package.json b/package.json
index e9b7003b..1ba25baf 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
     "name": "uptime-kuma",
-    "version": "1.15.1",
+    "version": "1.16.0-beta.0",
     "license": "MIT",
     "repository": {
         "type": "git",

From 175556f9fc0ae5411440451d1ba7ab7d310172f8 Mon Sep 17 00:00:00 2001
From: Alexis Lefebvre <alexislefebvre@users.noreply.github.com>
Date: Mon, 16 May 2022 20:30:16 +0200
Subject: [PATCH 24/25] s/cros/CORS

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

diff --git a/src/pages/StatusPage.vue b/src/pages/StatusPage.vue
index 00857db5..22608116 100644
--- a/src/pages/StatusPage.vue
+++ b/src/pages/StatusPage.vue
@@ -688,7 +688,7 @@ export default {
         },
 
         statusPageLogoLoaded(eventPayload) {
-            // Remark: may not work in dev, due to cros
+            // Remark: may not work in dev, due to CORS
             favicon.image(eventPayload.target);
         },
 

From 9fc5a3329f7db09733c40c27726fb3b05e903c6f Mon Sep 17 00:00:00 2001
From: Louis Lam <louislam@users.noreply.github.com>
Date: Wed, 18 May 2022 20:16:50 +0800
Subject: [PATCH 25/25] Revert #1208, due to the break animation

---
 src/assets/app.scss                |  2 +-
 src/components/HeartbeatBar.vue    | 47 +++---------------------------
 src/components/PublicGroupList.vue |  8 ++---
 3 files changed, 9 insertions(+), 48 deletions(-)

diff --git a/src/assets/app.scss b/src/assets/app.scss
index 8eaff499..c3f2fa79 100644
--- a/src/assets/app.scss
+++ b/src/assets/app.scss
@@ -367,7 +367,7 @@ textarea.form-control {
     .item {
         display: block;
         text-decoration: none;
-        padding: 15px;
+        padding: 13px 15px 10px 15px;
         border-radius: 10px;
         transition: all ease-in-out 0.15s;
 
diff --git a/src/components/HeartbeatBar.vue b/src/components/HeartbeatBar.vue
index 10b1f761..ce888a98 100644
--- a/src/components/HeartbeatBar.vue
+++ b/src/components/HeartbeatBar.vue
@@ -1,6 +1,6 @@
 <template>
     <div ref="wrap" class="wrap" :style="wrapStyle">
-        <div class="hp-bar-big d-flex" :style="barStyle">
+        <div class="hp-bar-big" :style="barStyle">
             <div
                 v-for="(beat, index) in shortBeatList"
                 :key="index"
@@ -8,11 +8,7 @@
                 :class="{ 'empty' : (beat === 0), 'down' : (beat.status === 0), 'pending' : (beat.status === 2) }"
                 :style="beatStyle"
                 :title="getBeatTitle(beat)"
-                @mouseenter="toggleActivateSibling"
-                @mouseleave="toggleActivateSibling"
-            >
-                <div class="beat-inner" />
-            </div>
+            />
         </div>
     </div>
 </template>
@@ -174,28 +170,6 @@ export default {
             return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : "");
         },
 
-        // Toggling the activeSibling class on hover over the current hover item
-        toggleActivateSibling(e) {
-            // Variable definition
-            const element = e.target;
-            const previous = element.previousSibling;
-            const next = element.nextSibling;
-
-            // Return if the hovered element has empty class
-            if (element.classList.contains("empty")) {
-                return;
-            }
-
-            // Check if Previous Sibling is heartbar element and doesn't have the empty class
-            if (previous.children && !previous.classList.contains("empty")) {
-                previous.classList.toggle("active-sibling");
-            }
-
-            // Check if Next Sibling is heartbar element and doesn't have the empty class
-            if (next.children && !next.classList.contains("empty")) {
-                next.classList.toggle("active-sibling");
-            }
-        }
     },
 };
 </script>
@@ -211,10 +185,9 @@ export default {
 
 .hp-bar-big {
     .beat {
+        display: inline-block;
         background-color: $primary;
         border-radius: $border-radius;
-        display: inline-block;
-        transition: all ease 0.6s;
 
         &.empty {
             background-color: aliceblue;
@@ -228,23 +201,11 @@ export default {
             background-color: $warning;
         }
 
-        .beat-inner {
-            border-radius: $border-radius;
-            display: inline-block;
-            height: 100%;
-            width: 5px;
-        }
-
         &:not(.empty):hover {
-            transition: all ease 0.15s;
+            transition: all ease-in-out 0.15s;
             opacity: 0.8;
             transform: scale(var(--hover-scale));
         }
-
-        &.active-sibling {
-            transform: scale(1.15);
-            transition: all ease 0.15s;
-        }
     }
 }
 
diff --git a/src/components/PublicGroupList.vue b/src/components/PublicGroupList.vue
index 4d0ada5c..98c0b7ff 100644
--- a/src/components/PublicGroupList.vue
+++ b/src/components/PublicGroupList.vue
@@ -33,19 +33,19 @@
                         <template #item="monitor">
                             <div class="item">
                                 <div class="row">
-                                    <div class="col-9 col-md-8 small-padding d-flex align-items-center flex-wrap">
-                                        <div class="info d-flex align-items-center gap-3 w-100">
+                                    <div class="col-9 col-md-8 small-padding">
+                                        <div class="info">
                                             <font-awesome-icon v-if="editMode" icon="arrows-alt-v" class="action drag me-3" />
                                             <font-awesome-icon v-if="editMode" icon="times" class="action remove me-3" @click="removeMonitor(group.index, monitor.index)" />
 
                                             <Uptime :monitor="monitor.element" type="24" :pill="true" />
                                             {{ monitor.element.name }}
                                         </div>
-                                        <div v-if="showTags && monitor.element.tags.length > 0" class="tags">
+                                        <div v-if="showTag" class="tags">
                                             <Tag v-for="tag in monitor.element.tags" :key="tag" :item="tag" :size="'sm'" />
                                         </div>
                                     </div>
-                                    <div :key="$root.userHeartbeatBar" class="col-3 col-md-4 d-flex align-items-center">
+                                    <div :key="$root.userHeartbeatBar" class="col-3 col-md-4">
                                         <HeartbeatBar size="small" :monitor-id="monitor.element.id" />
                                     </div>
                                 </div>