Compare commits
No commits in common. "main" and "v0.7.1" have entirely different histories.
19 changed files with 73 additions and 184 deletions
|
@ -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 |
|
||||
| ---- | ---- | -------- | ------- |
|
||||
| name | string | yes | |
|
||||
| title | 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` (when no value is specified)
|
||||
* default: `1600px`
|
||||
* 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://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://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).
|
||||
|
||||
### 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
|
||||
services:
|
||||
servies:
|
||||
immich-server:
|
||||
image: ghcr.io/immich-app/immich-server
|
||||
labels:
|
||||
|
@ -1852,29 +1852,11 @@ 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:
|
||||
|
||||
|
|
|
@ -66,6 +66,9 @@ 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.
Before Width: | Height: | Size: 1.3 MiB |
|
@ -53,16 +53,6 @@ theme:
|
|||
primary-color: 97 13 80
|
||||
```
|
||||
|
||||
### Gruvbox Dark
|
||||

|
||||
```yaml
|
||||
theme:
|
||||
background-color: 0 0 16
|
||||
primary-color: 43 59 81
|
||||
positive-color: 61 66 44
|
||||
negative-color: 6 96 59
|
||||
```
|
||||
|
||||
### Kanagawa Dark
|
||||

|
||||
```yaml
|
||||
|
|
|
@ -152,9 +152,9 @@ func newCustomIconField(value string) customIconField {
|
|||
}
|
||||
|
||||
if prefix == "di" {
|
||||
field.URL = "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/" + ext + "/" + basename + "." + ext
|
||||
field.URL = "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/" + ext + "/" + basename + "." + ext
|
||||
} else {
|
||||
field.URL = "https://cdn.jsdelivr.net/gh/selfhst/icons/" + ext + "/" + basename + "." + ext
|
||||
field.URL = "https://cdn.jsdelivr.net/gh/selfhst/icons@main/" + ext + "/" + basename + "." + ext
|
||||
}
|
||||
default:
|
||||
field.URL = value
|
||||
|
|
|
@ -242,7 +242,7 @@ func configFilesWatcher(
|
|||
// needed for lastContents and lastIncludes because they get updated in multiple goroutines
|
||||
mu := sync.Mutex{}
|
||||
|
||||
parseAndCompareBeforeCallback := func() {
|
||||
checkForContentChangesBeforeCallback := func() {
|
||||
currentContents, currentIncludes, err := parseYAMLIncludes(mainFilePath)
|
||||
if err != nil {
|
||||
onErr(fmt.Errorf("parsing main file contents for comparison: %w", err))
|
||||
|
@ -268,22 +268,15 @@ func configFilesWatcher(
|
|||
|
||||
const debounceDuration = 500 * time.Millisecond
|
||||
var debounceTimer *time.Timer
|
||||
debouncedParseAndCompareBeforeCallback := func() {
|
||||
debouncedCallback := func() {
|
||||
if debounceTimer != nil {
|
||||
debounceTimer.Stop()
|
||||
debounceTimer.Reset(debounceDuration)
|
||||
} else {
|
||||
debounceTimer = time.AfterFunc(debounceDuration, parseAndCompareBeforeCallback)
|
||||
debounceTimer = time.AfterFunc(debounceDuration, checkForContentChangesBeforeCallback)
|
||||
}
|
||||
}
|
||||
|
||||
deleteLastInclude := func(filePath string) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
fileAbsPath, _ := filepath.Abs(filePath)
|
||||
delete(lastIncludes, fileAbsPath)
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
|
@ -292,33 +285,16 @@ func configFilesWatcher(
|
|||
return
|
||||
}
|
||||
if event.Has(fsnotify.Write) {
|
||||
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
|
||||
|
||||
// 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()
|
||||
debouncedCallback()
|
||||
} else if event.Has(fsnotify.Remove) {
|
||||
deleteLastInclude(event.Name)
|
||||
debouncedParseAndCompareBeforeCallback()
|
||||
func() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
fileAbsPath, _ := filepath.Abs(event.Name)
|
||||
delete(lastIncludes, fileAbsPath)
|
||||
}()
|
||||
|
||||
debouncedCallback()
|
||||
}
|
||||
case err, isOpen := <-watcher.Errors:
|
||||
if !isOpen {
|
||||
|
|
|
@ -68,7 +68,7 @@ func newApplication(config *config) (*application, error) {
|
|||
|
||||
for w := range column.Widgets {
|
||||
widget := column.Widgets[w]
|
||||
app.widgetByID[widget.GetID()] = widget
|
||||
app.widgetByID[widget.id()] = widget
|
||||
|
||||
widget.setProviders(providers)
|
||||
}
|
||||
|
|
|
@ -284,9 +284,7 @@ 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) {
|
||||
|
@ -298,9 +296,7 @@ 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");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -674,7 +670,6 @@ 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]();
|
||||
|
|
|
@ -98,6 +98,7 @@ function showPopover() {
|
|||
}
|
||||
|
||||
contentElement.style.maxWidth = contentMaxWidth;
|
||||
containerElement.style.display = "block";
|
||||
activeTarget.classList.add("popover-active");
|
||||
document.addEventListener("keydown", handleHidePopoverOnEscape);
|
||||
window.addEventListener("resize", queueRepositionContainer);
|
||||
|
@ -105,8 +106,6 @@ function showPopover() {
|
|||
}
|
||||
|
||||
function repositionContainer() {
|
||||
containerElement.style.display = "block";
|
||||
|
||||
const targetBounds = activeTarget.dataset.popoverAnchor !== undefined
|
||||
? activeTarget.querySelector(activeTarget.dataset.popoverAnchor).getBoundingClientRect()
|
||||
: activeTarget.getBoundingClientRect();
|
||||
|
|
|
@ -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,19 +341,6 @@ 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;
|
||||
|
@ -576,7 +563,7 @@ kbd:active {
|
|||
}
|
||||
|
||||
.summary::after {
|
||||
content: "◀" / "";
|
||||
content: "◀";
|
||||
font-size: 1.2em;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
@ -835,7 +822,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;
|
||||
|
|
|
@ -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 gap-10">
|
||||
<li class="flex justify-between">
|
||||
<div class="text-truncate rtl">{{ .Domain }}</div>
|
||||
<div class="text-right" style="width: 4rem;"><span class="color-highlight">{{ .PercentBlocked }}</span>%</div>
|
||||
</li>
|
||||
|
|
|
@ -4,18 +4,17 @@
|
|||
|
||||
{{ define "widget-content" }}
|
||||
<div class="widget-group-header">
|
||||
<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 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>
|
||||
</div>
|
||||
|
||||
<div class="widget-group-contents">
|
||||
{{- 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>
|
||||
{{ range $i, $widget := .Widgets }}
|
||||
<div class="widget-group-content{{ if eq $i 0 }} widget-group-content-current{{ end }}">{{ .Render }}</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
{{ end }}
|
||||
|
|
|
@ -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 }}"{{ if eq .Slug $.Page.Slug }} aria-current="page"{{ end }}>{{ .Title }}</a>
|
||||
<a href="{{ $.App.Config.Server.BaseURL }}/{{ .Slug }}" class="nav-item{{ if eq .Slug $.Page.Slug }} nav-item-current{{ 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" 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">
|
||||
<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">
|
||||
{{ template "navigation-links" . }}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
@ -57,19 +57,17 @@
|
|||
</div>
|
||||
|
||||
<div class="content-bounds grow">
|
||||
<main class="page" id="page" aria-live="polite" aria-busy="true">
|
||||
<h1 class="visually-hidden">{{ .Page.Title }}</h1>
|
||||
<div class="page" id="page">
|
||||
<div class="page-content" id="page-content"></div>
|
||||
<div class="page-loading-container">
|
||||
<!-- TODO: add a bigger/better loading indicator -->
|
||||
<div class="visually-hidden">Loading</div>
|
||||
<div class="loading-icon" aria-hidden="true"></div>
|
||||
<div class="loading-icon"></div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ if not .App.Config.Branding.HideFooter }}
|
||||
<footer class="footer flex items-center flex-column">
|
||||
<div 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 }}
|
||||
|
@ -77,7 +75,7 @@
|
|||
{{ else }}
|
||||
{{ .App.Config.Branding.CustomFooter }}
|
||||
{{ end }}
|
||||
</footer>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
<div class="mobile-navigation-offset"></div>
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
{{- if not .HideHeader}}
|
||||
<div class="widget-header">
|
||||
{{- if ne "" .TitleURL }}
|
||||
<h2><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>
|
||||
{{- else }}
|
||||
<h2 class="uppercase">{{ .Title }}</h2>
|
||||
<div class="uppercase">{{ .Title }}</div>
|
||||
{{- end }}
|
||||
{{- if .IsWIP }}
|
||||
<div data-popover-type="html" data-popover-position="above">
|
||||
|
|
|
@ -241,7 +241,7 @@ func isDockerContainerHidden(container *dockerContainerJsonResponse, hideByDefau
|
|||
|
||||
func fetchAllDockerContainersFromSock(socketPath string) ([]dockerContainerJsonResponse, error) {
|
||||
client := &http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
Timeout: 3 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
|
||||
return net.Dial("unix", socketPath)
|
||||
|
|
|
@ -124,7 +124,6 @@ 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)
|
||||
}
|
||||
|
||||
|
@ -166,7 +165,8 @@ func fetchMarketsDataFromYahoo(marketRequests []marketRequest) (marketList, erro
|
|||
|
||||
points := svgPolylineCoordsFromYValues(100, 50, maybeCopySliceWithoutZeroValues(prices))
|
||||
|
||||
currency, exists := currencyToSymbol[strings.ToUpper(response.Chart.Result[0].Meta.Currency)]
|
||||
currency, exists := currencyToSymbol[response.Chart.Result[0].Meta.Currency]
|
||||
|
||||
if !exists {
|
||||
currency = response.Chart.Result[0].Meta.Currency
|
||||
}
|
||||
|
|
|
@ -8,11 +8,8 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand/v2"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -38,15 +35,8 @@ type requestDoer interface {
|
|||
Do(*http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
var userAgentPersistentVersion atomic.Int32
|
||||
|
||||
func setBrowserUserAgentHeader(request *http.Request) {
|
||||
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")
|
||||
request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0")
|
||||
}
|
||||
|
||||
func decodeJsonFromRequest[T any](client requestDoer, request *http.Request) (T, error) {
|
||||
|
|
|
@ -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) GetID() uint64 {
|
||||
func (w *widgetBase) id() uint64 {
|
||||
return w.ID
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ package sysinfo
|
|||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
@ -74,52 +73,24 @@ type MountpointInfo struct {
|
|||
}
|
||||
|
||||
type SystemInfoRequest struct {
|
||||
CPUTempSensor string `yaml:"cpu-temp-sensor"`
|
||||
HideMountpointsByDefault bool `yaml:"hide-mountpoints-by-default"`
|
||||
Mountpoints map[string]MointpointRequest `yaml:"mountpoints"`
|
||||
CPUTempSensor string `yaml:"cpu-temp-sensor"`
|
||||
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
|
||||
type cacheableHostInfo struct {
|
||||
var cachedHostInfo = 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 {
|
||||
|
@ -146,9 +117,13 @@ func Collect(req *SystemInfoRequest) (*SystemInfo, []error) {
|
|||
if cachedHostInfo.available {
|
||||
applyCachedHostInfo()
|
||||
} else {
|
||||
hostInfo, err := getHostInfo()
|
||||
hostInfo, err := host.Info()
|
||||
if err == nil {
|
||||
cachedHostInfo = hostInfo
|
||||
cachedHostInfo.available = true
|
||||
cachedHostInfo.bootTime = timestampJSON{time.Unix(int64(hostInfo.BootTime), 0)}
|
||||
cachedHostInfo.hostname = hostInfo.Hostname
|
||||
cachedHostInfo.platform = hostInfo.Platform
|
||||
|
||||
applyCachedHostInfo()
|
||||
} else {
|
||||
addErr(fmt.Errorf("getting host info: %v", err))
|
||||
|
@ -202,8 +177,7 @@ 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
|
||||
// also disabled on openbsd because it's not implemented by go-psutil
|
||||
if runtime.GOOS != "windows" && runtime.GOOS != "openbsd" {
|
||||
if runtime.GOOS != "windows" {
|
||||
sensorReadings, err := sensors.SensorsTemperatures()
|
||||
if err == nil {
|
||||
if req.CPUTempSensor != "" {
|
||||
|
@ -231,11 +205,7 @@ func Collect(req *SystemInfoRequest) (*SystemInfo, []error) {
|
|||
if err == nil {
|
||||
for _, fs := range filesystems {
|
||||
mpReq, ok := req.Mountpoints[fs.Mountpoint]
|
||||
isHidden := req.HideMountpointsByDefault
|
||||
if ok && mpReq.Hide != nil {
|
||||
isHidden = *mpReq.Hide
|
||||
}
|
||||
if isHidden {
|
||||
if ok && mpReq.Hide {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue