Compare commits

..

26 commits
v0.7.1 ... main

Author SHA1 Message Date
Svilen Markov
3b79c8e09f Remove symbol-link-template 2025-03-15 10:27:39 +00:00
Svilen Markov
d7bbf2b8e2 Accessibility improvements 2025-03-12 18:01:35 +00:00
Svilen Markov
b8df34309f Add note to docs about page width 2025-03-12 18:01:35 +00:00
Svilen Markov
278fa3c397 Add gap between domain and percent 2025-03-12 18:01:35 +00:00
Svilen Markov
6f48ee98e5 Uppercase currency before looking for matching symbol #365 2025-03-12 18:01:35 +00:00
Svilen Markov
774b0c104b
Merge pull request #399 from KadoBOT/patch-1
Update configuration.md
2025-03-08 09:46:37 +00:00
Ricardo Ambrogi
fbd4d9a74e
Update configuration.md
Correct `title` to `name`.

I'm unsure if `title` is also accepted, I haven't tested it. Please close this PR if this fix is not valid.
2025-03-07 09:07:10 +01:00
Svilen Markov
3f2fefe4f7
Merge pull request #382 from legoraft/main
Added gruvbox theme
2025-03-01 22:44:22 +00:00
Legoraft
fb8513bc9f
Added gruvbox theme code and screenshot 2025-03-01 14:19:36 +01:00
Legoraft
e4ec958edb
Added gruvbox screenshot 2025-03-01 14:17:17 +01:00
Svilen Markov
488a1f6070 Update link to point to repo instead of pkg.go.dev 2025-02-22 13:32:50 +00:00
Svilen Markov
1e12d937aa
Merge pull request #372 from DavisYe/main
Correct document
2025-02-22 12:58:56 +00:00
DavisYe
7319870289 Correct the docker compose environment format of GITHUB_TOKEN in the configuration document. 2025-02-20 21:30:34 +08:00
Svilen Markov
d4565acfe7 Markets widget rate limit fix 2025-02-19 02:25:07 +00:00
Svilen Markov
16129c53bd
Merge pull request #358 from rubiojr/rubiojr/reload-on-rename2
Auto-reload config file on RENAME
2025-02-19 01:25:08 +00:00
Svilen Markov
cbf1961510 Don't try to get sensor info on openbsd 2025-02-17 23:48:16 +00:00
Svilen Markov
c76a4d4be7 Increase docker containers widget timeout 2025-02-17 23:45:57 +00:00
Svilen Markov
27af0400c0 Tweak impl for handling config renames 2025-02-17 22:28:10 +00:00
Sergio Rubio
76a80ff034 Add clarifying comment 2025-02-17 19:17:49 +01:00
Sergio Rubio
f7f333ad52 Auto-reload config file on RENAME
Some editors (like Vim), create a temp file when saving, then replace
the file being edited with the temp file. This causes the FS notify
event to be RENAME, not WRITE, so auto-reload misses this.

In addition to that, the file is removed from the watcher and the
auto-reload functionality stops working entirely after the first RENAME.

https://github.com/fsnotify/fsnotify/issues/255 describes this.
2025-02-17 19:08:54 +01:00
Svilen Markov
0ce45e32aa Update dashboard icons repo 2025-02-15 14:59:20 +00:00
Svilen Markov
abeb11c8a6 Add hide-mountpoints-by-default prop for server-stats 2025-02-15 14:43:25 +00:00
Svilen Markov
e01af4adec Delay changing popover display
Previously would make the popover visible and then reposition
it on the next frame in order to avoid getting called recursively
due to the observer, however this causes the scrollbar to appear
if it wasn't already visible for a single frame which is janky.

This change fixes that.
2025-02-15 13:53:13 +00:00
Svilen Markov
3043a0bd15 Rework getting host info (#340) 2025-02-15 13:46:58 +00:00
Svilen Markov
232cab01f8 Fix typo 2025-02-10 23:15:12 +00:00
Svilen Markov
b301953249 Update docs 2025-02-10 20:38:50 +00:00
19 changed files with 183 additions and 72 deletions

View file

@ -97,9 +97,9 @@ Including config files from within your main config file is supported. This is d
```yaml
pages:
!include home.yml
!include videos.yml
!include homelab.yml
!include: home.yml
!include: videos.yml
!include: homelab.yml
```
The file you are including should not have any additional indentation, its values should be at the top level and the appropriate amount of indentation will be added automatically depending on where the file is included. Example:
@ -112,14 +112,14 @@ pages:
columns:
- size: full
widgets:
!include rss.yml
!include: rss.yml
- name: News
columns:
- size: full
widgets:
- type: group
widgets:
!include rss.yml
!include: rss.yml
- type: reddit
subreddit: news
```
@ -356,7 +356,7 @@ pages:
### Properties
| Name | Type | Required | Default |
| ---- | ---- | -------- | ------- |
| title | string | yes | |
| name | string | yes | |
| slug | string | no | |
| width | string | no | |
| center-vertically | boolean | no | false |
@ -374,7 +374,7 @@ The URL friendly version of the title which is used to access the page. For exam
#### `width`
The maximum width of the page on desktop. Possible values are `slim` and `wide`.
* default: `1600px`
* default: `1600px` (when no value is specified)
* slim: `1100px`
* wide: `1920px`
@ -1311,7 +1311,7 @@ headers:
When set to `true`, removes the border and padding around the widget.
##### `template`
The template that will be used to display the data. It relies on Go's `html/template` package so it's recommended to go through [its documentation](https://pkg.go.dev/text/template) to understand how to do basic things such as conditionals, loops, etc. In addition, it also uses [tidwall's gjson](https://pkg.go.dev/github.com/tidwall/gjson) package to parse the JSON data so it's worth going through its documentation if you want to use more advanced JSON selectors. You can view additional examples with explanations and function definitions [here](custom-api.md).
The template that will be used to display the data. It relies on Go's `html/template` package so it's recommended to go through [its documentation](https://pkg.go.dev/text/template) to understand how to do basic things such as conditionals, loops, etc. In addition, it also uses [tidwall's gjson](https://github.com/tidwall/gjson) package to parse the JSON data so it's worth going through its documentation if you want to use more advanced JSON selectors. You can view additional examples with explanations and function definitions [here](custom-api.md).
### Extension
Display a widget provided by an external source (3rd party). If you want to learn more about developing extensions, checkout the [extensions documentation](extensions.md) (WIP).
@ -1608,7 +1608,7 @@ services:
glance:
image: glanceapp/glance
environment:
- GITHUB_TOKEN: <your token>
- GITHUB_TOKEN=<your token>
```
and then use it in your `glance.yml` like this:
@ -1672,7 +1672,7 @@ For services with multiple containers you can specify a `glance.id` on the "main
<br>
```yaml
servies:
services:
immich-server:
image: ghcr.io/immich-app/immich-server
labels:
@ -1852,11 +1852,29 @@ Whether to hide the swap usage.
| Name | Type | Required | Default |
| ---- | ---- | -------- | ------- |
| cpu-temp-sensor | string | no | |
| hide-mointpoints-by-default | boolean | no | false |
| mountpoints | map\[string\]object | no | |
###### `cpu-temp-sensor`
The name of the sensor to use for the CPU temperature. When not provided the widget will attempt to find the correct one, if it fails to do so the temperature will not be displayed. To view the available sensors you can use `sensors` command.
###### `hide-mountpoints-by-default`
If set to `true` you'll have to manually make each mountpoint visible by adding a `hide: false` property to it like so:
```yaml
- type: server-stats
servers:
- type: local
hide-mountpoints-by-default: true
mountpoints:
"/":
hide: false
"/mnt/data":
hide: false
```
This is useful if you're running Glance inside of a container which usually mounts a lot of irrelevant filesystems.
###### `mountpoints`
A map of mountpoints to display disk usage for. The key is the path to the mountpoint and the value is an object with optional properties. Example:

View file

@ -66,9 +66,6 @@ pages:
# hide-location: true
- type: markets
# The link to go to when clicking on the symbol in the UI,
# {SYMBOL} will be substituded with the symbol for each market
symbol-link-template: https://www.tradingview.com/symbols/{SYMBOL}/news
markets:
- symbol: SPY
name: S&P 500

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View file

@ -53,6 +53,16 @@ theme:
primary-color: 97 13 80
```
### Gruvbox Dark
![screenshot](images/themes/gruvbox.png)
```yaml
theme:
background-color: 0 0 16
primary-color: 43 59 81
positive-color: 61 66 44
negative-color: 6 96 59
```
### Kanagawa Dark
![screenshot](images/themes/kanagawa-dark.png)
```yaml

View file

@ -152,9 +152,9 @@ func newCustomIconField(value string) customIconField {
}
if prefix == "di" {
field.URL = "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/" + ext + "/" + basename + "." + ext
field.URL = "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/" + ext + "/" + basename + "." + ext
} else {
field.URL = "https://cdn.jsdelivr.net/gh/selfhst/icons@main/" + ext + "/" + basename + "." + ext
field.URL = "https://cdn.jsdelivr.net/gh/selfhst/icons/" + ext + "/" + basename + "." + ext
}
default:
field.URL = value

View file

@ -242,7 +242,7 @@ func configFilesWatcher(
// needed for lastContents and lastIncludes because they get updated in multiple goroutines
mu := sync.Mutex{}
checkForContentChangesBeforeCallback := func() {
parseAndCompareBeforeCallback := func() {
currentContents, currentIncludes, err := parseYAMLIncludes(mainFilePath)
if err != nil {
onErr(fmt.Errorf("parsing main file contents for comparison: %w", err))
@ -268,15 +268,22 @@ func configFilesWatcher(
const debounceDuration = 500 * time.Millisecond
var debounceTimer *time.Timer
debouncedCallback := func() {
debouncedParseAndCompareBeforeCallback := func() {
if debounceTimer != nil {
debounceTimer.Stop()
debounceTimer.Reset(debounceDuration)
} else {
debounceTimer = time.AfterFunc(debounceDuration, checkForContentChangesBeforeCallback)
debounceTimer = time.AfterFunc(debounceDuration, parseAndCompareBeforeCallback)
}
}
deleteLastInclude := func(filePath string) {
mu.Lock()
defer mu.Unlock()
fileAbsPath, _ := filepath.Abs(filePath)
delete(lastIncludes, fileAbsPath)
}
go func() {
for {
select {
@ -285,16 +292,33 @@ func configFilesWatcher(
return
}
if event.Has(fsnotify.Write) {
debouncedCallback()
} else if event.Has(fsnotify.Remove) {
func() {
mu.Lock()
defer mu.Unlock()
fileAbsPath, _ := filepath.Abs(event.Name)
delete(lastIncludes, fileAbsPath)
}()
debouncedParseAndCompareBeforeCallback()
} else if event.Has(fsnotify.Rename) {
// on linux the file will no longer be watched after a rename, on windows
// it will continue to be watched with the new name but we have no access to
// the new name in this event in order to stop watching it manually and match the
// behavior in linux, may lead to weird unintended behaviors on windows as we're
// only handling renames from linux's perspective
// see https://github.com/fsnotify/fsnotify/issues/255
debouncedCallback()
// remove the old file from our manually tracked includes, calling
// debouncedParseAndCompareBeforeCallback will re-add it if it's still
// required after it triggers
deleteLastInclude(event.Name)
// wait for file to maybe get created again
// see https://github.com/glanceapp/glance/pull/358
for i := 0; i < 10; i++ {
if _, err := os.Stat(event.Name); err == nil {
break
}
time.Sleep(200 * time.Millisecond)
}
debouncedParseAndCompareBeforeCallback()
} else if event.Has(fsnotify.Remove) {
deleteLastInclude(event.Name)
debouncedParseAndCompareBeforeCallback()
}
case err, isOpen := <-watcher.Errors:
if !isOpen {

View file

@ -68,7 +68,7 @@ func newApplication(config *config) (*application, error) {
for w := range column.Widgets {
widget := column.Widgets[w]
app.widgetByID[widget.id()] = widget
app.widgetByID[widget.GetID()] = widget
widget.setProviders(providers)
}

View file

@ -284,7 +284,9 @@ function setupGroups() {
for (let i = 0; i < titles.length; i++) {
titles[i].classList.remove("widget-group-title-current");
titles[i].setAttribute("aria-selected", "false");
tabs[i].classList.remove("widget-group-content-current");
tabs[i].setAttribute("aria-hidden", "true");
}
if (current < t) {
@ -296,7 +298,9 @@ function setupGroups() {
current = t;
title.classList.add("widget-group-title-current");
title.setAttribute("aria-selected", "true");
tabs[t].classList.add("widget-group-content-current");
tabs[t].setAttribute("aria-hidden", "false");
});
}
}
@ -670,6 +674,7 @@ async function setupPage() {
setupLazyImages();
} finally {
pageElement.classList.add("content-ready");
pageElement.setAttribute("aria-busy", "false");
for (let i = 0; i < contentReadyCallbacks.length; i++) {
contentReadyCallbacks[i]();

View file

@ -98,7 +98,6 @@ function showPopover() {
}
contentElement.style.maxWidth = contentMaxWidth;
containerElement.style.display = "block";
activeTarget.classList.add("popover-active");
document.addEventListener("keydown", handleHidePopoverOnEscape);
window.addEventListener("resize", queueRepositionContainer);
@ -106,6 +105,8 @@ function showPopover() {
}
function repositionContainer() {
containerElement.style.display = "block";
const targetBounds = activeTarget.dataset.popoverAnchor !== undefined
? activeTarget.querySelector(activeTarget.dataset.popoverAnchor).getBoundingClientRect()
: activeTarget.getBoundingClientRect();

View file

@ -110,7 +110,7 @@
.visited-indicator:not(.text-truncate)::after,
.visited-indicator.text-truncate::before,
.bookmarks-link:not(.bookmarks-link-no-arrow)::after {
content: '↗';
content: '↗' / "";
margin-left: 0.5em;
display: inline-block;
position: relative;
@ -189,7 +189,7 @@
}
.expand-toggle-button-icon::before {
content: '';
content: '' / "";
font-size: 0.8rem;
transform: rotate(90deg);
line-height: 1;
@ -341,6 +341,19 @@ html, body, .body-content {
height: 100%;
}
h1, h2, h3, h4, h5 {
font: inherit;
}
.visually-hidden {
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}
a {
text-decoration: none;
color: inherit;
@ -563,7 +576,7 @@ kbd:active {
}
.summary::after {
content: "◀";
content: "◀" / "";
font-size: 1.2em;
position: absolute;
top: 0;
@ -822,7 +835,7 @@ details[open] .summary::after {
}
.list-horizontal-text > *:not(:last-child)::after {
content: '•';
content: '•' / "";
color: var(--color-text-subdue);
margin: 0 0.4rem;
position: relative;

View file

@ -76,7 +76,7 @@
<summary class="summary">Top blocked domains</summary>
<ul class="list list-gap-4 list-with-transition size-h5">
{{ range .Stats.TopBlockedDomains }}
<li class="flex justify-between">
<li class="flex justify-between gap-10">
<div class="text-truncate rtl">{{ .Domain }}</div>
<div class="text-right" style="width: 4rem;"><span class="color-highlight">{{ .PercentBlocked }}</span>%</div>
</li>

View file

@ -4,17 +4,18 @@
{{ define "widget-content" }}
<div class="widget-group-header">
<div class="widget-header gap-20">
{{ range $i, $widget := .Widgets }}
<button class="widget-group-title{{ if eq $i 0 }} widget-group-title-current{{ end }}"{{ if ne "" .TitleURL }} data-title-url="{{ .TitleURL }}"{{ end }}>{{ $widget.Title }}</button>
{{ end }}
<div class="widget-header gap-20" role="tablist">
{{- range $i, $widget := .Widgets }}
<button class="widget-group-title{{ if eq $i 0 }} widget-group-title-current{{ end }}"{{ if ne "" .TitleURL }} data-title-url="{{ .TitleURL }}"{{ end }} aria-selected="{{ if eq $i 0 }}true{{ else }}false{{ end }}" arial-level="2" role="tab" aria-controls="widget-{{ .GetID }}-tabpanel-{{ $i }}" id="widget-{{ .GetID }}-tab-{{ $i }}">{{ $widget.Title }}</button>
{{- end }}
</div>
</div>
<div class="widget-group-contents">
{{ range $i, $widget := .Widgets }}
<div class="widget-group-content{{ if eq $i 0 }} widget-group-content-current{{ end }}">{{ .Render }}</div>
{{ end }}
{{- range $i, $widget := .Widgets }}
<div class="widget-group-content{{ if eq $i 0 }} widget-group-content-current{{ end }}" id="widget-{{ .GetID }}-tabpanel-{{ $i }}" role="tabpanel" aria-labelledby="widget-{{ .GetID }}-tab-{{ $i }}" aria-hidden="{{ if eq $i 0 }}false{{ else }}true{{ end }}">
{{- .Render -}}
</div>
{{- end }}
</div>
{{ end }}

View file

@ -25,7 +25,7 @@
{{ define "navigation-links" }}
{{ range .App.Config.Pages }}
<a href="{{ $.App.Config.Server.BaseURL }}/{{ .Slug }}" class="nav-item{{ if eq .Slug $.Page.Slug }} nav-item-current{{ end }}">{{ .Title }}</a>
<a href="{{ $.App.Config.Server.BaseURL }}/{{ .Slug }}" class="nav-item{{ if eq .Slug $.Page.Slug }} nav-item-current{{ end }}"{{ if eq .Slug $.Page.Slug }} aria-current="page"{{ end }}>{{ .Title }}</a>
{{ end }}
{{ end }}
@ -35,10 +35,10 @@
<div class="header-container content-bounds">
<div class="header flex padding-inline-widget widget-content-frame">
<!-- TODO: Replace G with actual logo, first need an actual logo -->
<div class="logo">{{ if ne "" .App.Config.Branding.LogoURL }}<img src="{{ .App.Config.Branding.LogoURL }}" alt="">{{ else if ne "" .App.Config.Branding.LogoText }}{{ .App.Config.Branding.LogoText }}{{ else }}G{{ end }}</div>
<div class="nav flex grow">
<div class="logo" aria-hidden="true">{{ if ne "" .App.Config.Branding.LogoURL }}<img src="{{ .App.Config.Branding.LogoURL }}" alt="">{{ else if ne "" .App.Config.Branding.LogoText }}{{ .App.Config.Branding.LogoText }}{{ else }}G{{ end }}</div>
<nav class="nav flex grow">
{{ template "navigation-links" . }}
</div>
</nav>
</div>
</div>
{{ end }}
@ -57,17 +57,19 @@
</div>
<div class="content-bounds grow">
<div class="page" id="page">
<main class="page" id="page" aria-live="polite" aria-busy="true">
<h1 class="visually-hidden">{{ .Page.Title }}</h1>
<div class="page-content" id="page-content"></div>
<div class="page-loading-container">
<!-- TODO: add a bigger/better loading indicator -->
<div class="loading-icon"></div>
<div class="visually-hidden">Loading</div>
<div class="loading-icon" aria-hidden="true"></div>
</div>
</div>
</main>
</div>
{{ if not .App.Config.Branding.HideFooter }}
<div class="footer flex items-center flex-column">
<footer class="footer flex items-center flex-column">
{{ if eq "" .App.Config.Branding.CustomFooter }}
<div>
<a class="size-h3" href="https://github.com/glanceapp/glance" target="_blank" rel="noreferrer">Glance</a> {{ if ne "dev" .App.Version }}<a class="visited-indicator" title="Release notes" href="https://github.com/glanceapp/glance/releases/tag/{{ .App.Version }}" target="_blank" rel="noreferrer">{{ .App.Version }}</a>{{ else }}({{ .App.Version }}){{ end }}
@ -75,7 +77,7 @@
{{ else }}
{{ .App.Config.Branding.CustomFooter }}
{{ end }}
</div>
</footer>
{{ end }}
<div class="mobile-navigation-offset"></div>

View file

@ -2,9 +2,9 @@
{{- if not .HideHeader}}
<div class="widget-header">
{{- if ne "" .TitleURL }}
<a href="{{ .TitleURL | safeURL }}" target="_blank" rel="noreferrer" class="uppercase">{{ .Title }}</a>
<h2><a href="{{ .TitleURL | safeURL }}" target="_blank" rel="noreferrer" class="uppercase">{{ .Title }}</a></h2>
{{- else }}
<div class="uppercase">{{ .Title }}</div>
<h2 class="uppercase">{{ .Title }}</h2>
{{- end }}
{{- if .IsWIP }}
<div data-popover-type="html" data-popover-position="above">

View file

@ -241,7 +241,7 @@ func isDockerContainerHidden(container *dockerContainerJsonResponse, hideByDefau
func fetchAllDockerContainersFromSock(socketPath string) ([]dockerContainerJsonResponse, error) {
client := &http.Client{
Timeout: 3 * time.Second,
Timeout: 5 * time.Second,
Transport: &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return net.Dial("unix", socketPath)

View file

@ -124,6 +124,7 @@ func fetchMarketsDataFromYahoo(marketRequests []marketRequest) (marketList, erro
for i := range marketRequests {
request, _ := http.NewRequest("GET", fmt.Sprintf("https://query1.finance.yahoo.com/v8/finance/chart/%s?range=1mo&interval=1d", marketRequests[i].Symbol), nil)
setBrowserUserAgentHeader(request)
requests = append(requests, request)
}
@ -165,8 +166,7 @@ func fetchMarketsDataFromYahoo(marketRequests []marketRequest) (marketList, erro
points := svgPolylineCoordsFromYValues(100, 50, maybeCopySliceWithoutZeroValues(prices))
currency, exists := currencyToSymbol[response.Chart.Result[0].Meta.Currency]
currency, exists := currencyToSymbol[strings.ToUpper(response.Chart.Result[0].Meta.Currency)]
if !exists {
currency = response.Chart.Result[0].Meta.Currency
}

View file

@ -8,8 +8,11 @@ import (
"errors"
"fmt"
"io"
"math/rand/v2"
"net/http"
"strconv"
"sync"
"sync/atomic"
"time"
)
@ -35,8 +38,15 @@ type requestDoer interface {
Do(*http.Request) (*http.Response, error)
}
var userAgentPersistentVersion atomic.Int32
func setBrowserUserAgentHeader(request *http.Request) {
request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0")
if rand.IntN(2000) == 0 {
userAgentPersistentVersion.Store(rand.Int32N(5))
}
version := strconv.Itoa(130 + int(userAgentPersistentVersion.Load()))
request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:"+version+".0) Gecko/20100101 Firefox/"+version+".0")
}
func decodeJsonFromRequest[T any](client requestDoer, request *http.Request) (T, error) {

View file

@ -121,13 +121,13 @@ type widget interface {
// These need to be exported because they get called in templates
Render() template.HTML
GetType() string
GetID() uint64
initialize() error
requiresUpdate(*time.Time) bool
setProviders(*widgetProviders)
update(context.Context)
setID(uint64)
id() uint64
handleRequest(w http.ResponseWriter, r *http.Request)
setHideHeader(bool)
}
@ -184,7 +184,7 @@ func (w *widgetBase) update(ctx context.Context) {
}
func (w *widgetBase) id() uint64 {
func (w *widgetBase) GetID() uint64 {
return w.ID
}

View file

@ -3,6 +3,7 @@ package sysinfo
import (
"fmt"
"math"
"os"
"runtime"
"sort"
"strconv"
@ -73,24 +74,52 @@ type MountpointInfo struct {
}
type SystemInfoRequest struct {
CPUTempSensor string `yaml:"cpu-temp-sensor"`
Mountpoints map[string]MointpointRequest `yaml:"mountpoints"`
CPUTempSensor string `yaml:"cpu-temp-sensor"`
HideMountpointsByDefault bool `yaml:"hide-mountpoints-by-default"`
Mountpoints map[string]MointpointRequest `yaml:"mountpoints"`
}
type MointpointRequest struct {
Name string `yaml:"name"`
Hide bool `yaml:"hide"`
Hide *bool `yaml:"hide"`
}
// Currently caches hostname indefinitely which isn't ideal
// Potential issue with caching boot time as it may not initially get reported correctly:
// https://github.com/shirou/gopsutil/issues/842#issuecomment-1908972344
var cachedHostInfo = struct {
type cacheableHostInfo struct {
available bool
hostname string
platform string
bootTime timestampJSON
}{}
}
var cachedHostInfo cacheableHostInfo
func getHostInfo() (cacheableHostInfo, error) {
var err error
info := cacheableHostInfo{}
info.hostname, err = os.Hostname()
if err != nil {
return info, err
}
info.platform, _, _, err = host.PlatformInformation()
if err != nil {
return info, err
}
bootTime, err := host.BootTime()
if err != nil {
return info, err
}
info.bootTime = timestampJSON{time.Unix(int64(bootTime), 0)}
info.available = true
return info, nil
}
func Collect(req *SystemInfoRequest) (*SystemInfo, []error) {
if req == nil {
@ -117,13 +146,9 @@ func Collect(req *SystemInfoRequest) (*SystemInfo, []error) {
if cachedHostInfo.available {
applyCachedHostInfo()
} else {
hostInfo, err := host.Info()
hostInfo, err := getHostInfo()
if err == nil {
cachedHostInfo.available = true
cachedHostInfo.bootTime = timestampJSON{time.Unix(int64(hostInfo.BootTime), 0)}
cachedHostInfo.hostname = hostInfo.Hostname
cachedHostInfo.platform = hostInfo.Platform
cachedHostInfo = hostInfo
applyCachedHostInfo()
} else {
addErr(fmt.Errorf("getting host info: %v", err))
@ -177,7 +202,8 @@ func Collect(req *SystemInfoRequest) (*SystemInfo, []error) {
// keeps returning a single sensor with key "ACPI\\ThermalZone\\TZ00_0" which
// doesn't seem to be the CPU sensor or correspond to anything useful when
// compared against the temperatures Libre Hardware Monitor reports
if runtime.GOOS != "windows" {
// also disabled on openbsd because it's not implemented by go-psutil
if runtime.GOOS != "windows" && runtime.GOOS != "openbsd" {
sensorReadings, err := sensors.SensorsTemperatures()
if err == nil {
if req.CPUTempSensor != "" {
@ -205,7 +231,11 @@ func Collect(req *SystemInfoRequest) (*SystemInfo, []error) {
if err == nil {
for _, fs := range filesystems {
mpReq, ok := req.Mountpoints[fs.Mountpoint]
if ok && mpReq.Hide {
isHidden := req.HideMountpointsByDefault
if ok && mpReq.Hide != nil {
isHidden = *mpReq.Hide
}
if isHidden {
continue
}