Skip to content

Commit 2ab415b

Browse files
authored
Merge pull request #178 from Nedra1998/gatus
Add gatus monitor widget
2 parents b3935b0 + da9f1a0 commit 2ab415b

File tree

5 files changed

+314
-0
lines changed

5 files changed

+314
-0
lines changed

widgets/gatus-monitor/README.md

Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
## Introduction
2+
3+
This widget mimics the style of the builtin
4+
[monitor](https://github.com/glanceapp/glance/blob/main/docs/configuration.md#monitor)
5+
widget, but it pulls the data from a [Gatus](https://github.com/TwiN/gatus)
6+
instance instead. This allows including details like the uptime percentage and
7+
detailed status check results, in addition to the response times.
8+
9+
If you encounter any issues, please open an issue, tag me, and I’ll investigate further.
10+
11+
Customisation can be applied using the `options:` field. See [Options](#options) for more details.
12+
13+
## Preview
14+
15+
![Full style](preview.png)
16+
17+
![Condition failure details](conditions-preview.png)
18+
19+
![Compact style](compact-preview.png)
20+
21+
## Environment Variables
22+
23+
> [!IMPORTANT]
24+
>
25+
> For URLs, you **MUST** include `http://` or `https://`.
26+
> Do **NOT** include a trailing `/` at the end of URLs.
27+
28+
* `GATUS_URL` - The Gatus URL, e.g., `http://<ip_address>:<port>` or `https://<domain>`
29+
30+
## Options
31+
32+
Since `v0.8.0`, you can use the `options:` field to customise the widget.
33+
See [v0.8.0 Release Notes](https://github.com/glanceapp/glance/releases/tag/v0.8.0#g-rh-15) for more information.
34+
35+
Default options are:
36+
```yaml
37+
options:
38+
# Required options
39+
base-url: ${GATUS_URL} # Your environment-variables for the URL
40+
41+
# Optional options
42+
style: "full" # Display mode fo the widget, either `full`, or `compact`.
43+
duration: "24h" # The duration for the uptime and response time stats to be averaged over.
44+
groupFilter: "" # Filter to only show endpoints within this group from Gatus (or "" to include all groups).
45+
compactMetric: "uptime" # Which metric to show in `compact` style, either `uptime` or `response-time`
46+
showFailingOnly: false # Only display services currently failing healtchecks.
47+
showOnlyConfigured: false # Display only the endpoints which have an explicitly configured icon/url in these options.
48+
49+
# Endpoint options
50+
<ENDPOINT_NAME>: sh:immich # The display icon for the endpoint, supports URLS or the icon prefixes `si:`, `sh:`, `di:` or `mdi:`.
51+
<ENDPOINT_NAME>-url: https://example.com # Url to link to rather than the gatus page for the endpoint
52+
```
53+
54+
### Endpoint Options
55+
56+
By default this widget will automatically discover the endpoints configured in
57+
gatus and display them here. However you can add additional options to this
58+
widget to include icons and custom urls to link to for each endpoint. The
59+
`<ENDPOINT_NAME>` should match _exactly_ the endpoint name configured in Gatus
60+
(it can be quoted if the endpoint name includes special characters).
61+
62+
For example the options for the preview screenshot are:
63+
64+
```yaml
65+
options:
66+
base-url: http://gatus.example.com
67+
show-only-configured: true
68+
69+
Beszel: sh:beszel
70+
ConvertX: sh:convertx.png
71+
CyberChef: sh:cyberchef
72+
Feishin: sh:feishin.png
73+
Gatus: sh:gatus
74+
IT-Tools: di:it-tools-light
75+
MAZANOKE: sh:mazanoke
76+
Navidrome: sh:navidrome
77+
OmniTools: sh:omnitools.png
78+
Stirling-PDF: sh:stirling-pdf
79+
```
80+
81+
## Widget YAML
82+
83+
```yaml
84+
- type: custom-api
85+
title: Gatus
86+
cache: 5m
87+
options:
88+
base-url: ${GATUS_URL}
89+
90+
style: full
91+
duration: 24h
92+
groupFilter: ""
93+
compactMetric: uptime
94+
showFailingOnly: false
95+
showOnlyConfigured: false
96+
template: |
97+
{{/* Required config options */}}
98+
{{ $baseURL := .Options.StringOr "base-url" "" }}
99+
100+
{{/* Optional config options */}}
101+
{{ $duration := .Options.StringOr "duration" "24h" }}
102+
{{ $style := .Options.StringOr "style" "full" }}
103+
{{ $groupFilter := .Options.StringOr "group" "" }}
104+
{{ $compactMetric := .Options.StringOr "compact-metric" "uptime" }}
105+
{{ $showFailingOnly := .Options.BoolOr "show-failing-only" false }}
106+
{{ $showOnlyConfigured := .Options.BoolOr "show-only-configured" false }}
107+
108+
109+
{{/* Error message template */}}
110+
{{ define "errorMsg" }}
111+
<div class="widget-error-header">
112+
<div class="color-negative size-h3">ERROR</div>
113+
<svg class="widget-error-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5">
114+
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"></path>
115+
</svg>
116+
</div>
117+
<p class="break-all">{{ . }}</p>
118+
{{ end }}
119+
120+
{{/* Popover template */}}
121+
{{ define "popover" }}
122+
<div data-popover-html>
123+
<div style="margin: 5px;">
124+
<p class="margin-top-12" style="overflow-y: auto; text-align: justify; max-height: 20rem;">
125+
{{ range .Array "conditionResults" }}
126+
{{ if (.Bool "success") }}
127+
<p class="color-positive">{{ .String "condition" }}</p>
128+
{{ else }}
129+
<p class="color-negative">{{ .String "condition" }}</p>
130+
{{ end }}
131+
{{ end }}
132+
</p>
133+
</div>
134+
</div>
135+
{{ end }}
136+
137+
{{ $endpoints := newRequest (print $baseURL "/api/v1/endpoints/statuses")
138+
| getResponse }}
139+
140+
{{ $options := .Options }}
141+
{{ $displayedItems := 0 }}
142+
{{ if eq $style "compact" }}
143+
<ul class="dynamic-columns list-gap-8 ">
144+
{{ range $i, $endpoint := $endpoints.JSON.Array "" }}
145+
{{ $name := $endpoint.String "name" }}
146+
{{ $key := $endpoint.String "key" }}
147+
{{ $group := $endpoint.String "group" }}
148+
{{ $linkUrlOption := $options.StringOr (concat $name "-url") "" }}
149+
{{ $linkUrl := $options.StringOr (concat $name "-url") (concat $baseURL "/endpoints/" $key) }}
150+
{{ $lastResult := index ( $endpoint.Array "results" ) (sub (len ($endpoint.Array "results")) 1) }}
151+
{{ $latestSuccess := $lastResult.Bool "success" }}
152+
153+
{{ if and $groupFilter (not (eq $groupFilter $group)) }} {{ continue }} {{ end }}
154+
{{ if and $showFailingOnly $latestSuccess }} {{ continue }} {{ end }}
155+
{{ if and $showOnlyConfigured (eq $linkUrlOption "") }} {{ continue }} {{ end }}
156+
{{ $displayedItems = add $displayedItems 1 }}
157+
158+
{{ $responseTime := "" }}
159+
{{ $uptime := "" }}
160+
{{ $uptimeValue := "" }}
161+
162+
{{ if eq $compactMetric "uptime" }}
163+
{{ $uptime = newRequest (print $baseURL "/api/v1/endpoints/" $key "/uptimes/" $duration )
164+
| getResponse }}
165+
{{ $uptimeValue = mul 100 ($uptime.JSON.Float "") }}
166+
{{ else }}
167+
{{ $responseTime = newRequest (print $baseURL "/api/v1/endpoints/" $key "/response-times/" $duration )
168+
| getResponse }}
169+
{{ end }}
170+
171+
<div class="flex items-center gap-12">
172+
<a class="size-title-dynamic color-highlight text-truncate block grow" href="{{ $linkUrl | safeURL }}" target="_blank" rel="noreferrer">{{ $name }}</a>
173+
<a href="{{ $linkUrl | safeURL }}" target="_blank" rel="noreferrer">
174+
{{ if eq $compactMetric "uptime" }}
175+
<div>{{ printf "%.1f" $uptimeValue }}%</div>
176+
{{ else }}
177+
<div>{{ $responseTime.JSON.Int "" }}ms</div>
178+
{{ end }}
179+
</a>
180+
181+
{{ if $latestSuccess }}
182+
<div class="monitor-site-status-icon-compact">
183+
<a href="{{ $linkUrl | safeURL }}" target="_blank" rel="noreferrer">
184+
<div data-popover-type="html" data-popover-position="above" data-popover-show-delay="500" style="align-content: center;">
185+
{{ template "popover" $lastResult }}
186+
<svg fill="var(--color-positive)" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
187+
<path fill-rule="evenodd" d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16Zm3.857-9.809a.75.75 0 0 0-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 1 0-1.06 1.061l2.5 2.5a.75.75 0 0 0 1.137-.089l4-5.5Z" clip-rule="evenodd" />
188+
</svg>
189+
</div>
190+
</a>
191+
</div>
192+
{{ else }}
193+
<div class="monitor-site-status-icon-compact">
194+
<a href="{{ $linkUrl | safeURL }}" target="_blank" rel="noreferrer">
195+
<div data-popover-type="html" data-popover-position="above" data-popover-show-delay="500" style="align-content: center;">
196+
{{ template "popover" $lastResult }}
197+
<svg fill="var(--color-negative)" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
198+
<path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495ZM10 5a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 10 5Zm0 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z" clip-rule="evenodd" />
199+
</svg>
200+
</div>
201+
</a>
202+
</div>
203+
{{ end }}
204+
205+
</div>
206+
{{ end }}
207+
</ul>
208+
{{ else }}
209+
210+
<ul class="dynamic-columns list-gap-20 list-with-separator">
211+
{{ range $i, $endpoint := $endpoints.JSON.Array "" }}
212+
{{ $name := $endpoint.String "name" }}
213+
{{ $key := $endpoint.String "key" }}
214+
{{ $group := $endpoint.String "group" }}
215+
{{ $icon := $options.StringOr $name "" }}
216+
{{ $linkUrlOption := $options.StringOr (concat $name "-url") "" }}
217+
{{ $linkUrl := $options.StringOr (concat $name "-url") (concat $baseURL "/endpoints/" $key) }}
218+
{{ $lastResult := index ( $endpoint.Array "results" ) (sub (len ($endpoint.Array "results")) 1) }}
219+
{{ $latestSuccess := $lastResult.Bool "success" }}
220+
221+
{{ if and $groupFilter (not (eq $groupFilter $group)) }} {{ continue }} {{ end }}
222+
{{ if and $showFailingOnly $latestSuccess }} {{ continue }} {{ end }}
223+
{{ if and $showOnlyConfigured (or (eq $linkUrlOption "") (eq $icon "")) }} {{ continue }} {{ end }}
224+
{{ $displayedItems = add $displayedItems 1 }}
225+
226+
{{ $uptime := newRequest (print $baseURL "/api/v1/endpoints/" $key "/uptimes/" $duration )
227+
| getResponse }}
228+
{{ $responseTime := newRequest (print $baseURL "/api/v1/endpoints/" $key "/response-times/" $duration )
229+
| getResponse }}
230+
231+
{{ $uptimeValue := mul 100 ($uptime.JSON.Float "") }}
232+
233+
{{ $iconUrl := "" }}
234+
{{ if $icon }}
235+
{{ $iconPrefix := findMatch "^(si|di|mdi|sh):" $icon }}
236+
{{ $iconBase := replaceMatches "^(si|di|mdi|sh):" "" $icon }}
237+
238+
{{ $iconExt := findMatch "\\.[a-z]+$" $iconBase }}
239+
{{ $iconExt := replaceMatches "\\." "" $iconExt }}
240+
{{ $iconBase = replaceMatches "\\.[a-z]+$" "" $iconBase }}
241+
{{ if eq $iconExt "" }} {{ $iconExt = "svg" }} {{ end }}
242+
243+
{{ if eq $iconPrefix "si:" }}
244+
{{ $iconUrl = concat "https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/" $iconBase ".svg" }}
245+
{{ else if eq $iconPrefix "di:" }}
246+
{{ $iconUrl = concat "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/" $iconExt "/" $iconBase "." $iconExt }}
247+
{{ else if eq $iconPrefix "mdi:" }}
248+
{{ $iconUrl = concat "https://cdn.jsdelivr.net/npm/@mdi/svg@latest/svg/" $iconBase ".svg" }}
249+
{{ else if eq $iconPrefix "sh:" }}
250+
{{ $iconUrl = concat "https://cdn.jsdelivr.net/gh/selfhst/icons/" $iconExt "/" $iconBase "." $iconExt }}
251+
{{ else }}
252+
{{ $iconUrl = $icon }}
253+
{{ end }}
254+
{{ end }}
255+
256+
<div class="monitor-site flex items-center gap-15">
257+
{{ if $iconUrl }}
258+
<a href="{{ $linkUrl | safeURL }}" target="_blank" rel="noreferrer">
259+
<img class="monitor-site-icon" src="{{ $iconUrl | safeURL }}" alt="" loading="lazy">
260+
</a>
261+
{{ end }}
262+
<div class="grow min-width-0">
263+
<a class="size-h3 color-highlight text-truncate block" href="{{ $linkUrl | safeURL }}" target="_blank" rel="noreferrer">{{ $name }}</a>
264+
265+
<a href="{{ $linkUrl | safeURL }}" target="_blank" rel="noreferrer">
266+
<ul class="list-horizontal-text">
267+
<li class="{{ if not $latestSuccess }}color-negative{{ end }}">{{ printf "%.1f" $uptimeValue }}%</li>
268+
<li>{{ $responseTime.JSON.Int "" }}ms</li>
269+
</ul>
270+
</a>
271+
</div>
272+
273+
{{ if $latestSuccess }}
274+
<div class="monitor-site-status-icon">
275+
<a href="{{ $linkUrl | safeURL }}" target="_blank" rel="noreferrer">
276+
<div data-popover-type="html" data-popover-position="above" data-popover-show-delay="500" style="align-content: center;">
277+
{{ template "popover" $lastResult }}
278+
<svg fill="var(--color-positive)" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
279+
<path fill-rule="evenodd" d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16Zm3.857-9.809a.75.75 0 0 0-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 1 0-1.06 1.061l2.5 2.5a.75.75 0 0 0 1.137-.089l4-5.5Z" clip-rule="evenodd" />
280+
</svg>
281+
</div>
282+
</a>
283+
</div>
284+
{{ else }}
285+
<div class="monitor-site-status-icon">
286+
<a href="{{ $linkUrl | safeURL }}" target="_blank" rel="noreferrer">
287+
<div data-popover-type="html" data-popover-position="above" data-popover-show-delay="500" style="align-content: center;">
288+
{{ template "popover" $lastResult }}
289+
<svg fill="var(--color-negative)" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
290+
<path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495ZM10 5a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 10 5Zm0 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z" clip-rule="evenodd" />
291+
</svg>
292+
</div>
293+
</a>
294+
</div>
295+
{{ end }}
296+
297+
</div>
298+
{{ end }}
299+
</ul>
300+
{{ end }}
301+
302+
{{ if eq $displayedItems 0 }}
303+
<div class="flex items-center justify-center gap-10 padding-block-5">
304+
<p>All sites are online</p>
305+
<svg class="shrink-0" style="width: 1.7rem;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="var(--color-positive)">
306+
<path fill-rule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm13.36-1.814a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z" clip-rule="evenodd" />
307+
</svg>
308+
</div>
309+
{{ end }}
310+
```
311+
21.6 KB
Loading
7.3 KB
Loading

widgets/gatus-monitor/meta.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
title: Gatus Monitor
2+
description: Site monitor widget using Gatus for backend data
3+
author: Nedra1998

widgets/gatus-monitor/preview.png

41.1 KB
Loading

0 commit comments

Comments
 (0)