From 19e0aaa3c06557a98ec248b57de6bf86b57ef2a7 Mon Sep 17 00:00:00 2001 From: casmbu Date: Tue, 2 Sep 2025 09:52:44 -0400 Subject: [PATCH 1/2] feat: uptime kuma responsive design, surfacing cert information, and pending/maintenance statuses; bug fix: don't show response time when it isn't applicable --- docs/widgets.md | 9 +- src/components/Widgets/UptimeKuma.vue | 134 ++++++++++++++++++++++---- 2 files changed, 118 insertions(+), 25 deletions(-) diff --git a/docs/widgets.md b/docs/widgets.md index de637eb8..5ea06c1d 100644 --- a/docs/widgets.md +++ b/docs/widgets.md @@ -2614,10 +2614,11 @@ Linkding is a self-hosted bookmarking service, which has a clean interface and i #### Options -| **Field** | **Type** | **Required** | **Description** | -| ------------ | -------- | ------------ | ------------------------------------------------------------------------ | -| **`url`** | `string` | Required | The URL of the Uptime Kuma instance | -| **`apiKey`** | `string` | Required | The API key (see https://github.com/louislam/uptime-kuma/wiki/API-Keys). | +| **Field** | **Type** | **Required** | **Description** | +| -------------- | --------- | ------------ | ------------------------------------------------------------------------ | +| **`url`** | `string` | Required | The URL of the Uptime Kuma instance | +| **`apiKey`** | `string` | Required | The API key (see https://github.com/louislam/uptime-kuma/wiki/API-Keys). | +| **`showCert`** | `boolean` | _Optional_ | If true, will show certificate information, when it is available, in the tooltip of the status pill (on mouse hover). Also will color the status yellow if the service is up but the certificate is invalid. Default false. | #### Example diff --git a/src/components/Widgets/UptimeKuma.vue b/src/components/Widgets/UptimeKuma.vue index e7fb312f..1978887c 100644 --- a/src/components/Widgets/UptimeKuma.vue +++ b/src/components/Widgets/UptimeKuma.vue @@ -5,11 +5,28 @@
{{ monitor.name }}
-
- {{ monitor.status }} +
+ + {{ monitor.status }} +
-
- {{ monitor.responseTime }}ms +
+ + {{ monitor.responseTime }}ms +
@@ -58,6 +75,10 @@ export default { url() { return this.parseAsEnvVar(this.options.url); }, + /* Determine if the cert information should be shown */ + showCert() { + return this.options.showCert; + }, /* Create authorisation header for the instance from the apiKey */ authHeaders() { if (!this.apiKey) { @@ -121,11 +142,14 @@ export default { const copy = { ...monitor }; switch (key) { case 'monitor_cert_days_remaining': { - copy.certDaysRemaining = value; + if (!this.showCert) break; + copy.certDaysTitle = ` for ${value} days`; break; } case 'monitor_cert_is_valid': { - copy.certValid = value; + if (!this.showCert) break; + copy.certClass = value === '1' ? 'valid-cert' : 'invalid-cert'; + copy.certTitle = `Certificate is ${value === '1' ? 'valid' : 'invalid'}`; break; } case 'monitor_response_time': { @@ -133,7 +157,24 @@ export default { break; } case 'monitor_status': { - copy.status = value === '1' ? 'Up' : 'Down'; + switch (value) { + case '1': { + copy.status = 'Up'; + break; + } + case '2': { + copy.status = 'Pending'; + break; + } + case '3': { + copy.status = 'Maintenance'; + break; + } + default: { + copy.status = 'Down'; + break; + } + } copy.statusClass = copy.status.toLowerCase(); break; } @@ -144,7 +185,7 @@ export default { return copy; }, getRowValue(row) { - return this.getValueWithRegex(row, /\b(\d+)(\.\d+)*\b$/); + return this.getValueWithRegex(row, /[\s](-?\d+)(\.\d+)*\b$/); }, getRowMonitorName(row) { return this.getValueWithRegex(row, /monitor_name="([^"]+)"/); @@ -155,13 +196,18 @@ export default { getValueWithRegex(string, regex) { const result = string.match(regex); - const isArray = Array.isArray(result); - - if (!isArray) { + if (!Array.isArray(result)) { return result; } - return result.length > 1 ? result[1] : result[0]; + if (result.length > 1) { + // -1 means N/A + if (result[1] === '-1' && !result[2]) { + return null; + } + return result[1]; + } + return result[0]; }, optionsValid({ url, authHeaders }) { const errors = []; @@ -199,12 +245,27 @@ export default { &.up { background-color: rgb(92, 221, 139); color: black; + + &.invalid-cert { + background-color: rgb(219, 216, 18); + color: black; + } } &.down { background-color: rgb(220, 53, 69); color: white; } + + &.pending { + background-color: rgb(108, 117, 125); + color: black; + } + + &.maintenance { + background-color: rgb(23, 71, 245); + color: black; + } } div.item.monitor-row:hover { @@ -217,22 +278,53 @@ div.item.monitor-row:hover { } } -.monitors-container { - display: flex; - flex-direction: row; - align-items: center; - justify-content: space-around; - width: 50%; +.item-wrapper { + container-type: inline-size; + container-name: item-wrapper-container; } .monitor-row { display: flex; - justify-content: space-between; + flex-direction: row; + justify-content: left; padding: 0.35em 0.5em; align-items: center; + + .title-title { + font-weight: bold; + text-align: left; + flex: 1 1 60%; + } + + .monitors-container { + display: flex; + flex-wrap: wrap; + flex: 0 1 40%; + align-items: center; + justify-content: space-around; + + .status-container.up-status { + min-width: 7.5rem; + } + .status-container.response-time { + min-width: 5rem; + } + } } -.title-title { - font-weight: bold; +@container item-wrapper-container (width < 320px) { + .monitor-row { + flex-direction: column; + justify-content: center; + + .title-title { + text-align: center; + } + + .monitors-container { + flex-wrap: unset; + flex-direction: column; + } + } } From 3995a3d9c8003b89b906fa14e8e26e87134cbaa0 Mon Sep 17 00:00:00 2001 From: casmbu Date: Tue, 2 Sep 2025 10:48:28 -0400 Subject: [PATCH 2/2] bug fix: remove useless if condition --- src/components/Widgets/UptimeKuma.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Widgets/UptimeKuma.vue b/src/components/Widgets/UptimeKuma.vue index 1978887c..e98b0cdb 100644 --- a/src/components/Widgets/UptimeKuma.vue +++ b/src/components/Widgets/UptimeKuma.vue @@ -24,7 +24,7 @@
- + {{ monitor.responseTime }}ms