diff --git a/.github/ISSUE_TEMPLATE/ask-for-help.yaml b/.github/ISSUE_TEMPLATE/ask-for-help.yaml index 76a78e5c3..3442e8b73 100644 --- a/.github/ISSUE_TEMPLATE/ask-for-help.yaml +++ b/.github/ISSUE_TEMPLATE/ask-for-help.yaml @@ -1,6 +1,6 @@ name: "❓ Ask for help" description: "Submit any question related to Uptime Kuma" -title: "[Help] " +#title: "[Help] " labels: [help] body: - type: checkboxes diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 055c3a626..2dca1556a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -1,6 +1,6 @@ name: "🐛 Bug Report" description: "Submit a bug report to help us improve" -title: "[Bug] " +#title: "[Bug] " labels: [bug] body: - type: checkboxes diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index 6464e276a..f0a92ede1 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -1,6 +1,6 @@ name: 🚀 Feature Request description: "Submit a proposal for a new feature" -title: "[Feature] " +#title: "[Feature] " labels: [enhancement] body: - type: checkboxes diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index fa0aa883b..3be229315 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -24,3 +24,5 @@ Please delete options that are not relevant. - [ ] My code needed automated testing. I have added them (this is optional task) ## Screenshots (if any) + +Please do not use any external image service. Instead, just paste in or drag and drop the image here and it will be uploaded automatically. diff --git a/extra/healthcheck.js b/extra/healthcheck.js index 8044f654f..c6e8a349a 100644 --- a/extra/healthcheck.js +++ b/extra/healthcheck.js @@ -35,7 +35,7 @@ let options = { let request = client.request(options, (res) => { console.log(`Health Check OK [Res Code: ${res.statusCode}]`); - if (res.statusCode === 200) { + if (res.statusCode === 302) { process.exit(0); } else { process.exit(1); diff --git a/package.json b/package.json index 64be83ee1..d34531b36 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "args-parser": "~1.3.0", "axios": "~0.21.4", "bcryptjs": "~2.4.3", - "bootstrap": "~5.1.3", + "bootstrap": "5.1.3", "bree": "~6.3.1", "chardet": "^1.3.0", "chart.js": "~3.6.0", diff --git a/server/model/monitor.js b/server/model/monitor.js index fc3292317..980e0ac69 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -141,6 +141,7 @@ class Monitor extends BeanModel { // Do not do any queries/high loading things before the "bean.ping" let startTime = dayjs().valueOf(); + debug(`[${this.name}] Prepare Options for axios`); const options = { url: this.url, method: (this.method || "get").toLowerCase(), @@ -160,6 +161,8 @@ class Monitor extends BeanModel { return checkStatusCode(status, this.getAcceptedStatuscodes()); }, }; + + debug(`[${this.name}] Axios Request`); let res = await axios.request(options); bean.msg = `${res.status} - ${res.statusText}`; bean.ping = dayjs().valueOf() - startTime; @@ -167,12 +170,13 @@ class Monitor extends BeanModel { // Check certificate if https is used let certInfoStartTime = dayjs().valueOf(); if (this.getUrl()?.protocol === "https:") { + debug(`[${this.name}] Check cert`); try { let tlsInfoObject = checkCertificate(res); tlsInfo = await this.updateTlsInfo(tlsInfoObject); if (!this.getIgnoreTls()) { - debug("call sendCertNotification"); + debug(`[${this.name}] call sendCertNotification`); await this.sendCertNotification(tlsInfoObject); } @@ -351,15 +355,19 @@ class Monitor extends BeanModel { let beatInterval = this.interval; + debug(`[${this.name}] Check isImportant`); let isImportant = Monitor.isImportantBeat(isFirstBeat, previousBeat?.status, bean.status); // Mark as important if status changed, ignore pending pings, // Don't notify if disrupted changes to up if (isImportant) { bean.important = true; + + debug(`[${this.name}] sendNotification`); await Monitor.sendNotification(isFirstBeat, this, bean); // Clear Status Page Cache + debug(`[${this.name}] apicache clear`); apicache.clear(); } else { @@ -377,10 +385,14 @@ class Monitor extends BeanModel { console.warn(`Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Interval: ${beatInterval} seconds | Type: ${this.type}`); } + debug(`[${this.name}] Send to socket`); io.to(this.user_id).emit("heartbeat", bean.toJSON()); Monitor.sendStats(io, this.id, this.user_id); + debug(`[${this.name}] Store`); await R.store(bean); + + debug(`[${this.name}] prometheus.update`); prometheus.update(bean, tlsInfo); previousBeat = bean; @@ -394,7 +406,10 @@ class Monitor extends BeanModel { } } + debug(`[${this.name}] SetTimeout for next check.`); this.heartbeatInterval = setTimeout(safeBeat, beatInterval * 1000); + } else { + console.log(`[${this.name}] isStop = true, no next check.`); } }; diff --git a/server/notification-providers/feishu.js b/server/notification-providers/feishu.js index a3e340301..05fc9c186 100644 --- a/server/notification-providers/feishu.js +++ b/server/notification-providers/feishu.js @@ -27,7 +27,7 @@ class Feishu extends NotificationProvider { content: { post: { zh_cn: { - title: "UptimeKuma Alert: " + monitorJSON["name"], + title: "UptimeKuma Alert: [Down] " + monitorJSON["name"], content: [ [ { @@ -54,7 +54,7 @@ class Feishu extends NotificationProvider { content: { post: { zh_cn: { - title: "UptimeKuma Alert: " + monitorJSON["name"], + title: "UptimeKuma Alert: [Up] " + monitorJSON["name"], content: [ [ { diff --git a/server/routers/api-router.js b/server/routers/api-router.js index fbe8136e5..79e828378 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -101,6 +101,10 @@ router.get("/api/status-page/config", async (_request, response) => { config.statusPagePublished = true; } + if (! config.statusPageTags) { + config.statusPageTags = false; + } + if (! config.title) { config.title = "Uptime Kuma"; } @@ -140,10 +144,25 @@ router.get("/api/status-page/monitor-list", cache("5 minutes"), async (_request, try { await checkPublished(); const publicGroupList = []; - let list = await R.find("group", " public = 1 ORDER BY weight "); - + const tagsVisible = (await getSettings("statusPage")).statusPageTags; + const list = await R.find("group", " public = 1 ORDER BY weight "); for (let groupBean of list) { - publicGroupList.push(await groupBean.toPublicJSON()); + let monitorGroup = await groupBean.toPublicJSON(); + if (tagsVisible) { + monitorGroup.monitorList = await Promise.all(monitorGroup.monitorList.map(async (monitor) => { + // Includes tags as an array in response, allows for tags to be displayed on public status page + const tags = await R.getAll( + `SELECT monitor_tag.monitor_id, monitor_tag.value, tag.name, tag.color + FROM monitor_tag + JOIN tag + ON monitor_tag.tag_id = tag.id + WHERE monitor_tag.monitor_id = ?`, [monitor.id] + ); + return {...monitor, tags: tags} + })); + } + + publicGroupList.push(monitorGroup); } response.json(publicGroupList); diff --git a/server/server.js b/server/server.js index 0af003d13..d1fd7ff29 100644 --- a/server/server.js +++ b/server/server.js @@ -186,6 +186,15 @@ exports.entryPage = "dashboard"; // Normal Router here // *************************** + // Entry Page + app.get("/", async (_request, response) => { + if (exports.entryPage === "statusPage") { + response.redirect("/status"); + } else { + response.redirect("/dashboard"); + } + }); + // Robots.txt app.get("/robots.txt", async (_request, response) => { let txt = "User-agent: *\nDisallow:"; diff --git a/server/util-server.js b/server/util-server.js index 5d65f46d3..68f59f67f 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -201,8 +201,13 @@ const getDaysRemaining = (validFrom, validTo) => { // param: info - the chain obtained from getPeerCertificate() const parseCertificateInfo = function (info) { let link = info; + let i = 0; + + const existingList = {}; while (link) { + debug(`[${i}] ${link.fingerprint}`); + if (!link.valid_from || !link.valid_to) { break; } @@ -210,15 +215,24 @@ const parseCertificateInfo = function (info) { link.validFor = link.subjectaltname?.replace(/DNS:|IP Address:/g, "").split(", "); link.daysRemaining = getDaysRemaining(new Date(), link.validTo); + existingList[link.fingerprint] = true; + // Move up the chain until loop is encountered if (link.issuerCertificate == null) { break; - } else if (link.fingerprint == link.issuerCertificate.fingerprint) { + } else if (link.issuerCertificate.fingerprint in existingList) { + debug(`[Last] ${link.issuerCertificate.fingerprint}`); link.issuerCertificate = null; break; } else { link = link.issuerCertificate; } + + // Should be no use, but just in case. + if (i > 500) { + throw new Error("Dead loop occurred in parseCertificateInfo"); + } + i++; } return info; @@ -228,6 +242,7 @@ exports.checkCertificate = function (res) { const info = res.request.res.socket.getPeerCertificate(true); const valid = res.request.res.socket.authorized || false; + debug("Parsing Certificate Info"); const parsedInfo = parseCertificateInfo(info); return { diff --git a/src/assets/app.scss b/src/assets/app.scss index e1a5d052d..5578946bd 100644 --- a/src/assets/app.scss +++ b/src/assets/app.scss @@ -189,7 +189,7 @@ textarea.form-control { opacity: 1; } - .table-hover > tbody > tr:hover { + .table-hover > tbody > tr:hover > * { --bs-table-accent-bg: #070a10; color: $dark-font-color; } @@ -346,6 +346,10 @@ textarea.form-control { &.active { background-color: #cdf8f4; } + .tags { + // Removes margin to line up tags list with uptime percentage + margin-left: -0.25rem; + } } } diff --git a/src/components/PublicGroupList.vue b/src/components/PublicGroupList.vue index 23d19e6cd..f30edcef5 100644 --- a/src/components/PublicGroupList.vue +++ b/src/components/PublicGroupList.vue @@ -41,6 +41,9 @@ {{ monitor.element.name }} +
+ +
@@ -59,12 +62,14 @@ import Draggable from "vuedraggable"; import HeartbeatBar from "./HeartbeatBar.vue"; import Uptime from "./Uptime.vue"; +import Tag from "./Tag.vue"; export default { components: { Draggable, HeartbeatBar, Uptime, + Tag, }, props: { editMode: { diff --git a/src/components/Uptime.vue b/src/components/Uptime.vue index a4bf22f68..2717672c4 100644 --- a/src/components/Uptime.vue +++ b/src/components/Uptime.vue @@ -1,5 +1,5 @@