Compare commits

...
20 Commits
Author SHA1 Message Date
Svilen Markov ea04aec0b4 Add target property to search widget
Create release / release (push) Has been cancelled
2025-04-22 23:07:07 +01:00
Svilen Markov b8e2717faf Add parseLocalTime function 2025-04-22 22:40:55 +01:00
Svilen Markov 502950709c Update sensors warning check 2025-04-15 04:08:05 +01:00
anxdpanic 2c3e14d84c [sensors] allow sensor readings when there are warnings 2025-04-15 04:07:50 +01:00
Svilen Markov 8e71afe158 Escape proxied request URL 2025-04-15 04:02:23 +01:00
Svilen Markov 03b616622e Fix bug when making single request in job
Create release / release (push) Has been cancelled
2025-04-14 01:07:04 +01:00
Svilen Markov 50e1da01fe Add newline 2025-04-13 17:40:30 +01:00
Svilen Markov f8f50b26d8 Add mountpoint:info CLI command
Create release / release (push) Has been cancelled
2025-04-13 17:09:51 +01:00
Svilen Markov a3bc133bcb Maybe fix for missing mountpoint info 2025-04-13 16:52:17 +01:00
Svilen Markov 50dae22ff4 Add restart: unless-stopped 2025-04-13 16:15:59 +01:00
Svilen Markov 5a91154eab Document function 2025-04-13 16:08:04 +01:00
Ralph Ocdol d5b89d512a added unique to filter arrays with unique items in custom-api 2025-04-13 16:07:54 +01:00
Svilen Markov 97f43f88eb Only set title if attribute isn't set 2025-04-11 23:14:06 +01:00
Svilen Markov 3177af9524 Add rounded class 2025-04-11 22:34:53 +01:00
Svilen Markov f99d22738c Add replaceMatches 2025-04-11 20:37:45 +01:00
Ralph Ocdol ea7124db52 feat: custom-api's replaceAll with regex support 2025-04-11 20:37:36 +01:00
Lucas L. b1247e5de6 Add filter on already seen links for RSS feeds 2025-04-11 20:34:47 +01:00
Svilen Markov 5ab962634e Fix sorting bug in twitch channels widget 2025-04-11 20:34:03 +01:00
Svilen Markov d99944dcff Avoid spinning up unnecessary goroutines for single data jobs 2025-04-11 20:33:24 +01:00
Svilen Markov 920bcb06dc Print warnings when failing to get sensors 2025-04-11 19:34:49 +01:00
15 changed files with 156 additions and 45 deletions
+1
View File
@@ -195,6 +195,7 @@ services:
glance: glance:
container_name: glance container_name: glance
image: glanceapp/glance image: glanceapp/glance
restart: unless-stopped
volumes: volumes:
- ./config:/app/config - ./config:/app/config
ports: ports:
+4
View File
@@ -958,6 +958,7 @@ Preview:
| search-engine | string | no | duckduckgo | | search-engine | string | no | duckduckgo |
| new-tab | boolean | no | false | | new-tab | boolean | no | false |
| autofocus | boolean | no | false | | autofocus | boolean | no | false |
| target | string | no | _blank |
| placeholder | string | no | Type here to search… | | placeholder | string | no | Type here to search… |
| bangs | array | no | | | bangs | array | no | |
@@ -975,6 +976,9 @@ When set to `true`, swaps the shortcuts for showing results in the same or new t
##### `autofocus` ##### `autofocus`
When set to `true`, automatically focuses the search input on page load. When set to `true`, automatically focuses the search input on page load.
##### `target`
The target to use when opening the search results in a new tab. Possible values are `_blank`, `_self`, `_parent` and `_top`.
##### `placeholder` ##### `placeholder`
When set, modifies the text displayed in the input field before typing. When set, modifies the text displayed in the input field before typing.
+3
View File
@@ -378,6 +378,7 @@ The following helper functions provided by Glance are available:
- `offsetNow(offset string) time.Time`: Returns the current time with an offset. The offset can be positive or negative and must be in the format "3h" "-1h" or "2h30m10s". - `offsetNow(offset string) time.Time`: Returns the current time with an offset. The offset can be positive or negative and must be in the format "3h" "-1h" or "2h30m10s".
- `duration(str string) time.Duration`: Parses a string such as `1h`, `24h`, `5h30m`, etc into a `time.Duration`. - `duration(str string) time.Duration`: Parses a string such as `1h`, `24h`, `5h30m`, etc into a `time.Duration`.
- `parseTime(layout string, s string) time.Time`: Parses a string into time.Time. The layout must be provided in Go's [date format](https://pkg.go.dev/time#pkg-constants). You can alternatively use these values instead of the literal format: "unix", "RFC3339", "RFC3339Nano", "DateTime", "DateOnly". - `parseTime(layout string, s string) time.Time`: Parses a string into time.Time. The layout must be provided in Go's [date format](https://pkg.go.dev/time#pkg-constants). You can alternatively use these values instead of the literal format: "unix", "RFC3339", "RFC3339Nano", "DateTime", "DateOnly".
- `parseLocalTime(layout string, s string) time.Time`: Same as the above, except in the absence of a timezone, it will use the local timezone instead of UTC.
- `parseRelativeTime(layout string, s string) time.Time`: A shorthand for `{{ .String "date" | parseTime "rfc3339" | toRelativeTime }}`. - `parseRelativeTime(layout string, s string) time.Time`: A shorthand for `{{ .String "date" | parseTime "rfc3339" | toRelativeTime }}`.
- `add(a, b float) float`: Adds two numbers. - `add(a, b float) float`: Adds two numbers.
- `sub(a, b float) float`: Subtracts two numbers. - `sub(a, b float) float`: Subtracts two numbers.
@@ -389,6 +390,7 @@ The following helper functions provided by Glance are available:
- `trimSuffix(suffix string, str string) string`: Trims the suffix from a string. - `trimSuffix(suffix string, str string) string`: Trims the suffix from a string.
- `trimSpace(str string) string`: Trims whitespace from a string on both ends. - `trimSpace(str string) string`: Trims whitespace from a string on both ends.
- `replaceAll(old string, new string, str string) string`: Replaces all occurrences of a string in a string. - `replaceAll(old string, new string, str string) string`: Replaces all occurrences of a string in a string.
- `replaceMatches(pattern string, replacement string, str string) string`: Replaces all occurrences of a regular expression in a string.
- `findMatch(pattern string, str string) string`: Finds the first match of a regular expression in a string. - `findMatch(pattern string, str string) string`: Finds the first match of a regular expression in a string.
- `findSubmatch(pattern string, str string) string`: Finds the first submatch of a regular expression in a string. - `findSubmatch(pattern string, str string) string`: Finds the first submatch of a regular expression in a string.
- `sortByString(key string, order string, arr []JSON): []JSON`: Sorts an array of JSON objects by a string key in either ascending or descending order. - `sortByString(key string, order string, arr []JSON): []JSON`: Sorts an array of JSON objects by a string key in either ascending or descending order.
@@ -396,6 +398,7 @@ The following helper functions provided by Glance are available:
- `sortByFloat(key string, order string, arr []JSON): []JSON`: Sorts an array of JSON objects by a float key in either ascending or descending order. - `sortByFloat(key string, order string, arr []JSON): []JSON`: Sorts an array of JSON objects by a float key in either ascending or descending order.
- `sortByTime(key string, layout string, order string, arr []JSON): []JSON`: Sorts an array of JSON objects by a time key in either ascending or descending order. The format must be provided in Go's [date format](https://pkg.go.dev/time#pkg-constants). - `sortByTime(key string, layout string, order string, arr []JSON): []JSON`: Sorts an array of JSON objects by a time key in either ascending or descending order. The format must be provided in Go's [date format](https://pkg.go.dev/time#pkg-constants).
- `concat(strings ...string) string`: Concatenates multiple strings together. - `concat(strings ...string) string`: Concatenates multiple strings together.
- `unique(key string, arr []JSON) []JSON`: Returns a unique array of JSON objects based on the given key.
The following helper functions provided by Go's `text/template` are available: The following helper functions provided by Go's `text/template` are available:
+43 -1
View File
@@ -6,6 +6,7 @@ import (
"os" "os"
"strings" "strings"
"github.com/shirou/gopsutil/v4/disk"
"github.com/shirou/gopsutil/v4/sensors" "github.com/shirou/gopsutil/v4/sensors"
) )
@@ -18,11 +19,13 @@ const (
cliIntentConfigPrint cliIntentConfigPrint
cliIntentDiagnose cliIntentDiagnose
cliIntentSensorsPrint cliIntentSensorsPrint
cliIntentMountpointInfo
) )
type cliOptions struct { type cliOptions struct {
intent cliIntent intent cliIntent
configPath string configPath string
args []string
} }
func parseCliOptions() (*cliOptions, error) { func parseCliOptions() (*cliOptions, error) {
@@ -46,6 +49,7 @@ func parseCliOptions() (*cliOptions, error) {
fmt.Println(" config:validate Validate the config file") fmt.Println(" config:validate Validate the config file")
fmt.Println(" config:print Print the parsed config file with embedded includes") fmt.Println(" config:print Print the parsed config file with embedded includes")
fmt.Println(" sensors:print List all sensors") fmt.Println(" sensors:print List all sensors")
fmt.Println(" mountpoint:info Print information about a given mountpoint path")
fmt.Println(" diagnose Run diagnostic checks") fmt.Println(" diagnose Run diagnostic checks")
} }
configPath := flags.String("config", "glance.yml", "Set config path") configPath := flags.String("config", "glance.yml", "Set config path")
@@ -72,6 +76,12 @@ func parseCliOptions() (*cliOptions, error) {
} else { } else {
return nil, unknownCommandErr return nil, unknownCommandErr
} }
} else if len(args) == 2 {
if args[0] == "mountpoint:info" {
intent = cliIntentMountpointInfo
} else {
return nil, unknownCommandErr
}
} else { } else {
return nil, unknownCommandErr return nil, unknownCommandErr
} }
@@ -79,13 +89,24 @@ func parseCliOptions() (*cliOptions, error) {
return &cliOptions{ return &cliOptions{
intent: intent, intent: intent,
configPath: *configPath, configPath: *configPath,
args: args,
}, nil }, nil
} }
func cliSensorsPrint() int { func cliSensorsPrint() int {
tempSensors, err := sensors.SensorsTemperatures() tempSensors, err := sensors.SensorsTemperatures()
if err != nil { if err != nil {
fmt.Printf("Failed to retrieve list of sensors: %v\n", err) if warns, ok := err.(*sensors.Warnings); ok {
fmt.Printf("Could not retrieve information for some sensors (%v):\n", err)
for _, w := range warns.List {
fmt.Printf(" - %v\n", w)
}
fmt.Println()
} else {
fmt.Printf("Failed to retrieve sensor information: %v\n", err)
return 1
}
return 1 return 1
} }
@@ -94,9 +115,30 @@ func cliSensorsPrint() int {
return 0 return 0
} }
fmt.Println("Sensors found:")
for _, sensor := range tempSensors { for _, sensor := range tempSensors {
fmt.Printf(" %s: %.1f°C\n", sensor.SensorKey, sensor.Temperature) fmt.Printf(" %s: %.1f°C\n", sensor.SensorKey, sensor.Temperature)
} }
return 0 return 0
} }
func cliMountpointInfo(requestedPath string) int {
usage, err := disk.Usage(requestedPath)
if err != nil {
fmt.Printf("Failed to retrieve info for path %s: %v\n", requestedPath, err)
if warns, ok := err.(*disk.Warnings); ok {
for _, w := range warns.List {
fmt.Printf(" - %v\n", w)
}
}
return 1
}
fmt.Println("Path:", usage.Path)
fmt.Println("FS type:", ternary(usage.Fstype == "", "unknown", usage.Fstype))
fmt.Printf("Used percent: %.1f%%\n", usage.UsedPercent)
return 0
}
+2
View File
@@ -51,6 +51,8 @@ func Main() int {
fmt.Println(string(contents)) fmt.Println(string(contents))
case cliIntentSensorsPrint: case cliIntentSensorsPrint:
return cliSensorsPrint() return cliSensorsPrint()
case cliIntentMountpointInfo:
return cliMountpointInfo(options.args[1])
case cliIntentDiagnose: case cliIntentDiagnose:
runDiagnostic() runDiagnostic()
} }
+3 -2
View File
@@ -104,6 +104,7 @@ function setupSearchBoxes() {
for (let i = 0; i < searchWidgets.length; i++) { for (let i = 0; i < searchWidgets.length; i++) {
const widget = searchWidgets[i]; const widget = searchWidgets[i];
const defaultSearchUrl = widget.dataset.defaultSearchUrl; const defaultSearchUrl = widget.dataset.defaultSearchUrl;
const target = widget.dataset.target || "_blank";
const newTab = widget.dataset.newTab === "true"; const newTab = widget.dataset.newTab === "true";
const inputElement = widget.getElementsByClassName("search-input")[0]; const inputElement = widget.getElementsByClassName("search-input")[0];
const bangElement = widget.getElementsByClassName("search-bang")[0]; const bangElement = widget.getElementsByClassName("search-bang")[0];
@@ -143,7 +144,7 @@ function setupSearchBoxes() {
const url = searchUrlTemplate.replace("!QUERY!", encodeURIComponent(query)); const url = searchUrlTemplate.replace("!QUERY!", encodeURIComponent(query));
if (newTab && !event.ctrlKey || !newTab && event.ctrlKey) { if (newTab && !event.ctrlKey || !newTab && event.ctrlKey) {
window.open(url, '_blank').focus(); window.open(url, target).focus();
} else { } else {
window.location.href = url; window.location.href = url;
} }
@@ -649,7 +650,7 @@ function setupTruncatedElementTitles() {
for (let i = 0; i < elements.length; i++) { for (let i = 0; i < elements.length; i++) {
const element = elements[i]; const element = elements[i];
if (element.title === "") element.title = element.textContent; if (element.getAttribute("title") === null) element.title = element.textContent;
} }
} }
+1
View File
@@ -2038,6 +2038,7 @@ details[open] .summary::after {
.color-primary { color: var(--color-primary); } .color-primary { color: var(--color-primary); }
.cursor-help { cursor: help; } .cursor-help { cursor: help; }
.rounded { border-radius: var(--border-radius); }
.break-all { word-break: break-all; } .break-all { word-break: break-all; }
.text-left { text-align: left; } .text-left { text-align: left; }
.text-right { text-align: right; } .text-right { text-align: right; }
+1 -1
View File
@@ -3,7 +3,7 @@
{{ define "widget-content-classes" }}widget-content-frameless{{ end }} {{ define "widget-content-classes" }}widget-content-frameless{{ end }}
{{ define "widget-content" }} {{ define "widget-content" }}
<div class="search widget-content-frame padding-inline-widget flex gap-15 items-center" data-default-search-url="{{ .SearchEngine }}" data-new-tab="{{ .NewTab }}"> <div class="search widget-content-frame padding-inline-widget flex gap-15 items-center" data-default-search-url="{{ .SearchEngine }}" data-new-tab="{{ .NewTab }}" data-target="{{ .Target }}">
<div class="search-bangs"> <div class="search-bangs">
{{ range .Bangs }} {{ range .Bangs }}
<input type="hidden" data-shortcut="{{ .Shortcut }}" data-title="{{ .Title }}" data-url="{{ .URL }}"> <input type="hidden" data-shortcut="{{ .Shortcut }}" data-title="{{ .Title }}" data-url="{{ .URL }}">
+30 -6
View File
@@ -450,11 +450,16 @@ var customAPITemplateFuncs = func() template.FuncMap {
return d return d
}, },
"parseTime": customAPIFuncParseTime, "parseTime": func(layout, value string) time.Time {
return customAPIFuncParseTimeInLocation(layout, value, time.UTC)
},
"parseLocalTime": func(layout, value string) time.Time {
return customAPIFuncParseTimeInLocation(layout, value, time.Local)
},
"toRelativeTime": dynamicRelativeTimeAttrs, "toRelativeTime": dynamicRelativeTimeAttrs,
"parseRelativeTime": func(layout, value string) template.HTMLAttr { "parseRelativeTime": func(layout, value string) template.HTMLAttr {
// Shorthand to do both of the above with a single function call // Shorthand to do both of the above with a single function call
return dynamicRelativeTimeAttrs(customAPIFuncParseTime(layout, value)) return dynamicRelativeTimeAttrs(customAPIFuncParseTimeInLocation(layout, value, time.UTC))
}, },
// The reason we flip the parameter order is so that you can chain multiple calls together like this: // The reason we flip the parameter order is so that you can chain multiple calls together like this:
// {{ .JSON.String "foo" | trimPrefix "bar" | doSomethingElse }} // {{ .JSON.String "foo" | trimPrefix "bar" | doSomethingElse }}
@@ -471,6 +476,13 @@ var customAPITemplateFuncs = func() template.FuncMap {
"replaceAll": func(old, new, s string) string { "replaceAll": func(old, new, s string) string {
return strings.ReplaceAll(s, old, new) return strings.ReplaceAll(s, old, new)
}, },
"replaceMatches": func(pattern, replacement, s string) string {
if s == "" {
return ""
}
return getCachedRegexp(pattern).ReplaceAllString(s, replacement)
},
"findMatch": func(pattern, s string) string { "findMatch": func(pattern, s string) string {
if s == "" { if s == "" {
return "" return ""
@@ -521,8 +533,8 @@ var customAPITemplateFuncs = func() template.FuncMap {
}, },
"sortByTime": func(key, layout, order string, results []decoratedGJSONResult) []decoratedGJSONResult { "sortByTime": func(key, layout, order string, results []decoratedGJSONResult) []decoratedGJSONResult {
sort.Slice(results, func(a, b int) bool { sort.Slice(results, func(a, b int) bool {
timeA := customAPIFuncParseTime(layout, results[a].String(key)) timeA := customAPIFuncParseTimeInLocation(layout, results[a].String(key), time.UTC)
timeB := customAPIFuncParseTime(layout, results[b].String(key)) timeB := customAPIFuncParseTimeInLocation(layout, results[b].String(key), time.UTC)
if order == "asc" { if order == "asc" {
return timeA.Before(timeB) return timeA.Before(timeB)
@@ -536,6 +548,18 @@ var customAPITemplateFuncs = func() template.FuncMap {
"concat": func(items ...string) string { "concat": func(items ...string) string {
return strings.Join(items, "") return strings.Join(items, "")
}, },
"unique": func(key string, results []decoratedGJSONResult) []decoratedGJSONResult {
seen := make(map[string]struct{})
out := make([]decoratedGJSONResult, 0, len(results))
for _, result := range results {
val := result.String(key)
if _, ok := seen[val]; !ok {
seen[val] = struct{}{}
out = append(out, result)
}
}
return out
},
} }
for key, value := range globalTemplateFunctions { for key, value := range globalTemplateFunctions {
@@ -547,7 +571,7 @@ var customAPITemplateFuncs = func() template.FuncMap {
return funcs return funcs
}() }()
func customAPIFuncParseTime(layout, value string) time.Time { func customAPIFuncParseTimeInLocation(layout, value string, loc *time.Location) time.Time {
switch strings.ToLower(layout) { switch strings.ToLower(layout) {
case "unix": case "unix":
asInt, err := strconv.ParseInt(value, 10, 64) asInt, err := strconv.ParseInt(value, 10, 64)
@@ -566,7 +590,7 @@ func customAPIFuncParseTime(layout, value string) time.Time {
layout = time.DateOnly layout = time.DateOnly
} }
parsed, err := time.Parse(layout, value) parsed, err := time.ParseInLocation(layout, value, loc)
if err != nil { if err != nil {
return time.Unix(0, 0) return time.Unix(0, 0)
} }
+1 -1
View File
@@ -194,7 +194,7 @@ func fetchSubredditPosts(
var client requestDoer = defaultHTTPClient var client requestDoer = defaultHTTPClient
if requestUrlTemplate != "" { if requestUrlTemplate != "" {
requestUrl = strings.ReplaceAll(requestUrlTemplate, "{REQUEST-URL}", requestUrl) requestUrl = strings.ReplaceAll(requestUrlTemplate, "{REQUEST-URL}", url.QueryEscape(requestUrl))
} else if proxyClient != nil { } else if proxyClient != nil {
client = proxyClient client = proxyClient
} }
+8 -1
View File
@@ -331,6 +331,7 @@ func fetchItemsFromRSSFeeds(requests []rssFeedRequest) (rssFeedItemList, error)
failed := 0 failed := 0
entries := make(rssFeedItemList, 0, len(feeds)*10) entries := make(rssFeedItemList, 0, len(feeds)*10)
seen := make(map[string]struct{})
for i := range feeds { for i := range feeds {
if errs[i] != nil { if errs[i] != nil {
@@ -339,7 +340,13 @@ func fetchItemsFromRSSFeeds(requests []rssFeedRequest) (rssFeedItemList, error)
continue continue
} }
entries = append(entries, feeds[i]...) for _, item := range feeds[i] {
if _, exists := seen[item.Link]; exists {
continue
}
entries = append(entries, item)
seen[item.Link] = struct{}{}
}
} }
if failed == len(requests) { if failed == len(requests) {
+1
View File
@@ -20,6 +20,7 @@ type searchWidget struct {
SearchEngine string `yaml:"search-engine"` SearchEngine string `yaml:"search-engine"`
Bangs []SearchBang `yaml:"bangs"` Bangs []SearchBang `yaml:"bangs"`
NewTab bool `yaml:"new-tab"` NewTab bool `yaml:"new-tab"`
Target string `yaml:"target"`
Autofocus bool `yaml:"autofocus"` Autofocus bool `yaml:"autofocus"`
Placeholder string `yaml:"placeholder"` Placeholder string `yaml:"placeholder"`
} }
@@ -196,6 +196,10 @@ func fetchChannelFromTwitchTask(channel string) (twitchChannel, error) {
slog.Warn("Failed to parse Twitch stream started at", "error", err, "started_at", streamMetadata.UserOrNull.Stream.StartedAt) slog.Warn("Failed to parse Twitch stream started at", "error", err, "started_at", streamMetadata.UserOrNull.Stream.StartedAt)
} }
} }
} else {
// This prevents live channels with 0 viewers from being
// incorrectly sorted lower than offline channels
result.ViewersCount = -1
} }
return result, nil return result, nil
+5
View File
@@ -181,6 +181,11 @@ func workerPoolDo[I any, O any](job *workerPoolJob[I, O]) ([]O, []error, error)
return results, errs, nil return results, errs, nil
} }
if len(job.data) == 1 {
results[0], errs[0] = job.task(job.data[0])
return results, errs, nil
}
tasksQueue := make(chan *workerPoolTask[I, O]) tasksQueue := make(chan *workerPoolTask[I, O])
resultsQueue := make(chan *workerPoolTask[I, O]) resultsQueue := make(chan *workerPoolTask[I, O])
+29 -13
View File
@@ -201,11 +201,12 @@ func Collect(req *SystemInfoRequest) (*SystemInfo, []error) {
// currently disabled on Windows because it requires elevated privilidges, otherwise // currently disabled on Windows because it requires elevated privilidges, otherwise
// keeps returning a single sensor with key "ACPI\\ThermalZone\\TZ00_0" which // 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 // doesn't seem to be the CPU sensor or correspond to anything useful when
// compared against the temperatures Libre Hardware Monitor reports // compared against the temperatures Libre Hardware Monitor reports.
// also disabled on openbsd because it's not implemented by go-psutil // Also disabled on the bsd's because it's not implemented by go-psutil for them
if runtime.GOOS != "windows" && runtime.GOOS != "openbsd" { if runtime.GOOS != "windows" && runtime.GOOS != "openbsd" && runtime.GOOS != "netbsd" && runtime.GOOS != "freebsd" {
sensorReadings, err := sensors.SensorsTemperatures() sensorReadings, err := sensors.SensorsTemperatures()
if err == nil { _, errIsWarning := err.(*sensors.Warnings)
if err == nil || errIsWarning {
if req.CPUTempSensor != "" { if req.CPUTempSensor != "" {
for i := range sensorReadings { for i := range sensorReadings {
if sensorReadings[i].SensorKey == req.CPUTempSensor { if sensorReadings[i].SensorKey == req.CPUTempSensor {
@@ -227,22 +228,24 @@ func Collect(req *SystemInfoRequest) (*SystemInfo, []error) {
} }
} }
filesystems, err := disk.Partitions(false) addedMountpoints := map[string]struct{}{}
if err == nil { addMountpointInfo := func(requestedPath string, mpReq MointpointRequest) {
for _, fs := range filesystems { if _, exists := addedMountpoints[requestedPath]; exists {
mpReq, ok := req.Mountpoints[fs.Mountpoint] return
}
isHidden := req.HideMountpointsByDefault isHidden := req.HideMountpointsByDefault
if ok && mpReq.Hide != nil { if mpReq.Hide != nil {
isHidden = *mpReq.Hide isHidden = *mpReq.Hide
} }
if isHidden { if isHidden {
continue return
} }
usage, err := disk.Usage(fs.Mountpoint) usage, err := disk.Usage(requestedPath)
if err == nil { if err == nil {
mpInfo := MountpointInfo{ mpInfo := MountpointInfo{
Path: fs.Mountpoint, Path: requestedPath,
Name: mpReq.Name, Name: mpReq.Name,
TotalMB: usage.Total / 1024 / 1024, TotalMB: usage.Total / 1024 / 1024,
UsedMB: usage.Used / 1024 / 1024, UsedMB: usage.Used / 1024 / 1024,
@@ -250,13 +253,26 @@ func Collect(req *SystemInfoRequest) (*SystemInfo, []error) {
} }
info.Mountpoints = append(info.Mountpoints, mpInfo) info.Mountpoints = append(info.Mountpoints, mpInfo)
addedMountpoints[requestedPath] = struct{}{}
} else { } else {
addErr(fmt.Errorf("getting filesystem usage for %s: %v", fs.Mountpoint, err)) addErr(fmt.Errorf("getting filesystem usage for %s: %v", requestedPath, err))
} }
} }
if !req.HideMountpointsByDefault {
filesystems, err := disk.Partitions(false)
if err == nil {
for _, fs := range filesystems {
addMountpointInfo(fs.Mountpoint, req.Mountpoints[fs.Mountpoint])
}
} else { } else {
addErr(fmt.Errorf("getting filesystems: %v", err)) addErr(fmt.Errorf("getting filesystems: %v", err))
} }
}
for mountpoint, mpReq := range req.Mountpoints {
addMountpointInfo(mountpoint, mpReq)
}
sort.Slice(info.Mountpoints, func(a, b int) bool { sort.Slice(info.Mountpoints, func(a, b int) bool {
return info.Mountpoints[a].UsedPercent > info.Mountpoints[b].UsedPercent return info.Mountpoints[a].UsedPercent > info.Mountpoints[b].UsedPercent