mirror of https://github.com/glanceapp/glance.git
Nowosci i poprawki:
- Nowy widget do beszel wraz z dokumentacja - Zmiana wielkosci widgetu radyjkopull/878/head
parent
2e8eb157ee
commit
6009e0579e
@ -0,0 +1,76 @@
|
||||
# Widget Beszel - Instrukcja użycia
|
||||
|
||||
Widget Beszel pozwala na monitorowanie statusu serwerów w czasie rzeczywistym, wykorzystując lekkie i nowoczesne narzędzie monitoringu Beszel.
|
||||
|
||||
## Konfiguracja
|
||||
|
||||
Aby skonfigurować widget Beszel, dodaj następującą konfigurację do swojego pliku `glance.yml`:
|
||||
|
||||
```yaml
|
||||
- type: beszel
|
||||
url: https://twoja-instancja-beszel.pl # URL do Twojej instancji Beszel (API)
|
||||
redirect-url: https://twoja-instancja-beszel.pl # URL do interfejsu webowego Beszel
|
||||
token: twoj-token-jwt # Token JWT (
|
||||
cache: 10s # Częstotliwość odświeżania (domyślnie 10s)
|
||||
```
|
||||
|
||||
### Uzyskiwanie tokenu
|
||||
|
||||
Jeśli Twoja instancja Beszel jest zabezpieczona i nie udostępnia danych publicznie, będziesz potrzebować tokenu.
|
||||
Obecnie widget obsługuje tokeny Bearer. Możesz uzyskać token logując się do Beszel i sprawdzając żądania sieciowe w przeglądarce lub generując go w panelu administracyjnym (jeśli dostępne).
|
||||
|
||||
## Funkcje widgetu
|
||||
|
||||
### 1. Monitorowanie statusu serwerów
|
||||
|
||||
Widget wyświetla listę serwerów z następującymi informacjami:
|
||||
- **Nazwa serwera**: Nazwa zdefiniowana w Beszel.
|
||||
- **Status**: Ikona serwera zmienia kolor w zależności od dostępności (zielony - online, czerwony - offline).
|
||||
- **Uptime**: Czas nieprzerwanej pracy serwera (np. "5 days uptime").
|
||||
|
||||
### 2. Szczegółowe metryki
|
||||
|
||||
Dla każdego serwera wyświetlane są paski postępu z aktualnym użyciem zasobów:
|
||||
- **CPU**: Aktualne użycie procesora w procentach.
|
||||
- **RAM**: Aktualne użycie pamięci RAM w procentach.
|
||||
- **DISK**: Zajętość głównego dysku w procentach.
|
||||
|
||||
### 3. Dodatkowe informacje (Popover)
|
||||
|
||||
Po najechaniu kursorem na ikonę serwera lub paski postępu, wyświetlane są dodatkowe informacje w dymku:
|
||||
- **Host/IP**: Adres IP lub nazwa hosta serwera.
|
||||
- **Kernel**: Wersja jądra systemu.
|
||||
- **CPU Model**: Model procesora.
|
||||
- **Load Average**: Średnie obciążenie systemu (1m, 5m, 15m) - dostępne po najechaniu na pasek CPU.
|
||||
|
||||
### 4. Linkowanie do systemu
|
||||
|
||||
Jeśli skonfigurowano parametr `redirect-url`, kliknięcie w nazwę serwera otworzy nową kartę z szczegółowymi statystykami tego konkretnego systemu w panelu Beszel.
|
||||
|
||||
## Przykładowa konfiguracja
|
||||
|
||||
```yaml
|
||||
pages:
|
||||
- name: Home
|
||||
columns:
|
||||
- size: small
|
||||
widgets:
|
||||
- type: beszel
|
||||
title: Serwery
|
||||
url: https://beszel.example.com
|
||||
redirect-url: https://beszel.example.com
|
||||
token: TWóJ_TOKEN
|
||||
cache: 5s
|
||||
```
|
||||
|
||||
## Rozwiązywanie problemów
|
||||
|
||||
### Widget nie wyświetla danych
|
||||
|
||||
1. Sprawdź czy URL do instancji Beszel jest poprawny i dostępny z serwera, na którym działa Glance.
|
||||
2. Upewnij się, że endpoint `/api/collections/systems/records` jest dostępny.
|
||||
3. Jeśli Twoja instancja wymaga autoryzacji, upewnij się, że podałeś poprawny token.
|
||||
|
||||
### Brakujące metryki
|
||||
|
||||
Niektóre metryki (np. Load Average) mogą nie być dostępne w zależności od wersji agenta Beszel zainstalowanego na monitorowanym serwerze.
|
||||
@ -0,0 +1,88 @@
|
||||
{{ template "widget-base.html" . }}
|
||||
|
||||
{{- define "widget-content" }}
|
||||
{{- $redirect := .RedirectURL }}
|
||||
{{- range .Systems }}
|
||||
<div class="server">
|
||||
<div class="server-info">
|
||||
<div class="server-details">
|
||||
<div class="server-name size-h3">
|
||||
{{ if ne $redirect "" }}
|
||||
<a class="color-highlight" href="{{ $redirect }}/system/{{ .Name }}" target="_blank" rel="noopener">{{ .Name }}</a>
|
||||
{{ else }}
|
||||
<span class="color-highlight">{{ .Name }}</span>
|
||||
{{ end }}
|
||||
</div>
|
||||
<div>
|
||||
{{ if eq .Status "up" }}
|
||||
<span {{ dynamicRelativeTimeAttrs .BootTime }}></span> uptime
|
||||
{{ else }}
|
||||
<span class="color-negative">down</span>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="shrink-0" data-popover-type="html" data-popover-margin="0.2rem" data-popover-max-width="400px">
|
||||
<div data-popover-html>
|
||||
<div class="size-h5 text-compact">HOST</div>
|
||||
<div class="color-highlight">{{ .Host }}</div>
|
||||
|
||||
<div class="size-h5 text-compact margin-top-3">KERNEL</div>
|
||||
<div class="color-highlight">{{ .Info.Kernel }}</div>
|
||||
|
||||
<div class="size-h5 text-compact margin-top-3">CPU MODEL</div>
|
||||
<div class="color-highlight">{{ .Info.CPUModel }}</div>
|
||||
</div>
|
||||
<svg class="server-icon" stroke="var(--color-{{ if eq .Status "up" }}positive{{ else }}negative{{ end }})" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21.75 17.25v-.228a4.5 4.5 0 0 0-.12-1.03l-2.268-9.64a3.375 3.375 0 0 0-3.285-2.602H7.923a3.375 3.375 0 0 0-3.285 2.602l-2.268 9.64a4.5 4.5 0 0 0-.12 1.03v.228m19.5 0a3 3 0 0 1-3 3H5.25a3 3 0 0 1-3-3m19.5 0a3 3 0 0 0-3-3H5.25a3 3 0 0 0-3 3m16.5 0h.008v.008h-.008v-.008Zm-3 0h.008v.008h-.008v-.008Z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="server-stats">
|
||||
<div class="flex-1">
|
||||
<div class="flex items-end size-h5">
|
||||
<div>CPU</div>
|
||||
<div class="color-highlight margin-left-auto text-very-compact">{{ printf "%.1f" .Info.CPU }} <span class="color-base">%</span></div>
|
||||
</div>
|
||||
<div class="progress-bar" data-popover-type="html">
|
||||
<div data-popover-html>
|
||||
<div class="flex">
|
||||
<div class="size-h5">1M AVG</div>
|
||||
<div class="value-separator"></div>
|
||||
<div class="color-highlight text-very-compact">{{ .Info.Load1 }}</div>
|
||||
</div>
|
||||
<div class="flex margin-top-3">
|
||||
<div class="size-h5">5M AVG</div>
|
||||
<div class="value-separator"></div>
|
||||
<div class="color-highlight text-very-compact">{{ .Info.Load5 }}</div>
|
||||
</div>
|
||||
<div class="flex margin-top-3">
|
||||
<div class="size-h5">15M AVG</div>
|
||||
<div class="value-separator"></div>
|
||||
<div class="color-highlight text-very-compact">{{ .Info.Load15 }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress-value{{ if ge .Info.CPU 85.0 }} progress-value-notice{{ end }}" style="--percent: {{ .Info.CPU }}"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="flex items-end size-h5">
|
||||
<div>RAM</div>
|
||||
<div class="color-highlight margin-left-auto text-very-compact">{{ printf "%.1f" .Info.Memory }} <span class="color-base">%</span></div>
|
||||
</div>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-value{{ if ge .Info.Memory 85.0 }} progress-value-notice{{ end }}" style="--percent: {{ .Info.Memory }}"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="flex items-end size-h5">
|
||||
<div>DISK</div>
|
||||
<div class="color-highlight margin-left-auto text-very-compact">{{ printf "%.1f" .Info.Disk }} <span class="color-base">%</span></div>
|
||||
</div>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-value{{ if ge .Info.Disk 85.0 }} progress-value-notice{{ end }}" style="--percent: {{ .Info.Disk }}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@ -0,0 +1,81 @@
|
||||
package glance
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
var beszelWidgetTemplate = mustParseTemplate("beszel.html", "widget-base.html")
|
||||
|
||||
type beszelWidget struct {
|
||||
widgetBase `yaml:",inline"`
|
||||
URL string `yaml:"url"`
|
||||
Token string `yaml:"token"`
|
||||
RedirectURL string `yaml:"redirect-url"`
|
||||
Systems []beszelSystem `yaml:"-"`
|
||||
}
|
||||
|
||||
type beszelResponse struct {
|
||||
Items []beszelSystem `json:"items"`
|
||||
}
|
||||
|
||||
type beszelSystem struct {
|
||||
Name string `json:"name"`
|
||||
Host string `json:"host"`
|
||||
Status string `json:"status"`
|
||||
Info beszelInfo `json:"info"`
|
||||
BootTime time.Time `json:"-"`
|
||||
}
|
||||
|
||||
type beszelInfo struct {
|
||||
Kernel string `json:"k"`
|
||||
Uptime float64 `json:"u"`
|
||||
CPUModel string `json:"m"`
|
||||
CPU float64 `json:"cpu"`
|
||||
Memory float64 `json:"mp"`
|
||||
Disk float64 `json:"dp"`
|
||||
Load1 float64 `json:"l1"`
|
||||
Load5 float64 `json:"l5"`
|
||||
Load15 float64 `json:"l15"`
|
||||
}
|
||||
|
||||
func (w *beszelWidget) initialize() error {
|
||||
w.withTitle("Beszel").withCacheDuration(10 * time.Second)
|
||||
if w.URL == "" {
|
||||
return errors.New("beszel widget: url is required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *beszelWidget) update(ctx context.Context) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", w.URL+"/api/collections/systems/records", nil)
|
||||
if err != nil {
|
||||
w.withError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if w.Token != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+w.Token)
|
||||
}
|
||||
|
||||
resp, err := decodeJsonFromRequest[*beszelResponse](defaultHTTPClient, req)
|
||||
if err != nil {
|
||||
w.withError(err)
|
||||
return
|
||||
}
|
||||
|
||||
w.Systems = resp.Items
|
||||
now := time.Now()
|
||||
for i := range w.Systems {
|
||||
w.Systems[i].BootTime = now.Add(-time.Duration(w.Systems[i].Info.Uptime) * time.Second)
|
||||
}
|
||||
|
||||
w.withError(nil).scheduleNextUpdate()
|
||||
}
|
||||
|
||||
func (w *beszelWidget) Render() template.HTML {
|
||||
return w.renderTemplate(w, beszelWidgetTemplate)
|
||||
}
|
||||
Loading…
Reference in New Issue