diff --git a/package.json b/package.json index d34531b36..038d52ebd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uptime-kuma", - "version": "1.10.0", + "version": "1.10.1", "license": "MIT", "repository": { "type": "git", @@ -30,13 +30,13 @@ "build-docker": "npm run build-docker-debian && npm run build-docker-alpine", "build-docker-alpine-base": "docker buildx build -f docker/alpine-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base-alpine . --push", "build-docker-debian-base": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base-debian . --push", - "build-docker-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:alpine -t louislam/uptime-kuma:1-alpine -t louislam/uptime-kuma:1.10.0-alpine --target release . --push", - "build-docker-debian": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:1.10.0 -t louislam/uptime-kuma:debian -t louislam/uptime-kuma:1-debian -t louislam/uptime-kuma:1.10.0-debian --target release . --push", + "build-docker-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:alpine -t louislam/uptime-kuma:1-alpine -t louislam/uptime-kuma:1.10.1-alpine --target release . --push", + "build-docker-debian": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:1.10.1 -t louislam/uptime-kuma:debian -t louislam/uptime-kuma:1-debian -t louislam/uptime-kuma:1.10.1-debian --target release . --push", "build-docker-nightly": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly --target nightly . --push", "build-docker-nightly-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly-alpine --target nightly . --push", "build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain", "upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain", - "setup": "git checkout 1.10.0 && npm ci --production && npm run download-dist", + "setup": "git checkout 1.10.1 && npm ci --production && npm run download-dist", "download-dist": "node extra/download-dist.js", "update-version": "node extra/update-version.js", "mark-as-nightly": "node extra/mark-as-nightly.js", diff --git a/server/model/monitor.js b/server/model/monitor.js index 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/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/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 89639fd95..5578946bd 100644 --- a/src/assets/app.scss +++ b/src/assets/app.scss @@ -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 @@