From 9615e46568a067630f0609b2cd0fb4573fc17f1f Mon Sep 17 00:00:00 2001
From: Toby Liddicoat <toby@codesure.co.uk>
Date: Wed, 26 Feb 2025 08:59:13 +0000
Subject: [PATCH 1/5] Add GOV Notify integration for notifications

Introduced GOV Notify as a new notification provider, enabling support for email and SMS notifications via the GOV.UK Notify API. Updated relevant components, configuration, and dependencies to support this integration. This includes a dedicated form for GOV Notify in the notification dialog and backend logic for message handling.

Signed-off-by: Toby Liddicoat <toby@codesure.co.uk>
---
 package-lock.json                           | 26 +++++++++++
 package.json                                |  1 +
 server/notification-providers/gov-notify.js | 52 +++++++++++++++++++++
 server/notification.js                      |  6 ++-
 src/components/NotificationDialog.vue       |  3 +-
 src/components/notifications/GovNotify.vue  | 47 +++++++++++++++++++
 src/components/notifications/index.js       |  2 +
 7 files changed, 134 insertions(+), 3 deletions(-)
 create mode 100644 server/notification-providers/gov-notify.js
 create mode 100644 src/components/notifications/GovNotify.vue

diff --git a/package-lock.json b/package-lock.json
index 586b0d6a4..c6ac11343 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -61,6 +61,7 @@
                 "node-radius-client": "~1.0.0",
                 "nodemailer": "~6.9.13",
                 "nostr-tools": "^2.10.4",
+                "notifications-node-client": "^8.2.1",
                 "notp": "~2.0.3",
                 "openid-client": "^5.4.2",
                 "password-hash": "~1.2.2",
@@ -12625,6 +12626,31 @@
             "license": "MIT",
             "optional": true
         },
+        "node_modules/notifications-node-client": {
+            "version": "8.2.1",
+            "resolved": "https://registry.npmjs.org/notifications-node-client/-/notifications-node-client-8.2.1.tgz",
+            "integrity": "sha512-wyZh/NbjN8S2uQX18utYtCyC726BBaGeTc4HeUpdhZv5sYKuaQY94N31v9syh8SzVgehyMzW37y08EePmi+k3Q==",
+            "license": "MIT",
+            "dependencies": {
+                "axios": "^1.7.2",
+                "jsonwebtoken": "^9.0.2"
+            },
+            "engines": {
+                "node": ">=14.17.3",
+                "npm": ">=6.14.13"
+            }
+        },
+        "node_modules/notifications-node-client/node_modules/axios": {
+            "version": "1.7.9",
+            "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz",
+            "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
+            "license": "MIT",
+            "dependencies": {
+                "follow-redirects": "^1.15.6",
+                "form-data": "^4.0.0",
+                "proxy-from-env": "^1.1.0"
+            }
+        },
         "node_modules/notp": {
             "version": "2.0.3",
             "resolved": "https://registry.npmjs.org/notp/-/notp-2.0.3.tgz",
diff --git a/package.json b/package.json
index ef6456b4a..43adeb858 100644
--- a/package.json
+++ b/package.json
@@ -119,6 +119,7 @@
         "node-radius-client": "~1.0.0",
         "nodemailer": "~6.9.13",
         "nostr-tools": "^2.10.4",
+        "notifications-node-client": "^8.2.1",
         "notp": "~2.0.3",
         "openid-client": "^5.4.2",
         "password-hash": "~1.2.2",
diff --git a/server/notification-providers/gov-notify.js b/server/notification-providers/gov-notify.js
new file mode 100644
index 000000000..aa49ac903
--- /dev/null
+++ b/server/notification-providers/gov-notify.js
@@ -0,0 +1,52 @@
+const NotificationProvider = require("./notification-provider");
+const NotifyClient = require("notifications-node-client").NotifyClient;
+
+class GovNotify extends NotificationProvider {
+    name = "GovNotify";
+
+    async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
+        try {
+            const apiKey = notification.apiKey;
+            const emailRecipients = (typeof notification.emailRecipients === "string" && notification.emailRecipients.trim())
+                ? notification.emailRecipients.split(",").map(e => e.trim()).filter(e => e)
+                : [];
+            const smsRecipients = (typeof notification.smsRecipients === "string" && notification.smsRecipients.trim())
+                ? notification.smsRecipients.split(",").map(n => n.trim()).filter(n => n)
+                : [];
+            const message = notification.messageTemplate || msg;
+            const emailTemplateID = notification.emailTemplateId;
+            const smsTemplateID = notification.smsTemplateId;
+
+            const notifyClient = new NotifyClient(apiKey);
+
+            // Send Emails
+            for (const email of emailRecipients) {
+                await notifyClient.sendEmail(
+                    emailTemplateID,
+                    email,
+                    {
+                        personalisation: { message },
+                        reference: "Uptime-Kuma"
+                    });
+            }
+
+            // Send SMS
+            for (const number of smsRecipients) {
+                await notifyClient.sendSms(
+                    smsTemplateID,
+                    number,
+                    {
+                        personalisation: { message },
+                        reference: "Uptime-Kuma"
+                    });
+            }
+
+            return "Notification sent successfully";
+        } catch (error) {
+            console.error("GovNotify Error:", error.response ? error.response.data : error.message);
+            throw new Error("Failed to send notification via GOV Notify");
+        }
+    }
+}
+
+module.exports = GovNotify;
diff --git a/server/notification.js b/server/notification.js
index e7977eb4a..efe67bf35 100644
--- a/server/notification.js
+++ b/server/notification.js
@@ -69,6 +69,7 @@ const Cellsynt = require("./notification-providers/cellsynt");
 const Onesender = require("./notification-providers/onesender");
 const Wpush = require("./notification-providers/wpush");
 const SendGrid = require("./notification-providers/send-grid");
+const GovNotify = require("./notification-providers/gov-notify");
 
 class Notification {
 
@@ -154,7 +155,8 @@ class Notification {
             new GtxMessaging(),
             new Cellsynt(),
             new Wpush(),
-            new SendGrid()
+            new SendGrid(),
+            new GovNotify(),
         ];
         for (let item of list) {
             if (! item.name) {
@@ -181,7 +183,7 @@ class Notification {
         if (this.providerList[notification.type]) {
             return this.providerList[notification.type].send(notification, msg, monitorJSON, heartbeatJSON);
         } else {
-            throw new Error("Notification type is not supported");
+            throw new Error(`Notification type <${notification.type}> is not supported`);
         }
     }
 
diff --git a/src/components/NotificationDialog.vue b/src/components/NotificationDialog.vue
index f6d728029..d97e033a6 100644
--- a/src/components/NotificationDialog.vue
+++ b/src/components/NotificationDialog.vue
@@ -121,6 +121,7 @@ export default {
                 "Elks": "46elks",
                 "GoogleChat": "Google Chat (Google Workspace)",
                 "gorush": "Gorush",
+                "GovNotify": "GOV Notify",
                 "gotify": "Gotify",
                 "GrafanaOncall": "Grafana Oncall",
                 "HeiiOnCall": "Heii On-Call",
@@ -262,7 +263,7 @@ export default {
                 this.id = null;
                 this.notification = {
                     name: "",
-                    type: "telegram",
+                    type: "GovNotify",
                     isDefault: false,
                 };
             }
diff --git a/src/components/notifications/GovNotify.vue b/src/components/notifications/GovNotify.vue
new file mode 100644
index 000000000..d1b5a85a0
--- /dev/null
+++ b/src/components/notifications/GovNotify.vue
@@ -0,0 +1,47 @@
+<template>
+    <div class="mb-3">
+        <label class="form-label">GOV Notify API Key</label>
+        <input
+            v-model="$parent.notification.apiKey"
+            type="text"
+            class="form-control"
+        />
+    </div>
+    <div class="mb-3">
+        <label class="form-label">Email Recipients (comma-separated)</label>
+        <input
+            v-model="$parent.notification.emailRecipients"
+            type="text"
+            class="form-control"
+        />
+    </div>
+    <div class="mb-3">
+        <label class="form-label">SMS Recipients (comma-separated)</label>
+        <input
+            v-model="$parent.notification.smsRecipients"
+            type="text"
+            class="form-control"
+        />
+    </div>
+    <div class="mb-3">
+        <label class="form-label">Email Template ID</label>
+        <input
+            v-model="$parent.notification.emailTemplateId"
+            type="text"
+            class="form-control"
+        />
+    </div>
+    <div class="mb-3">
+        <label class="form-label">SMS Template ID</label>
+        <input
+            v-model="$parent.notification.smsTemplateId"
+            type="text"
+            class="form-control"
+            required
+        />
+    </div>
+</template>
+<script>
+export default {
+};
+</script>
diff --git a/src/components/notifications/index.js b/src/components/notifications/index.js
index efa2af5c4..76df2af65 100644
--- a/src/components/notifications/index.js
+++ b/src/components/notifications/index.js
@@ -67,6 +67,7 @@ import Cellsynt from "./Cellsynt.vue";
 import WPush from "./WPush.vue";
 import SIGNL4 from "./SIGNL4.vue";
 import SendGrid from "./SendGrid.vue";
+import GovNotify from "./GovNotify.vue";
 
 /**
  * Manage all notification form.
@@ -142,6 +143,7 @@ const NotificationFormList = {
     "Cellsynt": Cellsynt,
     "WPush": WPush,
     "SendGrid": SendGrid,
+    "GovNotify": GovNotify
 };
 
 export default NotificationFormList;

From 200f179b5f9078a45526746f7ec79a11ad08dbf4 Mon Sep 17 00:00:00 2001
From: Toby Liddicoat <toby@codesure.co.uk>
Date: Wed, 26 Feb 2025 14:57:50 +0000
Subject: [PATCH 2/5] Refactor GovNotify to enhance API key handling and
 messaging.

Introduced a toggle in the UI to securely display or edit the GOV Notify API key. Updated the backend to include dynamic subject lines and timestamps in notifications to improve clarity and contextual information for recipients.

Signed-off-by: Toby Liddicoat <toby@codesure.co.uk>
---
 server/notification-providers/gov-notify.js | 24 +++++++++++--
 src/components/notifications/GovNotify.vue  | 38 ++++++++++++++++++---
 2 files changed, 55 insertions(+), 7 deletions(-)

diff --git a/server/notification-providers/gov-notify.js b/server/notification-providers/gov-notify.js
index aa49ac903..1fb4188ba 100644
--- a/server/notification-providers/gov-notify.js
+++ b/server/notification-providers/gov-notify.js
@@ -1,4 +1,5 @@
 const NotificationProvider = require("./notification-provider");
+const { DOWN } = require("../../src/util");
 const NotifyClient = require("notifications-node-client").NotifyClient;
 
 class GovNotify extends NotificationProvider {
@@ -13,19 +14,38 @@ class GovNotify extends NotificationProvider {
             const smsRecipients = (typeof notification.smsRecipients === "string" && notification.smsRecipients.trim())
                 ? notification.smsRecipients.split(",").map(n => n.trim()).filter(n => n)
                 : [];
-            const message = notification.messageTemplate || msg;
+            let message = notification.messageTemplate || msg;
             const emailTemplateID = notification.emailTemplateId;
             const smsTemplateID = notification.smsTemplateId;
 
             const notifyClient = new NotifyClient(apiKey);
 
+            let subject = "⚠️ Test";
+
+            if (heartbeatJSON !== null) {
+                subject = (heartbeatJSON["status"] === DOWN) ? "🔴 Down" : "✅ Up";
+            }
+
+            const date = new Date();
+            const day = date.getDate();
+            const month = date.getMonth() + 1;
+            const year = date.getFullYear();
+            const hours = date.getHours();
+            const minutes = date.getMinutes();
+
+            const readableDate = `GMT ${day}/${month}/${year} ${hours}:${minutes}`;
+            message += `\n${readableDate}`;
+
             // Send Emails
             for (const email of emailRecipients) {
                 await notifyClient.sendEmail(
                     emailTemplateID,
                     email,
                     {
-                        personalisation: { message },
+                        personalisation: {
+                            message,
+                            subject,
+                        },
                         reference: "Uptime-Kuma"
                     });
             }
diff --git a/src/components/notifications/GovNotify.vue b/src/components/notifications/GovNotify.vue
index d1b5a85a0..d377dae1e 100644
--- a/src/components/notifications/GovNotify.vue
+++ b/src/components/notifications/GovNotify.vue
@@ -1,11 +1,25 @@
 <template>
     <div class="mb-3">
         <label class="form-label">GOV Notify API Key</label>
-        <input
-            v-model="$parent.notification.apiKey"
-            type="text"
-            class="form-control"
-        />
+        <div class="input-group">
+            <input
+                v-if="!showApiKey"
+                type="text"
+                class="form-control"
+                value="************"
+                disabled
+            />
+            <input
+                v-else
+                v-model="newApiKey"
+                type="text"
+                class="form-control"
+                placeholder="Enter new API key"
+            />
+            <button class="btn btn-outline-secondary" type="button" @click="toggleApiKey">
+                {{ showApiKey ? "Cancel" : "Change" }}
+            </button>
+        </div>
     </div>
     <div class="mb-3">
         <label class="form-label">Email Recipients (comma-separated)</label>
@@ -43,5 +57,19 @@
 </template>
 <script>
 export default {
+    data() {
+        return {
+            showApiKey: false,
+            newApiKey: "",
+        };
+    },
+    methods: {
+        toggleApiKey() {
+            if (this.showApiKey) {
+                this.newApiKey = "";
+            }
+            this.showApiKey = !this.showApiKey;
+        },
+    },
 };
 </script>

From 9081025c4ad3b087cb65e1d7e14daee2385f7267 Mon Sep 17 00:00:00 2001
From: Toby Liddicoat <toby@codesure.co.uk>
Date: Thu, 27 Feb 2025 19:58:07 +0000
Subject: [PATCH 3/5] Refactor modules for improved readability and consistency

Reformatted code across multiple modules, standardizing string quotes, indentation, and spacing. Improved readability by restructuring blocks and aligning object properties consistently. These changes ensure better code maintainability and follow standard conventions.

Signed-off-by: Toby Liddicoat <toby@codesure.co.uk>
---
 server/auth.js                                |   5 +-
 server/client.js                              |   5 +-
 server/database.js                            |  69 +++--
 server/docker.js                              |   6 +-
 server/jobs.js                                |   4 +-
 server/model/maintenance.js                   |   9 +-
 server/model/monitor.js                       |  97 ++++--
 server/model/status_page.js                   |  35 ++-
 server/model/user.js                          |   9 +-
 server/modules/apicache/apicache.js           |  52 ++--
 server/modules/apicache/index.js              |   2 +-
 server/modules/apicache/memory-cache.js       |   8 +-
 server/modules/axios-ntlm/lib/flags.js        |   4 +-
 server/modules/axios-ntlm/lib/hash.js         |  50 +--
 server/modules/axios-ntlm/lib/ntlm.js         |  94 +++---
 server/modules/axios-ntlm/lib/ntlmClient.js   | 284 +++++++++++++-----
 server/modules/dayjs/plugin/timezone.d.ts     |  32 +-
 server/modules/dayjs/plugin/timezone.js       |   4 +-
 server/monitor-types/dns.js                   |   7 +-
 server/monitor-types/mqtt.js                  |  14 +-
 server/monitor-types/rabbitmq.js              |   8 +-
 .../real-browser-monitor-type.js              |   9 +-
 server/monitor-types/snmp.js                  |  11 +-
 server/notification-providers/gov-notify.js   |   9 +
 .../notification-providers/home-assistant.js  |  16 +-
 server/notification.js                        |  10 +-
 server/prometheus.js                          |  12 +-
 server/proxy.js                               |  12 +-
 server/routers/api-router.js                  |  32 +-
 server/routers/status-page-router.js          |  23 +-
 server/server.js                              |  66 +++-
 server/settings.js                            |   8 +-
 server/util-server.js                         |  58 ++--
 server/utils/array-with-key.js                |   2 +-
 server/utils/limit-queue.js                   |   2 +-
 35 files changed, 699 insertions(+), 369 deletions(-)

diff --git a/server/auth.js b/server/auth.js
index 597cf3d75..679e294d2 100644
--- a/server/auth.js
+++ b/server/auth.js
@@ -3,7 +3,10 @@ const passwordHash = require("./password-hash");
 const { R } = require("redbean-node");
 const { setting } = require("./util-server");
 const { log } = require("../src/util");
-const { loginRateLimiter, apiRateLimiter } = require("./rate-limiter");
+const {
+    loginRateLimiter,
+    apiRateLimiter,
+} = require("./rate-limiter");
 const { Settings } = require("./settings");
 const dayjs = require("dayjs");
 
diff --git a/server/client.js b/server/client.js
index 72f0a4e8e..70741fe99 100644
--- a/server/client.js
+++ b/server/client.js
@@ -47,10 +47,11 @@ async function sendNotificationList(socket) {
  */
 async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite = false) {
     let list = await R.getAll(`
-        SELECT * FROM heartbeat
+        SELECT *
+        FROM heartbeat
         WHERE monitor_id = ?
         ORDER BY time DESC
-        LIMIT 100
+            LIMIT 100
     `, [
         monitorID,
     ]);
diff --git a/server/database.js b/server/database.js
index 0e6a7405d..da528f4a9 100644
--- a/server/database.js
+++ b/server/database.js
@@ -1,7 +1,13 @@
 const fs = require("fs");
 const { R } = require("redbean-node");
-const { setSetting, setting } = require("./util-server");
-const { log, sleep } = require("../src/util");
+const {
+    setSetting,
+    setting,
+} = require("./util-server");
+const {
+    log,
+    sleep,
+} = require("../src/util");
 const knex = require("knex");
 const path = require("path");
 const { EmbeddedMariaDB } = require("./embedded-mariadb");
@@ -136,24 +142,24 @@ class Database {
         Database.dataDir = process.env.DATA_DIR || args["data-dir"] || "./data/";
 
         Database.sqlitePath = path.join(Database.dataDir, "kuma.db");
-        if (! fs.existsSync(Database.dataDir)) {
+        if (!fs.existsSync(Database.dataDir)) {
             fs.mkdirSync(Database.dataDir, { recursive: true });
         }
 
         Database.uploadDir = path.join(Database.dataDir, "upload/");
 
-        if (! fs.existsSync(Database.uploadDir)) {
+        if (!fs.existsSync(Database.uploadDir)) {
             fs.mkdirSync(Database.uploadDir, { recursive: true });
         }
 
         // Create screenshot dir
         Database.screenshotDir = path.join(Database.dataDir, "screenshots/");
-        if (! fs.existsSync(Database.screenshotDir)) {
+        if (!fs.existsSync(Database.screenshotDir)) {
             fs.mkdirSync(Database.screenshotDir, { recursive: true });
         }
 
         Database.dockerTLSDir = path.join(Database.dataDir, "docker-tls/");
-        if (! fs.existsSync(Database.dockerTLSDir)) {
+        if (!fs.existsSync(Database.dockerTLSDir)) {
             fs.mkdirSync(Database.dockerTLSDir, { recursive: true });
         }
 
@@ -231,7 +237,7 @@ class Database {
 
         if (dbConfig.type === "sqlite") {
 
-            if (! fs.existsSync(Database.sqlitePath)) {
+            if (!fs.existsSync(Database.sqlitePath)) {
                 log.info("server", "Copying Database");
                 fs.copyFileSync(Database.templatePath, Database.sqlitePath);
             }
@@ -252,7 +258,7 @@ class Database {
                     idleTimeoutMillis: 120 * 1000,
                     propagateCreateError: false,
                     acquireTimeoutMillis: acquireConnectionTimeout,
-                }
+                },
             };
         } else if (dbConfig.type === "mariadb") {
             if (!/^\w+$/.test(dbConfig.dbName)) {
@@ -451,7 +457,7 @@ class Database {
     static async patchSqlite() {
         let version = parseInt(await setting("database_version"));
 
-        if (! version) {
+        if (!version) {
             version = 0;
         }
 
@@ -502,7 +508,7 @@ class Database {
         log.debug("db", "Database Patch 2.0 Process");
         let databasePatchedFiles = await setting("databasePatchedFiles");
 
-        if (! databasePatchedFiles) {
+        if (!databasePatchedFiles) {
             databasePatchedFiles = {};
         }
 
@@ -579,11 +585,11 @@ class Database {
             let id = await R.store(statusPage);
 
             await R.exec("UPDATE incident SET status_page_id = ? WHERE status_page_id IS NULL", [
-                id
+                id,
             ]);
 
             await R.exec("UPDATE [group] SET status_page_id = ? WHERE status_page_id IS NULL", [
-                id
+                id,
             ]);
 
             await R.exec("DELETE FROM setting WHERE type = 'statusPage'");
@@ -611,13 +617,13 @@ class Database {
     static async patch2Recursion(sqlFilename, databasePatchedFiles) {
         let value = this.patchList[sqlFilename];
 
-        if (! value) {
+        if (!value) {
             log.info("db", sqlFilename + " skip");
             return;
         }
 
         // Check if patched
-        if (! databasePatchedFiles[sqlFilename]) {
+        if (!databasePatchedFiles[sqlFilename]) {
             log.info("db", sqlFilename + " is not patched");
 
             if (value.parents) {
@@ -652,7 +658,7 @@ class Database {
         // Remove all comments (--)
         let lines = text.split("\n");
         lines = lines.filter((line) => {
-            return ! line.startsWith("--");
+            return !line.startsWith("--");
         });
 
         // Split statements by semicolon
@@ -797,7 +803,8 @@ class Database {
 
         // Stop if stat_* tables are not empty
         for (let table of [ "stat_minutely", "stat_hourly", "stat_daily" ]) {
-            let countResult = await R.getRow(`SELECT COUNT(*) AS count FROM ${table}`);
+            let countResult = await R.getRow(`SELECT COUNT(*) AS count
+                                              FROM ${table}`);
             let count = countResult.count;
             if (count > 0) {
                 log.warn("db", `Aggregate table ${table} is not empty, migration will not be started (Maybe you were using 2.0.0-dev?)`);
@@ -814,12 +821,12 @@ class Database {
         for (let monitor of monitors) {
             // Get a list of unique dates from the heartbeat table, using raw sql
             let dates = await R.getAll(`
-                SELECT DISTINCT DATE(time) AS date
+                SELECT DISTINCT DATE (time) AS date
                 FROM heartbeat
                 WHERE monitor_id = ?
                 ORDER BY date ASC
             `, [
-                monitor.monitor_id
+                monitor.monitor_id,
             ]);
 
             for (let date of dates) {
@@ -833,7 +840,7 @@ class Database {
                     SELECT status, ping, time
                     FROM heartbeat
                     WHERE monitor_id = ?
-                    AND DATE(time) = ?
+                      AND DATE (time) = ?
                     ORDER BY time ASC
                 `, [ monitor.monitor_id, date.date ]);
 
@@ -887,19 +894,21 @@ class Database {
                 log.info("db", "Deleting non-important heartbeats for monitor " + monitor.id);
             }
             await R.exec(`
-                DELETE FROM heartbeat
+                DELETE
+                FROM heartbeat
                 WHERE monitor_id = ?
-                AND important = 0
-                AND time < ${sqlHourOffset}
-                AND id NOT IN (
+                  AND important = 0
+                  AND time
+                    < ${sqlHourOffset}
+                  AND id NOT IN (
                     SELECT id FROM ( -- written this way for Maria's support
-                        SELECT id
-                        FROM heartbeat
-                        WHERE monitor_id = ?
-                        ORDER BY time DESC
-                        LIMIT ?
-                    )  AS limited_ids
-                )
+                    SELECT id
+                    FROM heartbeat
+                    WHERE monitor_id = ?
+                    ORDER BY time DESC
+                    LIMIT ?
+                    ) AS limited_ids
+                    )
             `, [
                 monitor.id,
                 -24,
diff --git a/server/docker.js b/server/docker.js
index ee6051dfa..249eff733 100644
--- a/server/docker.js
+++ b/server/docker.js
@@ -146,7 +146,7 @@ class DockerHost {
     static getHttpsAgentOptions(dockerType, url) {
         let baseOptions = {
             maxCachedSessions: 0,
-            rejectUnauthorized: true
+            rejectUnauthorized: true,
         };
         let certOptions = {};
 
@@ -163,13 +163,13 @@ class DockerHost {
             certOptions = {
                 ca,
                 key,
-                cert
+                cert,
             };
         }
 
         return {
             ...baseOptions,
-            ...certOptions
+            ...certOptions,
         };
     }
 }
diff --git a/server/jobs.js b/server/jobs.js
index 0838731d0..5f60e3484 100644
--- a/server/jobs.js
+++ b/server/jobs.js
@@ -15,7 +15,7 @@ const jobs = [
         interval: "*/5 * * * *",
         jobFunc: incrementalVacuum,
         croner: null,
-    }
+    },
 ];
 
 /**
@@ -54,5 +54,5 @@ const stopBackgroundJobs = function () {
 
 module.exports = {
     initBackgroundJobs,
-    stopBackgroundJobs
+    stopBackgroundJobs,
 };
diff --git a/server/model/maintenance.js b/server/model/maintenance.js
index 7111a18cb..f62b47a44 100644
--- a/server/model/maintenance.js
+++ b/server/model/maintenance.js
@@ -1,5 +1,9 @@
 const { BeanModel } = require("redbean-node/dist/bean-model");
-const { parseTimeObject, parseTimeFromTimeObject, log } = require("../../src/util");
+const {
+    parseTimeObject,
+    parseTimeFromTimeObject,
+    log,
+} = require("../../src/util");
 const { R } = require("redbean-node");
 const dayjs = require("dayjs");
 const Cron = require("croner");
@@ -192,7 +196,8 @@ class Maintenance extends BeanModel {
      * @returns {void}
      */
     static validateCron(cron) {
-        let job = new Cron(cron, () => {});
+        let job = new Cron(cron, () => {
+        });
         job.stop();
     }
 
diff --git a/server/model/monitor.js b/server/model/monitor.js
index 3ad8cfafc..8e2936f99 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -1,11 +1,37 @@
 const dayjs = require("dayjs");
 const axios = require("axios");
 const { Prometheus } = require("../prometheus");
-const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND,
-    SQL_DATETIME_FORMAT, evaluateJsonQuery
+const {
+    log,
+    UP,
+    DOWN,
+    PENDING,
+    MAINTENANCE,
+    flipStatus,
+    MAX_INTERVAL_SECOND,
+    MIN_INTERVAL_SECOND,
+    SQL_DATETIME_FORMAT,
+    evaluateJsonQuery,
 } = require("../../src/util");
-const { tcping, ping, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, setSetting, httpNtlm, radius, grpcQuery,
-    redisPingAsync, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal
+const {
+    tcping,
+    ping,
+    checkCertificate,
+    checkStatusCode,
+    getTotalClientInRoom,
+    setting,
+    mssqlQuery,
+    postgresQuery,
+    mysqlQuery,
+    setSetting,
+    httpNtlm,
+    radius,
+    grpcQuery,
+    redisPingAsync,
+    kafkaProducerAsync,
+    getOidcTokenClientCredentials,
+    rootCertificatesFingerprints,
+    axiosAbortSignal,
 } = require("../util-server");
 const { R } = require("redbean-node");
 const { BeanModel } = require("redbean-node/dist/bean-model");
@@ -61,7 +87,10 @@ class Monitor extends BeanModel {
         }
 
         if (certExpiry && (this.type === "http" || this.type === "keyword" || this.type === "json-query") && this.getURLProtocol() === "https:") {
-            const { certExpiryDaysRemaining, validCert } = await this.getCertExpiry(this.id);
+            const {
+                certExpiryDaysRemaining,
+                validCert,
+            } = await this.getCertExpiry(this.id);
             obj.certExpiryDaysRemaining = certExpiryDaysRemaining;
             obj.validCert = validCert;
         }
@@ -218,13 +247,13 @@ class Monitor extends BeanModel {
             if (tlsInfo?.valid && tlsInfo?.certInfo?.daysRemaining) {
                 return {
                     certExpiryDaysRemaining: tlsInfo.certInfo.daysRemaining,
-                    validCert: true
+                    validCert: true,
                 };
             }
         }
         return {
             certExpiryDaysRemaining: "",
-            validCert: false
+            validCert: false,
         };
     }
 
@@ -334,7 +363,7 @@ class Monitor extends BeanModel {
 
             let beatInterval = this.interval;
 
-            if (! beatInterval) {
+            if (!beatInterval) {
                 beatInterval = 1;
             }
 
@@ -479,7 +508,7 @@ class Monitor extends BeanModel {
                             ...(contentType ? { "Content-Type": contentType } : {}),
                             ...(basicAuthHeader),
                             ...(oauth2AuthHeader),
-                            ...(this.headers ? JSON.parse(this.headers) : {})
+                            ...(this.headers ? JSON.parse(this.headers) : {}),
                         },
                         maxRedirects: this.maxredirects,
                         validateStatus: (status) => {
@@ -504,7 +533,10 @@ class Monitor extends BeanModel {
                         const proxy = await R.load("proxy", this.proxy_id);
 
                         if (proxy && proxy.active) {
-                            const { httpAgent, httpsAgent } = Proxy.createAgents(proxy, {
+                            const {
+                                httpAgent,
+                                httpsAgent,
+                            } = Proxy.createAgents(proxy, {
                                 httpsAgentOptions: httpsAgentOptions,
                             });
 
@@ -518,7 +550,7 @@ class Monitor extends BeanModel {
                         let jar = new CookieJar();
                         let httpsCookieAgentOptions = {
                             ...httpsAgentOptions,
-                            cookies: { jar }
+                            cookies: { jar },
                         };
                         options.httpsAgent = new HttpsCookieAgent(httpsCookieAgentOptions);
                     }
@@ -600,7 +632,10 @@ class Monitor extends BeanModel {
                     } else if (this.type === "json-query") {
                         let data = res.data;
 
-                        const { status, response } = await evaluateJsonQuery(data, this.jsonPath, this.jsonPathOperator, this.expectedValue);
+                        const {
+                            status,
+                            response,
+                        } = await evaluateJsonQuery(data, this.jsonPath, this.jsonPathOperator, this.expectedValue);
 
                         if (status) {
                             bean.status = UP;
@@ -681,7 +716,7 @@ class Monitor extends BeanModel {
                         params: {
                             filter: filter,
                             key: steamAPIKey,
-                        }
+                        },
                     });
 
                     if (res.data.response && res.data.response.servers && res.data.response.servers.length > 0) {
@@ -690,7 +725,8 @@ class Monitor extends BeanModel {
 
                         try {
                             bean.ping = await ping(this.hostname, this.packetSize);
-                        } catch (_) { }
+                        } catch (_) {
+                        }
                     } else {
                         throw new Error("Server not found on Steam");
                     }
@@ -739,7 +775,7 @@ class Monitor extends BeanModel {
                     } else if (dockerHost._dockerType === "tcp") {
                         options.baseURL = DockerHost.patchDockerURL(dockerHost._dockerDaemon);
                         options.httpsAgent = new https.Agent(
-                            DockerHost.getHttpsAgentOptions(dockerHost._dockerType, options.baseURL)
+                            DockerHost.getHttpsAgentOptions(dockerHost._dockerType, options.baseURL),
                         );
                     }
 
@@ -984,12 +1020,12 @@ class Monitor extends BeanModel {
 
             previousBeat = bean;
 
-            if (! this.isStop) {
+            if (!this.isStop) {
                 log.debug("monitor", `[${this.name}] SetTimeout for next check.`);
 
                 let intervalRemainingMs = Math.max(
                     1,
-                    beatInterval * 1000 - dayjs().diff(dayjs.utc(bean.time))
+                    beatInterval * 1000 - dayjs().diff(dayjs.utc(bean.time)),
                 );
 
                 log.debug("monitor", `[${this.name}] Next heartbeat in: ${intervalRemainingMs}ms`);
@@ -1013,7 +1049,7 @@ class Monitor extends BeanModel {
                 UptimeKumaServer.errorLog(e, false);
                 log.error("monitor", "Please report to https://github.com/louislam/uptime-kuma/issues");
 
-                if (! this.isStop) {
+                if (!this.isStop) {
                     log.info("monitor", "Try to restart the monitor");
                     this.heartbeatInterval = setTimeout(safeBeat, this.interval * 1000);
                 }
@@ -1047,7 +1083,7 @@ class Monitor extends BeanModel {
                     username: this.basic_auth_user,
                     password: this.basic_auth_pass,
                     domain: this.authDomain,
-                    workstation: this.authWorkstation ? this.authWorkstation : undefined
+                    workstation: this.authWorkstation ? this.authWorkstation : undefined,
                 });
             } else {
                 res = await axios.request(options);
@@ -1065,8 +1101,9 @@ class Monitor extends BeanModel {
                 let oauth2AuthHeader = {
                     "Authorization": this.oauthAccessToken.token_type + " " + this.oauthAccessToken.access_token,
                 };
-                options.headers = { ...(options.headers),
-                    ...(oauth2AuthHeader)
+                options.headers = {
+                    ...(options.headers),
+                    ...(oauth2AuthHeader),
                 };
 
                 return this.makeAxiosRequest(options, true);
@@ -1158,7 +1195,7 @@ class Monitor extends BeanModel {
                     if (oldCertInfo.certInfo.fingerprint256 !== checkCertificateResult.certInfo.fingerprint256) {
                         log.debug("monitor", "Resetting sent_history");
                         await R.exec("DELETE FROM notification_sent_history WHERE type = 'certificate' AND monitor_id = ?", [
-                            this.id
+                            this.id,
                         ]);
                     } else {
                         log.debug("monitor", "No need to reset sent_history");
@@ -1168,7 +1205,8 @@ class Monitor extends BeanModel {
                 } else {
                     log.debug("monitor", "Not valid object");
                 }
-            } catch (e) { }
+            } catch (e) {
+            }
 
         }
 
@@ -1326,8 +1364,9 @@ class Monitor extends BeanModel {
             for (let notification of notificationList) {
                 try {
                     const heartbeatJSON = bean.toJSON();
-                    const monitorData = [{ id: monitor.id,
-                        active: monitor.active
+                    const monitorData = [ {
+                        id: monitor.id,
+                        active: monitor.active,
                     }];
                     const preloadData = await Monitor.preparePreloadData(monitorData);
                     // Prevent if the msg is undefined, notifications such as Discord cannot send out.
@@ -1370,7 +1409,7 @@ class Monitor extends BeanModel {
         if (tlsInfoObject && tlsInfoObject.certInfo && tlsInfoObject.certInfo.daysRemaining) {
             const notificationList = await Monitor.getNotificationList(this);
 
-            if (! notificationList.length > 0) {
+            if (!notificationList.length > 0) {
                 // fail fast. If no notification is set, all the following checks can be skipped.
                 log.debug("monitor", "No notification, no need to send cert notification");
                 return;
@@ -1458,7 +1497,7 @@ class Monitor extends BeanModel {
      */
     static async getPreviousHeartbeat(monitorID) {
         return await R.findOne("heartbeat", " id = (select MAX(id) from heartbeat where monitor_id = ?)", [
-            monitorID
+            monitorID,
         ]);
     }
 
@@ -1570,7 +1609,7 @@ class Monitor extends BeanModel {
                     monitor_id: row.monitor_id,
                     value: row.value,
                     name: row.name,
-                    color: row.color
+                    color: row.color,
                 });
             });
 
@@ -1687,7 +1726,7 @@ class Monitor extends BeanModel {
      */
     static async unlinkAllChildren(groupID) {
         return await R.exec("UPDATE `monitor` SET parent = ? WHERE parent = ? ", [
-            null, groupID
+            null, groupID,
         ]);
     }
 
diff --git a/server/model/status_page.js b/server/model/status_page.js
index 38f548ebb..02d80f6d1 100644
--- a/server/model/status_page.js
+++ b/server/model/status_page.js
@@ -8,7 +8,15 @@ const { marked } = require("marked");
 const { Feed } = require("feed");
 const config = require("../config");
 
-const { STATUS_PAGE_ALL_DOWN, STATUS_PAGE_ALL_UP, STATUS_PAGE_MAINTENANCE, STATUS_PAGE_PARTIAL_DOWN, UP, MAINTENANCE, DOWN } = require("../../src/util");
+const {
+    STATUS_PAGE_ALL_DOWN,
+    STATUS_PAGE_ALL_UP,
+    STATUS_PAGE_MAINTENANCE,
+    STATUS_PAGE_PARTIAL_DOWN,
+    UP,
+    MAINTENANCE,
+    DOWN,
+} = require("../../src/util");
 
 class StatusPage extends BeanModel {
 
@@ -16,7 +24,7 @@ class StatusPage extends BeanModel {
      * Like this: { "test-uptime.kuma.pet": "default" }
      * @type {{}}
      */
-    static domainMappingList = { };
+    static domainMappingList = {};
 
     /**
      * Handle responses to RSS pages
@@ -26,7 +34,7 @@ class StatusPage extends BeanModel {
      */
     static async handleStatusPageRSSResponse(response, slug) {
         let statusPage = await R.findOne("status_page", " slug = ? ", [
-            slug
+            slug,
         ]);
 
         if (statusPage) {
@@ -51,7 +59,7 @@ class StatusPage extends BeanModel {
         }
 
         let statusPage = await R.findOne("status_page", " slug = ? ", [
-            slug
+            slug,
         ]);
 
         if (statusPage) {
@@ -68,7 +76,10 @@ class StatusPage extends BeanModel {
      * @returns {Promise<string>} the rendered html
      */
     static async renderRSS(statusPage, slug) {
-        const { heartbeats, statusDescription } = await StatusPage.getRSSPageData(statusPage);
+        const {
+            heartbeats,
+            statusDescription,
+        } = await StatusPage.getRSSPageData(statusPage);
 
         let proto = config.isSSL ? "https" : "http";
         let host = `${proto}://${config.hostname || "localhost"}:${config.port}/status/${slug}`;
@@ -135,7 +146,7 @@ class StatusPage extends BeanModel {
         // Preload data
         // Add jsesc, fix https://github.com/louislam/uptime-kuma/issues/2186
         const escapedJSONObject = jsesc(await StatusPage.getStatusPageData(statusPage), {
-            "isScriptContext": true
+            "isScriptContext": true,
         });
 
         const script = $(`
@@ -174,7 +185,7 @@ class StatusPage extends BeanModel {
             }
         }
 
-        if (! hasUp) {
+        if (!hasUp) {
             status = STATUS_PAGE_ALL_DOWN;
         }
 
@@ -223,7 +234,7 @@ class StatusPage extends BeanModel {
         const showTags = !!statusPage.show_tags;
 
         const list = await R.find("group", " public = 1 AND status_page_id = ? ORDER BY weight ", [
-            statusPage.id
+            statusPage.id,
         ]);
 
         let heartbeats = [];
@@ -236,7 +247,7 @@ class StatusPage extends BeanModel {
                     heartbeats.push({
                         ...monitor,
                         status: heartbeat.status,
-                        time: heartbeat.time
+                        time: heartbeat.time,
                     });
                 }
             }
@@ -251,7 +262,7 @@ class StatusPage extends BeanModel {
 
         return {
             heartbeats,
-            statusDescription
+            statusDescription,
         };
     }
 
@@ -279,7 +290,7 @@ class StatusPage extends BeanModel {
         const showTags = !!statusPage.show_tags;
 
         const list = await R.find("group", " public = 1 AND status_page_id = ? ORDER BY weight ", [
-            statusPage.id
+            statusPage.id,
         ]);
 
         for (let groupBean of list) {
@@ -442,7 +453,7 @@ class StatusPage extends BeanModel {
      */
     static async slugToID(slug) {
         return await R.getCell("SELECT id FROM status_page WHERE slug = ? ", [
-            slug
+            slug,
         ]);
     }
 
diff --git a/server/model/user.js b/server/model/user.js
index 329402ff5..d07cf8142 100644
--- a/server/model/user.js
+++ b/server/model/user.js
@@ -2,7 +2,10 @@ const { BeanModel } = require("redbean-node/dist/bean-model");
 const passwordHash = require("../password-hash");
 const { R } = require("redbean-node");
 const jwt = require("jsonwebtoken");
-const { shake256, SHAKE256_LENGTH } = require("../util-server");
+const {
+    shake256,
+    SHAKE256_LENGTH,
+} = require("../util-server");
 
 class User extends BeanModel {
     /**
@@ -15,7 +18,7 @@ class User extends BeanModel {
     static async resetPassword(userID, newPassword) {
         await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [
             passwordHash.generate(newPassword),
-            userID
+            userID,
         ]);
     }
 
@@ -29,7 +32,7 @@ class User extends BeanModel {
 
         await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [
             hashedPassword,
-            this.id
+            this.id,
         ]);
 
         this.password = hashedPassword;
diff --git a/server/modules/apicache/apicache.js b/server/modules/apicache/apicache.js
index 95a04d9e3..dd7f6d7ca 100644
--- a/server/modules/apicache/apicache.js
+++ b/server/modules/apicache/apicache.js
@@ -100,7 +100,7 @@ function ApiCache() {
      * Generated by Trelent
      */
     function debug(a, b, c, d) {
-        let arr = ["\x1b[36m[apicache]\x1b[0m", a, b, c, d].filter(function (arg) {
+        let arr = [ "\x1b[36m[apicache]\x1b[0m", a, b, c, d ].filter(function (arg) {
             return arg !== undefined;
         });
         let debugEnv = process.env.DEBUG && process.env.DEBUG.split(",").indexOf("apicache") !== -1;
@@ -210,7 +210,8 @@ function ApiCache() {
             try {
                 redis.hset(key, "response", JSON.stringify(value));
                 redis.hset(key, "duration", duration);
-                redis.expire(key, duration / 1000, expireCallback || function () {});
+                redis.expire(key, duration / 1000, expireCallback || function () {
+                });
             } catch (err) {
                 debug("[apicache] error in redis.hset()");
             }
@@ -247,8 +248,8 @@ function ApiCache() {
                 }
 
                 res._apicache.content = Buffer.concat(
-                    [oldContent, content],
-                    oldContent.length + content.length
+                    [ oldContent, content ],
+                    oldContent.length + content.length,
                 );
             } else {
                 res._apicache.content = content;
@@ -268,7 +269,7 @@ function ApiCache() {
      * @param {function(Object, Object):boolean} toggle
      */
     function makeResponseCacheable(req, res, next, key, duration, strDuration, toggle) {
-    // monkeypatch res.end to create cache object
+        // monkeypatch res.end to create cache object
         res._apicache = {
             write: res.write,
             writeHead: res.writeHead,
@@ -314,7 +315,7 @@ function ApiCache() {
                         res.statusCode,
                         headers,
                         res._apicache.content,
-                        encoding
+                        encoding,
                     );
                     cacheResponse(key, cacheObject, duration);
 
@@ -367,7 +368,7 @@ function ApiCache() {
         let data = cacheObject.data;
         if (data && data.type === "Buffer") {
             data =
-        typeof data.data === "number" ? new Buffer.alloc(data.data) : new Buffer.from(data.data);
+                typeof data.data === "number" ? new Buffer.alloc(data.data) : new Buffer.from(data.data);
         }
 
         // test Etag against If-None-Match for 304
@@ -511,15 +512,15 @@ function ApiCache() {
     };
 
     /**
-   * Return cache performance statistics (hit rate).  Suitable for
-   * putting into a route:
-   * <code>
-   * app.get('/api/cache/performance', (req, res) => {
-   *    res.json(apicache.getPerformance())
-   * })
-   * </code>
-   * @returns {any[]}
-   */
+     * Return cache performance statistics (hit rate).  Suitable for
+     * putting into a route:
+     * <code>
+     * app.get('/api/cache/performance', (req, res) => {
+     *    res.json(apicache.getPerformance())
+     * })
+     * </code>
+     * @returns {any[]}
+     */
     this.getPerformance = function () {
         return performanceArray.map(function (p) {
             return p.report();
@@ -528,7 +529,7 @@ function ApiCache() {
 
     /**
      * Get index of a group
-     * @param {string} group 
+     * @param {string} group
      * @returns {number}
      */
     this.getIndex = function (group) {
@@ -543,9 +544,9 @@ function ApiCache() {
      * Express middleware
      * @param {(string|number)} strDuration Duration to cache responses
      * for.
-     * @param {function(Object, Object):boolean} middlewareToggle 
+     * @param {function(Object, Object):boolean} middlewareToggle
      * @param {Object} localOptions Options for APICache
-     * @returns 
+     * @returns
      */
     this.middleware = function cache(strDuration, middlewareToggle, localOptions) {
         let duration = instance.getDuration(strDuration);
@@ -573,7 +574,8 @@ function ApiCache() {
          * A Function for non tracking performance
          */
         function NOOPCachePerformance() {
-            this.report = this.hit = this.miss = function () {}; // noop;
+            this.report = this.hit = this.miss = function () {
+            }; // noop;
         }
 
         /**
@@ -762,8 +764,8 @@ function ApiCache() {
             }
             if (
                 req.headers["x-apicache-bypass"] ||
-        req.headers["x-apicache-force-fetch"] ||
-        (opt.respectCacheControl && req.headers["cache-control"] == "no-cache")
+                req.headers["x-apicache-force-fetch"] ||
+                (opt.respectCacheControl && req.headers["cache-control"] == "no-cache")
             ) {
                 return bypass();
             }
@@ -826,7 +828,7 @@ function ApiCache() {
                                 JSON.parse(obj.response),
                                 middlewareToggle,
                                 next,
-                                duration
+                                duration,
                             );
                         } else {
                             perf.miss(key);
@@ -837,7 +839,7 @@ function ApiCache() {
                                 key,
                                 duration,
                                 strDuration,
-                                middlewareToggle
+                                middlewareToggle,
                             );
                         }
                     });
@@ -859,7 +861,7 @@ function ApiCache() {
 
     /**
      * Process options
-     * @param {Object} options 
+     * @param {Object} options
      * @returns {Object}
      */
     this.options = function (options) {
diff --git a/server/modules/apicache/index.js b/server/modules/apicache/index.js
index b8bb9b354..1938f5dd9 100644
--- a/server/modules/apicache/index.js
+++ b/server/modules/apicache/index.js
@@ -2,7 +2,7 @@ const apicache = require("./apicache");
 
 apicache.options({
     headerBlacklist: [
-        "cache-control"
+        "cache-control",
     ],
     headers: {
         // Disable client side cache, only server side cache.
diff --git a/server/modules/apicache/memory-cache.js b/server/modules/apicache/memory-cache.js
index a91eee324..1e75bae32 100644
--- a/server/modules/apicache/memory-cache.js
+++ b/server/modules/apicache/memory-cache.js
@@ -4,7 +4,7 @@ function MemoryCache() {
 }
 
 /**
- * 
+ *
  * @param {string} key Key to store cache as
  * @param {any} value Value to store
  * @param {number} time Time to store for
@@ -22,7 +22,7 @@ MemoryCache.prototype.add = function (key, value, time, timeoutCallback) {
         timeout: setTimeout(function () {
             instance.delete(key);
             return timeoutCallback && typeof timeoutCallback === "function" && timeoutCallback(value, key);
-        }, time)
+        }, time),
     };
 
     this.cache[key] = entry;
@@ -52,7 +52,7 @@ MemoryCache.prototype.delete = function (key) {
 
 /**
  * Get value of key
- * @param {string} key 
+ * @param {string} key
  * @returns {Object}
  */
 MemoryCache.prototype.get = function (key) {
@@ -63,7 +63,7 @@ MemoryCache.prototype.get = function (key) {
 
 /**
  * Get value of cache entry
- * @param {string} key 
+ * @param {string} key
  * @returns {any}
  */
 MemoryCache.prototype.getValue = function (key) {
diff --git a/server/modules/axios-ntlm/lib/flags.js b/server/modules/axios-ntlm/lib/flags.js
index c16028cad..86c06033b 100644
--- a/server/modules/axios-ntlm/lib/flags.js
+++ b/server/modules/axios-ntlm/lib/flags.js
@@ -1,4 +1,4 @@
-'use strict';
+"use strict";
 // Original file https://raw.githubusercontent.com/elasticio/node-ntlm-client/master/lib/flags.js
 module.exports.NTLMFLAG_NEGOTIATE_UNICODE = 1 << 0;
 /* Indicates that Unicode strings are supported for use in security buffer
@@ -74,4 +74,4 @@ module.exports.NTLMFLAG_NEGOTIATE_KEY_EXCHANGE = 1 << 30;
 /* Indicates that the client will provide an encrypted master key in
    the "Session Key" field of the Type 3 message. */
 module.exports.NTLMFLAG_NEGOTIATE_56 = 1 << 31;
-//# sourceMappingURL=flags.js.map
\ No newline at end of file
+//# sourceMappingURL=flags.js.map
diff --git a/server/modules/axios-ntlm/lib/hash.js b/server/modules/axios-ntlm/lib/hash.js
index 4e5aa26b4..73cf97e4f 100644
--- a/server/modules/axios-ntlm/lib/hash.js
+++ b/server/modules/axios-ntlm/lib/hash.js
@@ -1,6 +1,7 @@
-'use strict';
+"use strict";
 // Original source at https://github.com/elasticio/node-ntlm-client/blob/master/lib/hash.js
-var crypto = require('crypto');
+var crypto = require("crypto");
+
 function createLMResponse(challenge, lmhash) {
     var buf = new Buffer.alloc(24), pwBuffer = new Buffer.alloc(21).fill(0);
     lmhash.copy(pwBuffer);
@@ -9,19 +10,21 @@ function createLMResponse(challenge, lmhash) {
     calculateDES(pwBuffer.slice(14), challenge).copy(buf, 16);
     return buf;
 }
+
 function createLMHash(password) {
-    var buf = new Buffer.alloc(16), pwBuffer = new Buffer.alloc(14), magicKey = new Buffer.from('KGS!@#$%', 'ascii');
+    var buf = new Buffer.alloc(16), pwBuffer = new Buffer.alloc(14), magicKey = new Buffer.from("KGS!@#$%", "ascii");
     if (password.length > 14) {
         buf.fill(0);
         return buf;
     }
     pwBuffer.fill(0);
-    pwBuffer.write(password.toUpperCase(), 0, 'ascii');
+    pwBuffer.write(password.toUpperCase(), 0, "ascii");
     return Buffer.concat([
         calculateDES(pwBuffer.slice(0, 7), magicKey),
-        calculateDES(pwBuffer.slice(7), magicKey)
+        calculateDES(pwBuffer.slice(7), magicKey),
     ]);
 }
+
 function calculateDES(key, message) {
     var desKey = new Buffer.alloc(8);
     desKey[0] = key[0] & 0xFE;
@@ -39,9 +42,10 @@ function calculateDES(key, message) {
         }
         desKey[i] |= (parity % 2) === 0 ? 1 : 0;
     }
-    var des = crypto.createCipheriv('DES-ECB', desKey, '');
+    var des = crypto.createCipheriv("DES-ECB", desKey, "");
     return des.update(message);
 }
+
 function createNTLMResponse(challenge, ntlmhash) {
     var buf = new Buffer.alloc(24), ntlmBuffer = new Buffer.alloc(21).fill(0);
     ntlmhash.copy(ntlmBuffer);
@@ -50,30 +54,36 @@ function createNTLMResponse(challenge, ntlmhash) {
     calculateDES(ntlmBuffer.slice(14), challenge).copy(buf, 16);
     return buf;
 }
+
 function createNTLMHash(password) {
-    var md4sum = crypto.createHash('md4');
-    md4sum.update(new Buffer.from(password, 'ucs2'));
+    var md4sum = crypto.createHash("md4");
+    md4sum.update(new Buffer.from(password, "ucs2"));
     return md4sum.digest();
 }
+
 function createNTLMv2Hash(ntlmhash, username, authTargetName) {
-    var hmac = crypto.createHmac('md5', ntlmhash);
-    hmac.update(new Buffer.from(username.toUpperCase() + authTargetName, 'ucs2'));
+    var hmac = crypto.createHmac("md5", ntlmhash);
+    hmac.update(new Buffer.from(username.toUpperCase() + authTargetName, "ucs2"));
     return hmac.digest();
 }
+
 function createLMv2Response(type2message, username, ntlmhash, nonce, targetName) {
-    var buf = new Buffer.alloc(24), ntlm2hash = createNTLMv2Hash(ntlmhash, username, targetName), hmac = crypto.createHmac('md5', ntlm2hash);
+    var buf = new Buffer.alloc(24), ntlm2hash = createNTLMv2Hash(ntlmhash, username, targetName),
+        hmac = crypto.createHmac("md5", ntlm2hash);
     //server challenge
     type2message.challenge.copy(buf, 8);
     //client nonce
-    buf.write(nonce || createPseudoRandomValue(16), 16, 'hex');
+    buf.write(nonce || createPseudoRandomValue(16), 16, "hex");
     //create hash
     hmac.update(buf.slice(8));
     var hashedBuffer = hmac.digest();
     hashedBuffer.copy(buf);
     return buf;
 }
+
 function createNTLMv2Response(type2message, username, ntlmhash, nonce, targetName) {
-    var buf = new Buffer.alloc(48 + type2message.targetInfo.buffer.length), ntlm2hash = createNTLMv2Hash(ntlmhash, username, targetName), hmac = crypto.createHmac('md5', ntlm2hash);
+    var buf = new Buffer.alloc(48 + type2message.targetInfo.buffer.length),
+        ntlm2hash = createNTLMv2Hash(ntlmhash, username, targetName), hmac = crypto.createHmac("md5", ntlm2hash);
     //the first 8 bytes are spare to store the hashed value before the blob
     //server challenge
     type2message.challenge.copy(buf, 8);
@@ -86,12 +96,12 @@ function createNTLMv2Response(type2message, username, ntlmhash, nonce, targetNam
     // maybe think about a different solution here
     // 11644473600000 = diff between 1970 and 1601
     var timestamp = ((Date.now() + 11644473600000) * 10000).toString(16);
-    var timestampLow = Number('0x' + timestamp.substring(Math.max(0, timestamp.length - 8)));
-    var timestampHigh = Number('0x' + timestamp.substring(0, Math.max(0, timestamp.length - 8)));
+    var timestampLow = Number("0x" + timestamp.substring(Math.max(0, timestamp.length - 8)));
+    var timestampHigh = Number("0x" + timestamp.substring(0, Math.max(0, timestamp.length - 8)));
     buf.writeUInt32LE(timestampLow, 24, false);
     buf.writeUInt32LE(timestampHigh, 28, false);
     //random client nonce
-    buf.write(nonce || createPseudoRandomValue(16), 32, 'hex');
+    buf.write(nonce || createPseudoRandomValue(16), 32, "hex");
     //zero
     buf.writeUInt32LE(0, 40);
     //complete target information block from type 2 message
@@ -103,13 +113,15 @@ function createNTLMv2Response(type2message, username, ntlmhash, nonce, targetNam
     hashedBuffer.copy(buf);
     return buf;
 }
+
 function createPseudoRandomValue(length) {
-    var str = '';
+    var str = "";
     while (str.length < length) {
         str += Math.floor(Math.random() * 16).toString(16);
     }
     return str;
 }
+
 module.exports = {
     createLMHash: createLMHash,
     createNTLMHash: createNTLMHash,
@@ -117,6 +129,6 @@ module.exports = {
     createNTLMResponse: createNTLMResponse,
     createLMv2Response: createLMv2Response,
     createNTLMv2Response: createNTLMv2Response,
-    createPseudoRandomValue: createPseudoRandomValue
+    createPseudoRandomValue: createPseudoRandomValue,
 };
-//# sourceMappingURL=hash.js.map
\ No newline at end of file
+//# sourceMappingURL=hash.js.map
diff --git a/server/modules/axios-ntlm/lib/ntlm.js b/server/modules/axios-ntlm/lib/ntlm.js
index 54490c0a6..eda4b1b76 100644
--- a/server/modules/axios-ntlm/lib/ntlm.js
+++ b/server/modules/axios-ntlm/lib/ntlm.js
@@ -1,13 +1,14 @@
-'use strict';
+"use strict";
 // Original file https://raw.githubusercontent.com/elasticio/node-ntlm-client/master/lib/ntlm.js
-var os = require('os'), flags = require('./flags'), hash = require('./hash');
+var os = require("os"), flags = require("./flags"), hash = require("./hash");
 var NTLMSIGNATURE = "NTLMSSP\0";
+
 function createType1Message(workstation, target) {
     var dataPos = 32, pos = 0, buf = new Buffer.alloc(1024);
     workstation = workstation === undefined ? os.hostname() : workstation;
-    target = target === undefined ? '' : target;
+    target = target === undefined ? "" : target;
     //signature
-    buf.write(NTLMSIGNATURE, pos, NTLMSIGNATURE.length, 'ascii');
+    buf.write(NTLMSIGNATURE, pos, NTLMSIGNATURE.length, "ascii");
     pos += NTLMSIGNATURE.length;
     //message type
     buf.writeUInt32LE(1, pos);
@@ -27,7 +28,7 @@ function createType1Message(workstation, target) {
     buf.writeUInt32LE(target.length === 0 ? 0 : dataPos, pos);
     pos += 4;
     if (target.length > 0) {
-        dataPos += buf.write(target, dataPos, 'ascii');
+        dataPos += buf.write(target, dataPos, "ascii");
     }
     //workstation security buffer
     buf.writeUInt16LE(workstation.length, pos);
@@ -37,39 +38,39 @@ function createType1Message(workstation, target) {
     buf.writeUInt32LE(workstation.length === 0 ? 0 : dataPos, pos);
     pos += 4;
     if (workstation.length > 0) {
-        dataPos += buf.write(workstation, dataPos, 'ascii');
+        dataPos += buf.write(workstation, dataPos, "ascii");
     }
-    return 'NTLM ' + buf.toString('base64', 0, dataPos);
+    return "NTLM " + buf.toString("base64", 0, dataPos);
 }
+
 function decodeType2Message(str) {
     if (str === undefined) {
-        throw new Error('Invalid argument');
+        throw new Error("Invalid argument");
     }
     //convenience
-    if (Object.prototype.toString.call(str) !== '[object String]') {
-        if (str.hasOwnProperty('headers') && str.headers.hasOwnProperty('www-authenticate')) {
-            str = str.headers['www-authenticate'];
-        }
-        else {
-            throw new Error('Invalid argument');
+    if (Object.prototype.toString.call(str) !== "[object String]") {
+        if (str.hasOwnProperty("headers") && str.headers.hasOwnProperty("www-authenticate")) {
+            str = str.headers["www-authenticate"];
+        } else {
+            throw new Error("Invalid argument");
         }
     }
     var ntlmMatch = /^NTLM ([^,\s]+)/.exec(str);
     if (ntlmMatch) {
         str = ntlmMatch[1];
     }
-    var buf = new Buffer.from(str, 'base64'), obj = {};
+    var buf = new Buffer.from(str, "base64"), obj = {};
     //check signature
-    if (buf.toString('ascii', 0, NTLMSIGNATURE.length) !== NTLMSIGNATURE) {
-        throw new Error('Invalid message signature: ' + str);
+    if (buf.toString("ascii", 0, NTLMSIGNATURE.length) !== NTLMSIGNATURE) {
+        throw new Error("Invalid message signature: " + str);
     }
     //check message type
     if (buf.readUInt32LE(NTLMSIGNATURE.length) !== 2) {
-        throw new Error('Invalid message type (no type 2)');
+        throw new Error("Invalid message type (no type 2)");
     }
     //read flags
     obj.flags = buf.readUInt32LE(20);
-    obj.encoding = (obj.flags & flags.NTLMFLAG_NEGOTIATE_OEM) ? 'ascii' : 'ucs2';
+    obj.encoding = (obj.flags & flags.NTLMFLAG_NEGOTIATE_OEM) ? "ascii" : "ucs2";
     obj.version = (obj.flags & flags.NTLMFLAG_NEGOTIATE_NTLM2_KEY) ? 2 : 1;
     obj.challenge = buf.slice(24, 32);
     //read target name
@@ -78,10 +79,10 @@ function decodeType2Message(str) {
         //skipping allocated space
         var offset = buf.readUInt32LE(16);
         if (length === 0) {
-            return '';
+            return "";
         }
         if ((offset + length) > buf.length || offset < 32) {
-            throw new Error('Bad type 2 message');
+            throw new Error("Bad type 2 message");
         }
         return buf.toString(obj.encoding, offset, offset + length);
     })();
@@ -98,7 +99,7 @@ function decodeType2Message(str) {
                 return info;
             }
             if ((offset + length) > buf.length || offset < 32) {
-                throw new Error('Bad type 2 message');
+                throw new Error("Bad type 2 message");
             }
             var pos = offset;
             while (pos < (offset + length)) {
@@ -113,37 +114,38 @@ function decodeType2Message(str) {
                 var blockTypeStr = void 0;
                 switch (blockType) {
                     case 1:
-                        blockTypeStr = 'SERVER';
+                        blockTypeStr = "SERVER";
                         break;
                     case 2:
-                        blockTypeStr = 'DOMAIN';
+                        blockTypeStr = "DOMAIN";
                         break;
                     case 3:
-                        blockTypeStr = 'FQDN';
+                        blockTypeStr = "FQDN";
                         break;
                     case 4:
-                        blockTypeStr = 'DNS';
+                        blockTypeStr = "DNS";
                         break;
                     case 5:
-                        blockTypeStr = 'PARENT_DNS';
+                        blockTypeStr = "PARENT_DNS";
                         break;
                     default:
-                        blockTypeStr = '';
+                        blockTypeStr = "";
                         break;
                 }
                 if (blockTypeStr) {
-                    info[blockTypeStr] = buf.toString('ucs2', pos, pos + blockLength);
+                    info[blockTypeStr] = buf.toString("ucs2", pos, pos + blockLength);
                 }
                 pos += blockLength;
             }
             return {
                 parsed: info,
-                buffer: targetInfoBuffer
+                buffer: targetInfoBuffer,
             };
         })();
     }
     return obj;
 }
+
 function createType3Message(type2Message, username, password, workstation, target) {
     var dataPos = 52, buf = new Buffer.alloc(1024);
     if (workstation === undefined) {
@@ -153,12 +155,14 @@ function createType3Message(type2Message, username, password, workstation, targe
         target = type2Message.targetName;
     }
     //signature
-    buf.write(NTLMSIGNATURE, 0, NTLMSIGNATURE.length, 'ascii');
+    buf.write(NTLMSIGNATURE, 0, NTLMSIGNATURE.length, "ascii");
     //message type
     buf.writeUInt32LE(3, 8);
     if (type2Message.version === 2) {
         dataPos = 64;
-        var ntlmHash = hash.createNTLMHash(password), nonce = hash.createPseudoRandomValue(16), lmv2 = hash.createLMv2Response(type2Message, username, ntlmHash, nonce, target), ntlmv2 = hash.createNTLMv2Response(type2Message, username, ntlmHash, nonce, target);
+        var ntlmHash = hash.createNTLMHash(password), nonce = hash.createPseudoRandomValue(16),
+            lmv2 = hash.createLMv2Response(type2Message, username, ntlmHash, nonce, target),
+            ntlmv2 = hash.createNTLMv2Response(type2Message, username, ntlmHash, nonce, target);
         //lmv2 security buffer
         buf.writeUInt16LE(lmv2.length, 12);
         buf.writeUInt16LE(lmv2.length, 14);
@@ -171,9 +175,10 @@ function createType3Message(type2Message, username, password, workstation, targe
         buf.writeUInt32LE(dataPos, 24);
         ntlmv2.copy(buf, dataPos);
         dataPos += ntlmv2.length;
-    }
-    else {
-        var lmHash = hash.createLMHash(password), ntlmHash = hash.createNTLMHash(password), lm = hash.createLMResponse(type2Message.challenge, lmHash), ntlm = hash.createNTLMResponse(type2Message.challenge, ntlmHash);
+    } else {
+        var lmHash = hash.createLMHash(password), ntlmHash = hash.createNTLMHash(password),
+            lm = hash.createLMResponse(type2Message.challenge, lmHash),
+            ntlm = hash.createNTLMResponse(type2Message.challenge, ntlmHash);
         //lm security buffer
         buf.writeUInt16LE(lm.length, 12);
         buf.writeUInt16LE(lm.length, 14);
@@ -188,18 +193,18 @@ function createType3Message(type2Message, username, password, workstation, targe
         dataPos += ntlm.length;
     }
     //target name security buffer
-    buf.writeUInt16LE(type2Message.encoding === 'ascii' ? target.length : target.length * 2, 28);
-    buf.writeUInt16LE(type2Message.encoding === 'ascii' ? target.length : target.length * 2, 30);
+    buf.writeUInt16LE(type2Message.encoding === "ascii" ? target.length : target.length * 2, 28);
+    buf.writeUInt16LE(type2Message.encoding === "ascii" ? target.length : target.length * 2, 30);
     buf.writeUInt32LE(dataPos, 32);
     dataPos += buf.write(target, dataPos, type2Message.encoding);
     //user name security buffer
-    buf.writeUInt16LE(type2Message.encoding === 'ascii' ? username.length : username.length * 2, 36);
-    buf.writeUInt16LE(type2Message.encoding === 'ascii' ? username.length : username.length * 2, 38);
+    buf.writeUInt16LE(type2Message.encoding === "ascii" ? username.length : username.length * 2, 36);
+    buf.writeUInt16LE(type2Message.encoding === "ascii" ? username.length : username.length * 2, 38);
     buf.writeUInt32LE(dataPos, 40);
     dataPos += buf.write(username, dataPos, type2Message.encoding);
     //workstation name security buffer
-    buf.writeUInt16LE(type2Message.encoding === 'ascii' ? workstation.length : workstation.length * 2, 44);
-    buf.writeUInt16LE(type2Message.encoding === 'ascii' ? workstation.length : workstation.length * 2, 46);
+    buf.writeUInt16LE(type2Message.encoding === "ascii" ? workstation.length : workstation.length * 2, 44);
+    buf.writeUInt16LE(type2Message.encoding === "ascii" ? workstation.length : workstation.length * 2, 46);
     buf.writeUInt32LE(dataPos, 48);
     dataPos += buf.write(workstation, dataPos, type2Message.encoding);
     if (type2Message.version === 2) {
@@ -210,11 +215,12 @@ function createType3Message(type2Message, username, password, workstation, targe
         //flags
         buf.writeUInt32LE(type2Message.flags, 60);
     }
-    return 'NTLM ' + buf.toString('base64', 0, dataPos);
+    return "NTLM " + buf.toString("base64", 0, dataPos);
 }
+
 module.exports = {
     createType1Message: createType1Message,
     decodeType2Message: decodeType2Message,
-    createType3Message: createType3Message
+    createType3Message: createType3Message,
 };
-//# sourceMappingURL=ntlm.js.map
\ No newline at end of file
+//# sourceMappingURL=ntlm.js.map
diff --git a/server/modules/axios-ntlm/lib/ntlmClient.js b/server/modules/axios-ntlm/lib/ntlmClient.js
index 682de5f9a..dbd6ba189 100644
--- a/server/modules/axios-ntlm/lib/ntlmClient.js
+++ b/server/modules/axios-ntlm/lib/ntlmClient.js
@@ -1,57 +1,172 @@
 "use strict";
-var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
-    if (k2 === undefined) k2 = k;
-    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
-}) : (function(o, m, k, k2) {
-    if (k2 === undefined) k2 = k;
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function (o, m, k, k2) {
+    if (k2 === undefined) {
+        k2 = k;
+    }
+    Object.defineProperty(o, k2, {
+        enumerable: true,
+        get: function () {
+            return m[k];
+        },
+    });
+}) : (function (o, m, k, k2) {
+    if (k2 === undefined) {
+        k2 = k;
+    }
     o[k2] = m[k];
 }));
-var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
-    Object.defineProperty(o, "default", { enumerable: true, value: v });
-}) : function(o, v) {
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function (o, v) {
+    Object.defineProperty(o, "default", {
+        enumerable: true,
+        value: v,
+    });
+}) : function (o, v) {
     o["default"] = v;
 });
 var __importStar = (this && this.__importStar) || function (mod) {
-    if (mod && mod.__esModule) return mod;
+    if (mod && mod.__esModule) {
+        return mod;
+    }
     var result = {};
-    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+    if (mod != null) {
+        for (var k in mod) {
+            if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) {
+                __createBinding(result, mod, k);
+            }
+        }
+    }
     __setModuleDefault(result, mod);
     return result;
 };
 var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
-    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+    function adopt(value) {
+        return value instanceof P ? value : new P(function (resolve) {
+            resolve(value);
+        });
+    }
+
     return new (P || (P = Promise))(function (resolve, reject) {
-        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
-        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
-        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+        function fulfilled(value) {
+            try {
+                step(generator.next(value));
+            } catch (e) {
+                reject(e);
+            }
+        }
+
+        function rejected(value) {
+            try {
+                step(generator["throw"](value));
+            } catch (e) {
+                reject(e);
+            }
+        }
+
+        function step(result) {
+            result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
+        }
+
         step((generator = generator.apply(thisArg, _arguments || [])).next());
     });
 };
 var __generator = (this && this.__generator) || function (thisArg, body) {
-    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
-    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
-    function verb(n) { return function (v) { return step([n, v]); }; }
-    function step(op) {
-        if (f) throw new TypeError("Generator is already executing.");
-        while (_) try {
-            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
-            if (y = 0, t) op = [op[0] & 2, t.value];
-            switch (op[0]) {
-                case 0: case 1: t = op; break;
-                case 4: _.label++; return { value: op[1], done: false };
-                case 5: _.label++; y = op[1]; op = [0]; continue;
-                case 7: op = _.ops.pop(); _.trys.pop(); continue;
-                default:
-                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
-                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
-                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
-                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
-                    if (t[2]) _.ops.pop();
-                    _.trys.pop(); continue;
+    var _ = {
+        label: 0,
+        sent: function () {
+            if (t[0] & 1) {
+                throw t[1];
             }
-            op = body.call(thisArg, _);
-        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
-        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+            return t[1];
+        },
+        trys: [],
+        ops: [],
+    }, f, y, t, g;
+    return g = {
+        next: verb(0),
+        "throw": verb(1),
+        "return": verb(2),
+    }, typeof Symbol === "function" && (g[Symbol.iterator] = function () {
+        return this;
+    }), g;
+
+    function verb(n) {
+        return function (v) {
+            return step([ n, v ]);
+        };
+    }
+
+    function step(op) {
+        if (f) {
+            throw new TypeError("Generator is already executing.");
+        }
+        while (_) {
+            try {
+                if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) {
+                    return t;
+                }
+                if (y = 0, t) {
+                    op = [ op[0] & 2, t.value ];
+                }
+                switch (op[0]) {
+                    case 0:
+                    case 1:
+                        t = op;
+                        break;
+                    case 4:
+                        _.label++;
+                        return {
+                            value: op[1],
+                            done: false,
+                        };
+                    case 5:
+                        _.label++;
+                        y = op[1];
+                        op = [ 0 ];
+                        continue;
+                    case 7:
+                        op = _.ops.pop();
+                        _.trys.pop();
+                        continue;
+                    default:
+                        if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
+                            _ = 0;
+                            continue;
+                        }
+                        if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) {
+                            _.label = op[1];
+                            break;
+                        }
+                        if (op[0] === 6 && _.label < t[1]) {
+                            _.label = t[1];
+                            t = op;
+                            break;
+                        }
+                        if (t && _.label < t[2]) {
+                            _.label = t[2];
+                            _.ops.push(op);
+                            break;
+                        }
+                        if (t[2]) {
+                            _.ops.pop();
+                        }
+                        _.trys.pop();
+                        continue;
+                }
+                op = body.call(thisArg, _);
+            } catch (e) {
+                op = [ 6, e ];
+                y = 0;
+            } finally {
+                f = t = 0;
+            }
+        }
+        if (op[0] & 5) {
+            throw op[1];
+        }
+        return {
+            value: op[0] ? op[1] : void 0,
+            done: true,
+        };
     }
 };
 var __importDefault = (this && this.__importDefault) || function (mod) {
@@ -64,12 +179,13 @@ var ntlm = __importStar(require("./ntlm"));
 var https = __importStar(require("https"));
 var http = __importStar(require("http"));
 var dev_null_1 = __importDefault(require("dev-null"));
+
 /**
-* @param credentials An NtlmCredentials object containing the username and password
-* @param AxiosConfig The Axios config for the instance you wish to create
-*
-* @returns This function returns an axios instance configured to use the provided credentials
-*/
+ * @param credentials An NtlmCredentials object containing the username and password
+ * @param AxiosConfig The Axios config for the instance you wish to create
+ *
+ * @returns This function returns an axios instance configured to use the provided credentials
+ */
 function NtlmClient(credentials, AxiosConfig) {
     var _this = this;
     var config = AxiosConfig !== null && AxiosConfig !== void 0 ? AxiosConfig : {};
@@ -82,46 +198,56 @@ function NtlmClient(credentials, AxiosConfig) {
     var client = axios_1.default.create(config);
     client.interceptors.response.use(function (response) {
         return response;
-    }, function (err) { return __awaiter(_this, void 0, void 0, function () {
-        var error, t1Msg, t2Msg, t3Msg, stream_1;
-        var _a;
-        return __generator(this, function (_b) {
-            switch (_b.label) {
-                case 0:
-                    error = err.response;
-                    if (!(error && error.status === 401
-                        && error.headers['www-authenticate']
-                        && error.headers['www-authenticate'].includes('NTLM'))) return [3 /*break*/, 3];
-                    // This length check is a hack because SharePoint is awkward and will
-                    // include the Negotiate option when responding with the T2 message
-                    // There is nore we could do to ensure we are processing correctly,
-                    // but this is the easiest option for now
-                    if (error.headers['www-authenticate'].length < 50) {
-                        t1Msg = ntlm.createType1Message(credentials.workstation, credentials.domain);
-                        error.config.headers["Authorization"] = t1Msg;
-                    }
-                    else {
-                        t2Msg = ntlm.decodeType2Message((error.headers['www-authenticate'].match(/^NTLM\s+(.+?)(,|\s+|$)/) || [])[1]);
-                        t3Msg = ntlm.createType3Message(t2Msg, credentials.username, credentials.password, credentials.workstation, credentials.domain);
-                        error.config.headers["X-retry"] = "false";
-                        error.config.headers["Authorization"] = t3Msg;
-                    }
-                    if (!(error.config.responseType === "stream")) return [3 /*break*/, 2];
-                    stream_1 = (_a = err.response) === null || _a === void 0 ? void 0 : _a.data;
-                    if (!(stream_1 && !stream_1.readableEnded)) return [3 /*break*/, 2];
-                    return [4 /*yield*/, new Promise(function (resolve) {
+    }, function (err) {
+        return __awaiter(_this, void 0, void 0, function () {
+            var error, t1Msg, t2Msg, t3Msg, stream_1;
+            var _a;
+            return __generator(this, function (_b) {
+                switch (_b.label) {
+                    case 0:
+                        error = err.response;
+                        if (!(error && error.status === 401
+                            && error.headers["www-authenticate"]
+                            && error.headers["www-authenticate"].includes("NTLM"))) {
+                            return [ 3 /*break*/, 3 ];
+                        }
+                        // This length check is a hack because SharePoint is awkward and will
+                        // include the Negotiate option when responding with the T2 message
+                        // There is nore we could do to ensure we are processing correctly,
+                        // but this is the easiest option for now
+                        if (error.headers["www-authenticate"].length < 50) {
+                            t1Msg = ntlm.createType1Message(credentials.workstation, credentials.domain);
+                            error.config.headers["Authorization"] = t1Msg;
+                        } else {
+                            t2Msg = ntlm.decodeType2Message((error.headers["www-authenticate"].match(/^NTLM\s+(.+?)(,|\s+|$)/) || [])[1]);
+                            t3Msg = ntlm.createType3Message(t2Msg, credentials.username, credentials.password, credentials.workstation, credentials.domain);
+                            error.config.headers["X-retry"] = "false";
+                            error.config.headers["Authorization"] = t3Msg;
+                        }
+                        if (!(error.config.responseType === "stream")) {
+                            return [ 3 /*break*/, 2 ];
+                        }
+                        stream_1 = (_a = err.response) === null || _a === void 0 ? void 0 : _a.data;
+                        if (!(stream_1 && !stream_1.readableEnded)) {
+                            return [ 3 /*break*/, 2 ];
+                        }
+                        return [ 4 /*yield*/, new Promise(function (resolve) {
                             stream_1.pipe((0, dev_null_1.default)());
                             stream_1.once('close', resolve);
-                        })];
-                case 1:
-                    _b.sent();
-                    _b.label = 2;
-                case 2: return [2 /*return*/, client(error.config)];
-                case 3: throw err;
-            }
+                        }) ];
+                    case 1:
+                        _b.sent();
+                        _b.label = 2;
+                    case 2:
+                        return [ 2 /*return*/, client(error.config) ];
+                    case 3:
+                        throw err;
+                }
+            });
         });
-    }); });
+    });
     return client;
 }
+
 exports.NtlmClient = NtlmClient;
-//# sourceMappingURL=ntlmClient.js.map
\ No newline at end of file
+//# sourceMappingURL=ntlmClient.js.map
diff --git a/server/modules/dayjs/plugin/timezone.d.ts b/server/modules/dayjs/plugin/timezone.d.ts
index d504f6927..4a66bb355 100644
--- a/server/modules/dayjs/plugin/timezone.d.ts
+++ b/server/modules/dayjs/plugin/timezone.d.ts
@@ -1,20 +1,24 @@
-import { PluginFunc, ConfigType } from 'dayjs'
+import { PluginFunc, ConfigType } from "dayjs";
 
-declare const plugin: PluginFunc
+declare const plugin: PluginFunc;
 export = plugin
 
-declare module 'dayjs' {
-  interface Dayjs {
-    tz(timezone?: string, keepLocalTime?: boolean): Dayjs
-    offsetName(type?: 'short' | 'long'): string | undefined
-  }
+declare module "dayjs" {
+    interface Dayjs {
+        tz(timezone?: string, keepLocalTime?: boolean): Dayjs;
 
-  interface DayjsTimezone {
-    (date: ConfigType, timezone?: string): Dayjs
-    (date: ConfigType, format: string, timezone?: string): Dayjs
-    guess(): string
-    setDefault(timezone?: string): void
-  }
+        offsetName(type?: "short" | "long"): string | undefined;
+    }
 
-  const tz: DayjsTimezone
+    interface DayjsTimezone {
+        (date: ConfigType, timezone?: string): Dayjs;
+
+        (date: ConfigType, format: string, timezone?: string): Dayjs;
+
+        guess(): string;
+
+        setDefault(timezone?: string): void;
+    }
+
+    const tz: DayjsTimezone;
 }
diff --git a/server/modules/dayjs/plugin/timezone.js b/server/modules/dayjs/plugin/timezone.js
index de709ae4b..32abb4270 100644
--- a/server/modules/dayjs/plugin/timezone.js
+++ b/server/modules/dayjs/plugin/timezone.js
@@ -15,7 +15,7 @@
         day: 2,
         hour: 3,
         minute: 4,
-        second: 5
+        second: 5,
     };
     let e = {};
     return function (n, i, o) {
@@ -37,7 +37,7 @@
                     hour: "2-digit",
                     minute: "2-digit",
                     second: "2-digit",
-                    timeZoneName: i
+                    timeZoneName: i,
                 }), e[o] = r), r;
             }(n, i);
             return r.formatToParts(o);
diff --git a/server/monitor-types/dns.js b/server/monitor-types/dns.js
index 8b87932fe..5e57ba476 100644
--- a/server/monitor-types/dns.js
+++ b/server/monitor-types/dns.js
@@ -1,5 +1,8 @@
 const { MonitorType } = require("./monitor-type");
-const { UP, DOWN } = require("../../src/util");
+const {
+    UP,
+    DOWN,
+} = require("../../src/util");
 const dayjs = require("dayjs");
 const { dnsResolve } = require("../util-server");
 const { R } = require("redbean-node");
@@ -14,7 +17,7 @@ class DnsMonitorType extends MonitorType {
     supportsConditions = true;
 
     conditionVariables = [
-        new ConditionVariable("record", defaultStringOperators ),
+        new ConditionVariable("record", defaultStringOperators),
     ];
 
     /**
diff --git a/server/monitor-types/mqtt.js b/server/monitor-types/mqtt.js
index ad734ce8e..923c4d418 100644
--- a/server/monitor-types/mqtt.js
+++ b/server/monitor-types/mqtt.js
@@ -1,5 +1,8 @@
 const { MonitorType } = require("./monitor-type");
-const { log, UP } = require("../../src/util");
+const {
+    log,
+    UP,
+} = require("../../src/util");
 const mqtt = require("mqtt");
 const jsonata = require("jsonata");
 
@@ -57,7 +60,12 @@ class MqttMonitorType extends MonitorType {
      */
     mqttAsync(hostname, topic, options = {}) {
         return new Promise((resolve, reject) => {
-            const { port, username, password, interval = 20 } = options;
+            const {
+                port,
+                username,
+                password,
+                interval = 20,
+            } = options;
 
             // Adds MQTT protocol to the hostname if not already present
             if (!/^(?:http|mqtt|ws)s?:\/\//.test(hostname)) {
@@ -77,7 +85,7 @@ class MqttMonitorType extends MonitorType {
             let client = mqtt.connect(mqttUrl, {
                 username,
                 password,
-                clientId: "uptime-kuma_" + Math.random().toString(16).substr(2, 8)
+                clientId: "uptime-kuma_" + Math.random().toString(16).substr(2, 8),
             });
 
             client.on("connect", () => {
diff --git a/server/monitor-types/rabbitmq.js b/server/monitor-types/rabbitmq.js
index 165a0ed91..d1868fd01 100644
--- a/server/monitor-types/rabbitmq.js
+++ b/server/monitor-types/rabbitmq.js
@@ -1,5 +1,9 @@
 const { MonitorType } = require("./monitor-type");
-const { log, UP, DOWN } = require("../../src/util");
+const {
+    log,
+    UP,
+    DOWN,
+} = require("../../src/util");
 const { axiosAbortSignal } = require("../util-server");
 const axios = require("axios");
 
@@ -21,7 +25,7 @@ class RabbitMqMonitorType extends MonitorType {
         for (let baseUrl of baseUrls) {
             try {
                 // Without a trailing slash, path in baseUrl will be removed. https://example.com/api -> https://example.com
-                if ( !baseUrl.endsWith("/") ) {
+                if (!baseUrl.endsWith("/")) {
                     baseUrl += "/";
                 }
                 const options = {
diff --git a/server/monitor-types/real-browser-monitor-type.js b/server/monitor-types/real-browser-monitor-type.js
index 2a2871d2c..c1538ca1c 100644
--- a/server/monitor-types/real-browser-monitor-type.js
+++ b/server/monitor-types/real-browser-monitor-type.js
@@ -1,6 +1,9 @@
 const { MonitorType } = require("./monitor-type");
 const { chromium } = require("playwright-core");
-const { UP, log } = require("../../src/util");
+const {
+    UP,
+    log,
+} = require("../../src/util");
 const { Settings } = require("../settings");
 const commandExistsSync = require("command-exists").sync;
 const childProcess = require("child_process");
@@ -122,7 +125,7 @@ async function prepareChromeExecutable(executablePath) {
             executablePath = "/usr/bin/chromium";
 
             // Install chromium in container via apt install
-            if ( !commandExistsSync(executablePath)) {
+            if (!commandExistsSync(executablePath)) {
                 await new Promise((resolve, reject) => {
                     log.info("Chromium", "Installing Chromium...");
                     let child = childProcess.exec("apt update && apt --yes --no-install-recommends install chromium fonts-indic fonts-noto fonts-noto-cjk");
@@ -213,6 +216,7 @@ async function testChrome(executablePath) {
         throw new Error(e.message);
     }
 }
+
 // test remote browser
 /**
  * @param {string} remoteBrowserURL Remote Browser URL
@@ -228,6 +232,7 @@ async function testRemoteBrowser(remoteBrowserURL) {
         throw new Error(e.message);
     }
 }
+
 class RealBrowserMonitorType extends MonitorType {
 
     name = "real-browser";
diff --git a/server/monitor-types/snmp.js b/server/monitor-types/snmp.js
index a1760fa3d..a9066514e 100644
--- a/server/monitor-types/snmp.js
+++ b/server/monitor-types/snmp.js
@@ -1,5 +1,9 @@
 const { MonitorType } = require("./monitor-type");
-const { UP, log, evaluateJsonQuery } = require("../../src/util");
+const {
+    UP,
+    log,
+    evaluateJsonQuery,
+} = require("../../src/util");
 const snmp = require("net-snmp");
 
 class SNMPMonitorType extends MonitorType {
@@ -42,7 +46,10 @@ class SNMPMonitorType extends MonitorType {
             // We restrict querying to one OID per monitor, therefore `varbinds[0]` will always contain the value we're interested in.
             const value = varbinds[0].value;
 
-            const { status, response } = await evaluateJsonQuery(value, monitor.jsonPath, monitor.jsonPathOperator, monitor.expectedValue);
+            const {
+                status,
+                response,
+            } = await evaluateJsonQuery(value, monitor.jsonPath, monitor.jsonPathOperator, monitor.expectedValue);
 
             if (status) {
                 heartbeat.status = UP;
diff --git a/server/notification-providers/gov-notify.js b/server/notification-providers/gov-notify.js
index 1fb4188ba..5c95b4900 100644
--- a/server/notification-providers/gov-notify.js
+++ b/server/notification-providers/gov-notify.js
@@ -5,6 +5,15 @@ const NotifyClient = require("notifications-node-client").NotifyClient;
 class GovNotify extends NotificationProvider {
     name = "GovNotify";
 
+    /**
+     * Sends notifications via email and SMS using the GOV.UK Notify service.
+     * @param {object} notification The notification object containing configuration such as API key, email recipients, SMS recipients, message template, and template IDs for email and SMS.
+     * @param {string} msg The message content to send if no message template is provided in the notification object.
+     * @param {object | null} monitorJSON Optional parameter containing monitoring-related data.
+     * @param {object | null} heartbeatJSON Optional parameter containing heartbeat-related data, used to determine notification subject (e.g., status up or down).
+     * @returns {Promise<string>} A promise that resolves to a success message after sending notifications or rejects with an error if the sending fails.
+     * @throws {Error} Throws an error if notification sending fails.
+     */
     async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
         try {
             const apiKey = notification.apiKey;
diff --git a/server/notification-providers/home-assistant.js b/server/notification-providers/home-assistant.js
index 4536b2a43..6de759793 100644
--- a/server/notification-providers/home-assistant.js
+++ b/server/notification-providers/home-assistant.js
@@ -20,19 +20,21 @@ class HomeAssistant extends NotificationProvider {
                 {
                     title: "Uptime Kuma",
                     message: msg,
-                    ...(notificationService !== "persistent_notification" && { data: {
-                        name: monitorJSON?.name,
-                        status: heartbeatJSON?.status,
-                        channel: "Uptime Kuma",
-                        icon_url: "https://github.com/louislam/uptime-kuma/blob/master/public/icon.png?raw=true",
-                    } }),
+                    ...(notificationService !== "persistent_notification" && {
+                        data: {
+                            name: monitorJSON?.name,
+                            status: heartbeatJSON?.status,
+                            channel: "Uptime Kuma",
+                            icon_url: "https://github.com/louislam/uptime-kuma/blob/master/public/icon.png?raw=true",
+                        },
+                    }),
                 },
                 {
                     headers: {
                         Authorization: `Bearer ${notification.longLivedAccessToken}`,
                         "Content-Type": "application/json",
                     },
-                }
+                },
             );
 
             return okMsg;
diff --git a/server/notification.js b/server/notification.js
index efe67bf35..cac3ebdeb 100644
--- a/server/notification.js
+++ b/server/notification.js
@@ -159,7 +159,7 @@ class Notification {
             new GovNotify(),
         ];
         for (let item of list) {
-            if (! item.name) {
+            if (!item.name) {
                 throw new Error("Notification provider without name");
             }
 
@@ -203,7 +203,7 @@ class Notification {
                 userID,
             ]);
 
-            if (! bean) {
+            if (!bean) {
                 throw new Error("notification not found");
             }
 
@@ -236,7 +236,7 @@ class Notification {
             userID,
         ]);
 
-        if (! bean) {
+        if (!bean) {
             throw new Error("notification not found");
         }
 
@@ -263,7 +263,7 @@ class Notification {
  */
 async function applyNotificationEveryMonitor(notificationID, userID) {
     let monitors = await R.getAll("SELECT id FROM monitor WHERE user_id = ?", [
-        userID
+        userID,
     ]);
 
     for (let i = 0; i < monitors.length; i++) {
@@ -272,7 +272,7 @@ async function applyNotificationEveryMonitor(notificationID, userID) {
             notificationID,
         ]);
 
-        if (! checkNotification) {
+        if (!checkNotification) {
             let relation = R.dispense("monitor_notification");
             relation.monitor_id = monitors[i].id;
             relation.notification_id = notificationID;
diff --git a/server/prometheus.js b/server/prometheus.js
index f26125d2c..49d98ae52 100644
--- a/server/prometheus.js
+++ b/server/prometheus.js
@@ -12,24 +12,24 @@ const commonLabels = [
 const monitorCertDaysRemaining = new PrometheusClient.Gauge({
     name: "monitor_cert_days_remaining",
     help: "The number of days remaining until the certificate expires",
-    labelNames: commonLabels
+    labelNames: commonLabels,
 });
 
 const monitorCertIsValid = new PrometheusClient.Gauge({
     name: "monitor_cert_is_valid",
     help: "Is the certificate still valid? (1 = Yes, 0= No)",
-    labelNames: commonLabels
+    labelNames: commonLabels,
 });
 const monitorResponseTime = new PrometheusClient.Gauge({
     name: "monitor_response_time",
     help: "Monitor Response Time (ms)",
-    labelNames: commonLabels
+    labelNames: commonLabels,
 });
 
 const monitorStatus = new PrometheusClient.Gauge({
     name: "monitor_status",
     help: "Monitor Status (1 = UP, 0= DOWN, 2= PENDING, 3= MAINTENANCE)",
-    labelNames: commonLabels
+    labelNames: commonLabels,
 });
 
 class Prometheus {
@@ -44,7 +44,7 @@ class Prometheus {
             monitor_type: monitor.type,
             monitor_url: monitor.url,
             monitor_hostname: monitor.hostname,
-            monitor_port: monitor.port
+            monitor_port: monitor.port,
         };
     }
 
@@ -119,5 +119,5 @@ class Prometheus {
 }
 
 module.exports = {
-    Prometheus
+    Prometheus,
 };
diff --git a/server/proxy.js b/server/proxy.js
index d38e3e4f1..5501b3a9a 100644
--- a/server/proxy.js
+++ b/server/proxy.js
@@ -36,7 +36,7 @@ class Proxy {
         if (!this.SUPPORTED_PROXY_PROTOCOLS.includes(proxy.protocol)) {
             throw new Error(`
                 Unsupported proxy protocol "${proxy.protocol}.
-                Supported protocols are ${this.SUPPORTED_PROXY_PROTOCOLS.join(", ")}."`
+                Supported protocols are ${this.SUPPORTED_PROXY_PROTOCOLS.join(", ")}."`,
             );
         }
 
@@ -92,7 +92,10 @@ class Proxy {
      * @throws Proxy protocol is unsupported
      */
     static createAgents(proxy, options) {
-        const { httpAgentOptions, httpsAgentOptions } = options || {};
+        const {
+            httpAgentOptions,
+            httpsAgentOptions,
+        } = options || {};
         let agent;
         let httpAgent;
         let httpsAgent;
@@ -150,12 +153,13 @@ class Proxy {
                 httpsAgent = agent;
                 break;
 
-            default: throw new Error(`Unsupported proxy protocol provided. ${proxy.protocol}`);
+            default:
+                throw new Error(`Unsupported proxy protocol provided. ${proxy.protocol}`);
         }
 
         return {
             httpAgent,
-            httpsAgent
+            httpsAgent,
         };
     }
 
diff --git a/server/routers/api-router.js b/server/routers/api-router.js
index ed6db2cd1..ca773e8ea 100644
--- a/server/routers/api-router.js
+++ b/server/routers/api-router.js
@@ -11,7 +11,15 @@ const { R } = require("redbean-node");
 const apicache = require("../modules/apicache");
 const Monitor = require("../model/monitor");
 const dayjs = require("dayjs");
-const { UP, MAINTENANCE, DOWN, PENDING, flipStatus, log, badgeConstants } = require("../../src/util");
+const {
+    UP,
+    MAINTENANCE,
+    DOWN,
+    PENDING,
+    flipStatus,
+    log,
+    badgeConstants,
+} = require("../../src/util");
 const StatusPage = require("../model/status_page");
 const { UptimeKumaServer } = require("../uptime-kuma-server");
 const { makeBadge } = require("badge-maker");
@@ -28,7 +36,7 @@ let io = server.io;
 router.get("/api/entry-page", async (request, response) => {
     allowDevAllOrigin(response);
 
-    let result = { };
+    let result = {};
     let hostname = request.hostname;
     if ((await setting("trustProxy")) && request.headers["x-forwarded-host"]) {
         hostname = request.headers["x-forwarded-host"];
@@ -53,10 +61,10 @@ router.all("/api/push/:pushToken", async (request, response) => {
         let status = (statusString === "up") ? UP : DOWN;
 
         let monitor = await R.findOne("monitor", " push_token = ? AND active = 1 ", [
-            pushToken
+            pushToken,
         ]);
 
-        if (! monitor) {
+        if (!monitor) {
             throw new Error("Monitor not found or not active.");
         }
 
@@ -127,7 +135,7 @@ router.all("/api/push/:pushToken", async (request, response) => {
     } catch (e) {
         response.status(404).json({
             ok: false,
-            msg: e.message
+            msg: e.message,
         });
     }
 });
@@ -159,7 +167,7 @@ router.get("/api/badge/:id/status", cache("5 minutes"), async (request, response
                 AND monitor_group.monitor_id = ?
                 AND public = 1
             `,
-        [ requestedMonitorId ]
+            [ requestedMonitorId ],
         );
 
         const badgeValues = { style };
@@ -242,7 +250,7 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques
                 AND monitor_group.monitor_id = ?
                 AND public = 1
             `,
-        [ requestedMonitorId ]
+            [ requestedMonitorId ],
         );
 
         const badgeValues = { style };
@@ -362,7 +370,7 @@ router.get("/api/badge/:id/avg-response/:duration?", cache("5 minutes"), async (
             request.params.duration
                 ? parseInt(request.params.duration, 10)
                 : 24,
-            720
+            720,
         );
         const overrideValue = value && parseFloat(value);
 
@@ -376,7 +384,7 @@ router.get("/api/badge/:id/avg-response/:duration?", cache("5 minutes"), async (
             AND public = 1
             AND heartbeat.monitor_id = ?
             `,
-        [ -requestedDuration, requestedMonitorId ]
+            [ -requestedDuration, requestedMonitorId ],
         ));
 
         const badgeValues = { style };
@@ -443,7 +451,7 @@ router.get("/api/badge/:id/cert-exp", cache("5 minutes"), async (request, respon
             AND monitor_group.monitor_id = ?
             AND public = 1
             `,
-        [ requestedMonitorId ]
+            [ requestedMonitorId ],
         );
 
         const badgeValues = { style };
@@ -528,7 +536,7 @@ router.get("/api/badge/:id/response", cache("5 minutes"), async (request, respon
             AND monitor_group.monitor_id = ?
             AND public = 1
             `,
-        [ requestedMonitorId ]
+            [ requestedMonitorId ],
         );
 
         const badgeValues = { style };
@@ -540,7 +548,7 @@ router.get("/api/badge/:id/response", cache("5 minutes"), async (request, respon
             badgeValues.color = badgeConstants.naColor;
         } else {
             const heartbeat = await Monitor.getPreviousHeartbeat(
-                requestedMonitorId
+                requestedMonitorId,
             );
 
             if (!heartbeat.ping) {
diff --git a/server/routers/status-page-router.js b/server/routers/status-page-router.js
index 893f57564..54ee06018 100644
--- a/server/routers/status-page-router.js
+++ b/server/routers/status-page-router.js
@@ -2,7 +2,10 @@ let express = require("express");
 const apicache = require("../modules/apicache");
 const { UptimeKumaServer } = require("../uptime-kuma-server");
 const StatusPage = require("../model/status_page");
-const { allowDevAllOrigin, sendHttpError } = require("../util-server");
+const {
+    allowDevAllOrigin,
+    sendHttpError,
+} = require("../util-server");
 const { R } = require("redbean-node");
 const { badgeConstants } = require("../../src/util");
 const { makeBadge } = require("badge-maker");
@@ -44,7 +47,7 @@ router.get("/api/status-page/:slug", cache("5 minutes"), async (request, respons
     try {
         // Get Status Page
         let statusPage = await R.findOne("status_page", " slug = ? ", [
-            slug
+            slug,
         ]);
 
         if (!statusPage) {
@@ -81,7 +84,7 @@ router.get("/api/status-page/heartbeat/:slug", cache("1 minutes"), async (reques
             AND public = 1
             AND \`group\`.status_page_id = ?
         `, [
-            statusPageID
+            statusPageID,
         ]);
 
         for (let monitorID of monitorIDList) {
@@ -103,7 +106,7 @@ router.get("/api/status-page/heartbeat/:slug", cache("1 minutes"), async (reques
 
         response.json({
             heartbeatList,
-            uptimeList
+            uptimeList,
         });
 
     } catch (error) {
@@ -120,7 +123,7 @@ router.get("/api/status-page/:slug/manifest.json", cache("1440 minutes"), async
     try {
         // Get Status Page
         let statusPage = await R.findOne("status_page", " slug = ? ", [
-            slug
+            slug,
         ]);
 
         if (!statusPage) {
@@ -137,9 +140,9 @@ router.get("/api/status-page/:slug/manifest.json", cache("1440 minutes"), async
                 {
                     "src": statusPage.icon,
                     "sizes": "128x128",
-                    "type": "image/png"
-                }
-            ]
+                    "type": "image/png",
+                },
+            ],
         });
 
     } catch (error) {
@@ -159,7 +162,7 @@ router.get("/api/status-page/:slug/badge", cache("5 minutes"), async (request, r
         downColor = badgeConstants.defaultDownColor,
         partialColor = "#F6BE00",
         maintenanceColor = "#808080",
-        style = badgeConstants.defaultStyle
+        style = badgeConstants.defaultStyle,
     } = request.query;
 
     try {
@@ -169,7 +172,7 @@ router.get("/api/status-page/:slug/badge", cache("5 minutes"), async (request, r
             AND public = 1
             AND \`group\`.status_page_id = ?
         `, [
-            statusPageID
+            statusPageID,
         ]);
 
         let hasUp = false;
diff --git a/server/server.js b/server/server.js
index ec5ad49f6..19410e7b2 100644
--- a/server/server.js
+++ b/server/server.js
@@ -37,13 +37,19 @@ if (!semver.satisfies(nodeVersion, requiredNodeVersions)) {
 }
 
 const args = require("args-parser")(process.argv);
-const { sleep, log, getRandomInt, genSecret, isDev } = require("../src/util");
+const {
+    sleep,
+    log,
+    getRandomInt,
+    genSecret,
+    isDev,
+} = require("../src/util");
 const config = require("./config");
 
 log.debug("server", "Arguments");
 log.debug("server", args);
 
-if (! process.env.NODE_ENV) {
+if (!process.env.NODE_ENV) {
     process.env.NODE_ENV = "production";
 }
 
@@ -90,7 +96,16 @@ const Monitor = require("./model/monitor");
 const User = require("./model/user");
 
 log.debug("server", "Importing Settings");
-const { getSettings, setSettings, setting, initJWTSecret, checkLogin, doubleCheckPassword, shake256, SHAKE256_LENGTH, allowDevAllOrigin,
+const {
+    getSettings,
+    setSettings,
+    setting,
+    initJWTSecret,
+    checkLogin,
+    doubleCheckPassword,
+    shake256,
+    SHAKE256_LENGTH,
+    allowDevAllOrigin,
 } = require("./util-server");
 
 log.debug("server", "Importing Notification");
@@ -101,8 +116,14 @@ log.debug("server", "Importing Database");
 const Database = require("./database");
 
 log.debug("server", "Importing Background Jobs");
-const { initBackgroundJobs, stopBackgroundJobs } = require("./jobs");
-const { loginRateLimiter, twoFaRateLimiter } = require("./rate-limiter");
+const {
+    initBackgroundJobs,
+    stopBackgroundJobs,
+} = require("./jobs");
+const {
+    loginRateLimiter,
+    twoFaRateLimiter,
+} = require("./rate-limiter");
 
 const { apiAuth } = require("./auth");
 const { login } = require("./auth");
@@ -122,7 +143,7 @@ const cloudflaredToken = args["cloudflared-token"] || process.env.UPTIME_KUMA_CL
 // 2FA / notp verification defaults
 const twoFAVerifyOptions = {
     "window": 1,
-    "time": 30
+    "time": 30,
 };
 
 /**
@@ -132,13 +153,26 @@ const twoFAVerifyOptions = {
 const testMode = !!args["test"] || false;
 
 // Must be after io instantiation
-const { sendNotificationList, sendHeartbeatList, sendInfo, sendProxyList, sendDockerHostList, sendAPIKeyList, sendRemoteBrowserList, sendMonitorTypeList } = require("./client");
+const {
+    sendNotificationList,
+    sendHeartbeatList,
+    sendInfo,
+    sendProxyList,
+    sendDockerHostList,
+    sendAPIKeyList,
+    sendRemoteBrowserList,
+    sendMonitorTypeList,
+} = require("./client");
 const { statusPageSocketHandler } = require("./socket-handlers/status-page-socket-handler");
 const { databaseSocketHandler } = require("./socket-handlers/database-socket-handler");
 const { remoteBrowserSocketHandler } = require("./socket-handlers/remote-browser-socket-handler");
 const TwoFA = require("./2fa");
 const StatusPage = require("./model/status_page");
-const { cloudflaredSocketHandler, autoStart: cloudflaredAutoStart, stop: cloudflaredStop } = require("./socket-handlers/cloudflared-socket-handler");
+const {
+    cloudflaredSocketHandler,
+    autoStart: cloudflaredAutoStart,
+    stop: cloudflaredStop,
+} = require("./socket-handlers/cloudflared-socket-handler");
 const { proxySocketHandler } = require("./socket-handlers/proxy-socket-handler");
 const { dockerSocketHandler } = require("./socket-handlers/docker-socket-handler");
 const { maintenanceSocketHandler } = require("./socket-handlers/maintenance-socket-handler");
@@ -933,8 +967,9 @@ let needSetup = false;
                     monitorID,
                     socket.userID,
                 ]);
-                const monitorData = [{ id: monitor.id,
-                    active: monitor.active
+                const monitorData = [ {
+                    id: monitor.id,
+                    active: monitor.active,
                 }];
                 const preloadData = await Monitor.preparePreloadData(monitorData);
                 callback({
@@ -966,7 +1001,8 @@ let needSetup = false;
                     SELECT *
                     FROM heartbeat
                     WHERE monitor_id = ?
-                      AND time > ${sqlHourOffset}
+                      AND time
+                        > ${sqlHourOffset}
                     ORDER BY time ASC
                 `, [
                     monitorID,
@@ -1519,7 +1555,7 @@ let needSetup = false;
                 log.info("manage", `Clear Heartbeats Monitor: ${monitorID} User ID: ${socket.userID}`);
 
                 await R.exec("DELETE FROM heartbeat WHERE monitor_id = ?", [
-                    monitorID
+                    monitorID,
                 ]);
 
                 await sendHeartbeatList(socket, monitorID, true, true);
@@ -1658,7 +1694,7 @@ async function checkOwner(userID, monitorID) {
         userID,
     ]);
 
-    if (! row) {
+    if (!row) {
         throw new Error("You do not own this monitor.");
     }
 }
@@ -1698,7 +1734,7 @@ async function afterLogin(socket, user) {
 
     // Set server timezone from client browser if not set
     // It should be run once only
-    if (! await Settings.get("initServerTimezone")) {
+    if (!await Settings.get("initServerTimezone")) {
         log.debug("server", "emit initServerTimezone");
         socket.emit("initServerTimezone");
     }
@@ -1722,7 +1758,7 @@ async function initDatabase(testMode = false) {
         "jwtSecret",
     ]);
 
-    if (! jwtSecretBean) {
+    if (!jwtSecretBean) {
         log.info("server", "JWT secret is not found, generate one.");
         jwtSecretBean = await initJWTSecret();
         log.info("server", "Stored JWT secret into database");
diff --git a/server/settings.js b/server/settings.js
index 4776c554d..46dc48c0f 100644
--- a/server/settings.js
+++ b/server/settings.js
@@ -17,9 +17,7 @@ class Settings {
      *     }
      * @type {{}}
      */
-    static cacheList = {
-
-    };
+    static cacheList = {};
 
     static cacheCleaner = null;
 
@@ -61,7 +59,7 @@ class Settings {
 
             Settings.cacheList[key] = {
                 value: v,
-                timestamp: Date.now()
+                timestamp: Date.now(),
             };
 
             return v;
@@ -129,7 +127,7 @@ class Settings {
 
         for (let key of keyList) {
             let bean = await R.findOne("setting", " `key` = ? ", [
-                key
+                key,
             ]);
 
             if (bean == null) {
diff --git a/server/util-server.js b/server/util-server.js
index 5ebc62ac5..bfc309b05 100644
--- a/server/util-server.js
+++ b/server/util-server.js
@@ -1,7 +1,11 @@
 const tcpp = require("tcp-ping");
 const ping = require("@louislam/ping");
 const { R } = require("redbean-node");
-const { log, genSecret, badgeConstants } = require("../src/util");
+const {
+    log,
+    genSecret,
+    badgeConstants,
+} = require("../src/util");
 const passwordHash = require("./password-hash");
 const { Resolver } = require("dns");
 const iconv = require("iconv-lite");
@@ -22,14 +26,20 @@ const tls = require("tls");
 
 const {
     dictionaries: {
-        rfc2865: { file, attributes },
+        rfc2865: {
+            file,
+            attributes,
+        },
     },
 } = require("node-radius-utils");
 const dayjs = require("dayjs");
 
 // SASLOptions used in JSDoc
 // eslint-disable-next-line no-unused-vars
-const { Kafka, SASLOptions } = require("kafkajs");
+const {
+    Kafka,
+    SASLOptions,
+} = require("kafkajs");
 const crypto = require("crypto");
 
 const isWindows = process.platform === /^win/.test(process.platform);
@@ -75,7 +85,7 @@ exports.getOidcTokenClientCredentials = async (tokenEndpoint, clientId, clientSe
     let client = new oauthProvider.Client({
         client_id: clientId,
         client_secret: clientSecret,
-        token_endpoint_auth_method: authMethod
+        token_endpoint_auth_method: authMethod,
     });
 
     // Increase default timeout and clock tolerance
@@ -185,7 +195,12 @@ exports.pingAsync = function (hostname, ipv6 = false, size = 56) {
  */
 exports.kafkaProducerAsync = function (brokers, topic, message, options = {}, saslOptions = {}) {
     return new Promise((resolve, reject) => {
-        const { interval = 20, allowAutoTopicCreation = false, ssl = false, clientId = "Uptime-Kuma" } = options;
+        const {
+            interval = 20,
+            allowAutoTopicCreation = false,
+            ssl = false,
+            clientId = "Uptime-Kuma",
+        } = options;
 
         let connectedToKafka = false;
 
@@ -213,7 +228,7 @@ exports.kafkaProducerAsync = function (brokers, topic, message, options = {}, sa
             allowAutoTopicCreation: allowAutoTopicCreation,
             retry: {
                 retries: 0,
-            }
+            },
         });
 
         producer.connect().then(
@@ -234,14 +249,14 @@ exports.kafkaProducerAsync = function (brokers, topic, message, options = {}, sa
                     connectedToKafka = true;
                     clearTimeout(timeoutID);
                 });
-            }
+            },
         ).catch(
             (e) => {
                 connectedToKafka = true;
                 producer.disconnect();
                 clearTimeout(timeoutID);
                 reject(new Error("Error in producer connection: " + e.message));
-            }
+            },
         );
 
         producer.on("producer.network.request_timeout", (_) => {
@@ -409,7 +424,7 @@ exports.mysqlQuery = function (connectionString, query, password = undefined) {
     return new Promise((resolve, reject) => {
         const connection = mysql.createConnection({
             uri: connectionString,
-            password
+            password,
         });
 
         connection.on("error", (err) => {
@@ -494,8 +509,8 @@ exports.redisPingAsync = function (dsn, rejectUnauthorized) {
         const client = redis.createClient({
             url: dsn,
             socket: {
-                rejectUnauthorized
-            }
+                rejectUnauthorized,
+            },
         });
         client.on("error", (err) => {
             if (client.isOpen) {
@@ -661,7 +676,7 @@ exports.checkCertificate = function (socket) {
 
     return {
         valid: valid,
-        certInfo: parsedInfo
+        certInfo: parsedInfo,
     };
 };
 
@@ -693,7 +708,7 @@ exports.checkStatusCode = function (status, acceptedCodes) {
             }
         } else {
             log.error("monitor", `${codeRange} is not a valid status code range`);
-            continue;
+
         }
     }
 
@@ -925,14 +940,21 @@ module.exports.timeObjectToLocal = (obj, timezone = undefined) => {
  * @returns {Promise<object>} Result of gRPC query
  */
 module.exports.grpcQuery = async (options) => {
-    const { grpcUrl, grpcProtobufData, grpcServiceName, grpcEnableTls, grpcMethod, grpcBody } = options;
+    const {
+        grpcUrl,
+        grpcProtobufData,
+        grpcServiceName,
+        grpcEnableTls,
+        grpcMethod,
+        grpcBody,
+    } = options;
     const protocObject = protojs.parse(grpcProtobufData);
     const protoServiceObject = protocObject.root.lookupService(grpcServiceName);
     const Client = grpc.makeGenericClientConstructor({});
     const credentials = grpcEnableTls ? grpc.credentials.createSsl() : grpc.credentials.createInsecure();
     const client = new Client(
         grpcUrl,
-        credentials
+        credentials,
     );
     const grpcService = protoServiceObject.create(function (method, requestData, cb) {
         const fullServiceName = method.fullName;
@@ -955,14 +977,14 @@ module.exports.grpcQuery = async (options) => {
                     return resolve({
                         code: err.code,
                         errorMessage: err.details,
-                        data: ""
+                        data: "",
                     });
                 } else {
                     log.debug("monitor:", `gRPC response: ${JSON.stringify(response)}`);
                     return resolve({
                         code: 1,
                         errorMessage: "",
-                        data: responseData
+                        data: responseData,
                     });
                 }
             });
@@ -970,7 +992,7 @@ module.exports.grpcQuery = async (options) => {
             return resolve({
                 code: -1,
                 errorMessage: `Error ${err}. Please review your gRPC configuration option. The service name must not include package name value, and the method name must follow camelCase format`,
-                data: ""
+                data: "",
             });
         }
 
diff --git a/server/utils/array-with-key.js b/server/utils/array-with-key.js
index 94afc792a..1adbd9f9b 100644
--- a/server/utils/array-with-key.js
+++ b/server/utils/array-with-key.js
@@ -81,5 +81,5 @@ class ArrayWithKey {
 }
 
 module.exports = {
-    ArrayWithKey
+    ArrayWithKey,
 };
diff --git a/server/utils/limit-queue.js b/server/utils/limit-queue.js
index 9da6d410e..023f26e28 100644
--- a/server/utils/limit-queue.js
+++ b/server/utils/limit-queue.js
@@ -44,5 +44,5 @@ class LimitQueue extends ArrayWithKey {
 }
 
 module.exports = {
-    LimitQueue
+    LimitQueue,
 };

From fdd1ece65bbedb2b497b4a8f24190d147abadb11 Mon Sep 17 00:00:00 2001
From: Toby Liddicoat <toby@codesure.co.uk>
Date: Thu, 27 Feb 2025 20:08:44 +0000
Subject: [PATCH 4/5] Fix inconsistent bracket spacing and cleanup unused
 comment

Standardize spacing for array brackets across the codebase to improve readability and consistency. Additionally, relocate the eslint-disable comment for clarity and ensure proper association with the unused variable declaration.

Signed-off-by: Toby Liddicoat <toby@codesure.co.uk>
---
 server/util-server.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/server/util-server.js b/server/util-server.js
index bfc309b05..8e0fd5aa6 100644
--- a/server/util-server.js
+++ b/server/util-server.js
@@ -35,9 +35,9 @@ const {
 const dayjs = require("dayjs");
 
 // SASLOptions used in JSDoc
-// eslint-disable-next-line no-unused-vars
 const {
     Kafka,
+    // eslint-disable-next-line no-unused-vars
     SASLOptions,
 } = require("kafkajs");
 const crypto = require("crypto");

From 0ca978a97bef2add4cc3475e52022e0a3bd95211 Mon Sep 17 00:00:00 2001
From: Toby Liddicoat <toby@codesure.co.uk>
Date: Thu, 27 Feb 2025 20:15:35 +0000
Subject: [PATCH 5/5] Fix spacing inconsistencies in code formatting

Corrected spacing in function parameters and object definitions across multiple files to improve code readability and maintain consistency. No functionality was altered as part of these changes.

Signed-off-by: Toby Liddicoat <toby@codesure.co.uk>
---
 server/model/monitor.js      |  2 +-
 server/routers/api-router.js | 10 +++++-----
 server/server.js             |  2 +-
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/server/model/monitor.js b/server/model/monitor.js
index 8e2936f99..2696bf132 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -1364,7 +1364,7 @@ class Monitor extends BeanModel {
             for (let notification of notificationList) {
                 try {
                     const heartbeatJSON = bean.toJSON();
-                    const monitorData = [ {
+                    const monitorData = [{
                         id: monitor.id,
                         active: monitor.active,
                     }];
diff --git a/server/routers/api-router.js b/server/routers/api-router.js
index ca773e8ea..34f9b15b0 100644
--- a/server/routers/api-router.js
+++ b/server/routers/api-router.js
@@ -167,7 +167,7 @@ router.get("/api/badge/:id/status", cache("5 minutes"), async (request, response
                 AND monitor_group.monitor_id = ?
                 AND public = 1
             `,
-            [ requestedMonitorId ],
+        [ requestedMonitorId ],
         );
 
         const badgeValues = { style };
@@ -250,7 +250,7 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques
                 AND monitor_group.monitor_id = ?
                 AND public = 1
             `,
-            [ requestedMonitorId ],
+        [ requestedMonitorId ],
         );
 
         const badgeValues = { style };
@@ -384,7 +384,7 @@ router.get("/api/badge/:id/avg-response/:duration?", cache("5 minutes"), async (
             AND public = 1
             AND heartbeat.monitor_id = ?
             `,
-            [ -requestedDuration, requestedMonitorId ],
+        [ -requestedDuration, requestedMonitorId ],
         ));
 
         const badgeValues = { style };
@@ -451,7 +451,7 @@ router.get("/api/badge/:id/cert-exp", cache("5 minutes"), async (request, respon
             AND monitor_group.monitor_id = ?
             AND public = 1
             `,
-            [ requestedMonitorId ],
+        [ requestedMonitorId ],
         );
 
         const badgeValues = { style };
@@ -536,7 +536,7 @@ router.get("/api/badge/:id/response", cache("5 minutes"), async (request, respon
             AND monitor_group.monitor_id = ?
             AND public = 1
             `,
-            [ requestedMonitorId ],
+        [ requestedMonitorId ],
         );
 
         const badgeValues = { style };
diff --git a/server/server.js b/server/server.js
index 19410e7b2..90ce47922 100644
--- a/server/server.js
+++ b/server/server.js
@@ -967,7 +967,7 @@ let needSetup = false;
                     monitorID,
                     socket.userID,
                 ]);
-                const monitorData = [ {
+                const monitorData = [{
                     id: monitor.id,
                     active: monitor.active,
                 }];