From 50e1da01fef1b11df166fbf91c6ab49eff77bcf7 Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Sun, 13 Apr 2025 17:40:30 +0100 Subject: [PATCH 01/12] Add newline --- internal/glance/cli.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/glance/cli.go b/internal/glance/cli.go index 693aa1fe..f5a16fb9 100644 --- a/internal/glance/cli.go +++ b/internal/glance/cli.go @@ -133,7 +133,7 @@ func cliMountpointInfo(requestedPath string) int { fmt.Println("Path:", usage.Path) fmt.Println("FS type:", ternary(usage.Fstype == "", "unknown", usage.Fstype)) - fmt.Printf("Used percent: %.1f%%", usage.UsedPercent) + fmt.Printf("Used percent: %.1f%%\n", usage.UsedPercent) return 0 } From 03b616622e7f511e40c7360d72e5ea4efe38f626 Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Mon, 14 Apr 2025 01:07:04 +0100 Subject: [PATCH 02/12] Fix bug when making single request in job --- internal/glance/widget-utils.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/glance/widget-utils.go b/internal/glance/widget-utils.go index 4aaf5d52..9489cbfd 100644 --- a/internal/glance/widget-utils.go +++ b/internal/glance/widget-utils.go @@ -182,8 +182,8 @@ func workerPoolDo[I any, O any](job *workerPoolJob[I, O]) ([]O, []error, error) } if len(job.data) == 1 { - output, err := job.task(job.data[0]) - return append(results, output), append(errs, err), nil + results[0], errs[0] = job.task(job.data[0]) + return results, errs, nil } tasksQueue := make(chan *workerPoolTask[I, O]) From 8e71afe158defedfc6d31b95eafd9d0536d17d1b Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Tue, 15 Apr 2025 04:02:23 +0100 Subject: [PATCH 03/12] Escape proxied request URL --- internal/glance/widget-reddit.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/glance/widget-reddit.go b/internal/glance/widget-reddit.go index e7109fa8..86832b5d 100644 --- a/internal/glance/widget-reddit.go +++ b/internal/glance/widget-reddit.go @@ -194,7 +194,7 @@ func fetchSubredditPosts( var client requestDoer = defaultHTTPClient if requestUrlTemplate != "" { - requestUrl = strings.ReplaceAll(requestUrlTemplate, "{REQUEST-URL}", requestUrl) + requestUrl = strings.ReplaceAll(requestUrlTemplate, "{REQUEST-URL}", url.QueryEscape(requestUrl)) } else if proxyClient != nil { client = proxyClient } From 2c3e14d84c76b41213566ca141416322ed025cda Mon Sep 17 00:00:00 2001 From: anxdpanic Date: Sun, 13 Apr 2025 20:32:36 -0400 Subject: [PATCH 04/12] [sensors] allow sensor readings when there are warnings --- internal/glance/cli.go | 7 ++++--- pkg/sysinfo/sysinfo.go | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/internal/glance/cli.go b/internal/glance/cli.go index f5a16fb9..102052ee 100644 --- a/internal/glance/cli.go +++ b/internal/glance/cli.go @@ -96,7 +96,7 @@ func parseCliOptions() (*cliOptions, error) { func cliSensorsPrint() int { tempSensors, err := sensors.SensorsTemperatures() if err != nil { - fmt.Printf("Failed to retrieve list of sensors: %v\n", err) + fmt.Printf("Errors encountered while retrieving list of sensors:\n %v\n", err) if warns, ok := err.(*sensors.Warnings); ok { for _, w := range warns.List { fmt.Printf(" - %v\n", w) @@ -106,13 +106,14 @@ func cliSensorsPrint() int { return 1 } - if len(tempSensors) == 0 { + if tempSensors == nil || len(tempSensors) == 0 { fmt.Println("No sensors found") return 0 } + fmt.Println("Sensors found:") 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 diff --git a/pkg/sysinfo/sysinfo.go b/pkg/sysinfo/sysinfo.go index 1ed8c75a..9a35710a 100644 --- a/pkg/sysinfo/sysinfo.go +++ b/pkg/sysinfo/sysinfo.go @@ -205,7 +205,7 @@ func Collect(req *SystemInfoRequest) (*SystemInfo, []error) { // 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 sensorReadings != nil && len(sensorReadings) > 0 { if req.CPUTempSensor != "" { for i := range sensorReadings { if sensorReadings[i].SensorKey == req.CPUTempSensor { From 502950709cfdf31a1bd25930b8da67fe614f6674 Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Tue, 15 Apr 2025 03:37:38 +0100 Subject: [PATCH 05/12] Update sensors warning check --- internal/glance/cli.go | 8 ++++++-- pkg/sysinfo/sysinfo.go | 9 +++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/internal/glance/cli.go b/internal/glance/cli.go index 102052ee..5d78fe1d 100644 --- a/internal/glance/cli.go +++ b/internal/glance/cli.go @@ -96,17 +96,21 @@ func parseCliOptions() (*cliOptions, error) { func cliSensorsPrint() int { tempSensors, err := sensors.SensorsTemperatures() if err != nil { - fmt.Printf("Errors encountered while retrieving list of sensors:\n %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 } - if tempSensors == nil || len(tempSensors) == 0 { + if len(tempSensors) == 0 { fmt.Println("No sensors found") return 0 } diff --git a/pkg/sysinfo/sysinfo.go b/pkg/sysinfo/sysinfo.go index 9a35710a..ed203188 100644 --- a/pkg/sysinfo/sysinfo.go +++ b/pkg/sysinfo/sysinfo.go @@ -201,11 +201,12 @@ func Collect(req *SystemInfoRequest) (*SystemInfo, []error) { // currently disabled on Windows because it requires elevated privilidges, otherwise // 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" { + // compared against the temperatures Libre Hardware Monitor reports. + // Also disabled on the bsd's because it's not implemented by go-psutil for them + if runtime.GOOS != "windows" && runtime.GOOS != "openbsd" && runtime.GOOS != "netbsd" && runtime.GOOS != "freebsd" { sensorReadings, err := sensors.SensorsTemperatures() - if sensorReadings != nil && len(sensorReadings) > 0 { + _, errIsWarning := err.(*sensors.Warnings) + if err == nil || errIsWarning { if req.CPUTempSensor != "" { for i := range sensorReadings { if sensorReadings[i].SensorKey == req.CPUTempSensor { From f771fc3577d0b361295266536805fec737288b9f Mon Sep 17 00:00:00 2001 From: vishalkadam47 Date: Sun, 20 Apr 2025 20:26:28 +0530 Subject: [PATCH 06/12] feat: enhance asset path handling to include config directory and improve assets directory validation --- internal/glance/config.go | 28 ++++++++++++++++++++++++++-- internal/glance/glance.go | 3 ++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/internal/glance/config.go b/internal/glance/config.go index a63afff8..f48582f2 100644 --- a/internal/glance/config.go +++ b/internal/glance/config.go @@ -430,8 +430,32 @@ func isConfigStateValid(config *config) error { } if config.Server.AssetsPath != "" { - if _, err := os.Stat(config.Server.AssetsPath); os.IsNotExist(err) { - return fmt.Errorf("assets directory does not exist: %s", config.Server.AssetsPath) + if _, err := os.Stat(config.Server.AssetsPath); err == nil { + return nil + } + + workingDir, err := os.Getwd() + if err != nil { + return fmt.Errorf("directory does not exist: %v", err) + } + + // Try these paths in order: + possiblePaths := []string{ + config.Server.AssetsPath, // Original path + filepath.Join(workingDir, "config"), // Local config directory + filepath.Join(workingDir, config.Server.AssetsPath), // Relative to working dir + "/app/config", // Docker path + } + + for _, path := range possiblePaths { + if _, err := os.Stat(path); err == nil { + config.Server.AssetsPath = path + return nil + } + } + + if !strings.HasPrefix(config.Server.AssetsPath, "/app/") { + return fmt.Errorf("assets directory not found in any of the expected locations. Tried: %v", possiblePaths) } } diff --git a/internal/glance/glance.go b/internal/glance/glance.go index ab635361..8f9cda99 100644 --- a/internal/glance/glance.go +++ b/internal/glance/glance.go @@ -127,7 +127,7 @@ func (p *page) updateOutdatedWidgets() { } func (a *application) transformUserDefinedAssetPath(path string) string { - if strings.HasPrefix(path, "/assets/") { + if strings.HasPrefix(path, "/assets/") || strings.HasPrefix(path, "/config/") { return a.Config.Server.BaseURL + path } @@ -262,6 +262,7 @@ func (a *application) server() (func() error, func() error) { absAssetsPath, _ = filepath.Abs(a.Config.Server.AssetsPath) assetsFS := fileServerWithCache(http.Dir(a.Config.Server.AssetsPath), 2*time.Hour) mux.Handle("/assets/{path...}", http.StripPrefix("/assets/", assetsFS)) + mux.Handle("/config/{path...}", http.StripPrefix("/config/", assetsFS)) } server := http.Server{ From c04eae2d5533f1f4f4505d419017f95ba7752e48 Mon Sep 17 00:00:00 2001 From: vishalkadam47 Date: Sun, 20 Apr 2025 20:45:29 +0530 Subject: [PATCH 07/12] add config directory to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2cd84fc0..0b9ecea6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /assets +/config /build /playground /.idea From b8e2717fafa8131c3dc55095d8d26ec6d4482cc4 Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Tue, 22 Apr 2025 22:40:55 +0100 Subject: [PATCH 08/12] Add parseLocalTime function --- docs/custom-api.md | 1 + internal/glance/widget-custom-api.go | 17 +++++++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/custom-api.md b/docs/custom-api.md index d42d1fc5..3a37df26 100644 --- a/docs/custom-api.md +++ b/docs/custom-api.md @@ -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". - `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". +- `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 }}`. - `add(a, b float) float`: Adds two numbers. - `sub(a, b float) float`: Subtracts two numbers. diff --git a/internal/glance/widget-custom-api.go b/internal/glance/widget-custom-api.go index 93d51c0f..d5a1b673 100644 --- a/internal/glance/widget-custom-api.go +++ b/internal/glance/widget-custom-api.go @@ -450,11 +450,16 @@ var customAPITemplateFuncs = func() template.FuncMap { 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, "parseRelativeTime": func(layout, value string) template.HTMLAttr { // 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: // {{ .JSON.String "foo" | trimPrefix "bar" | doSomethingElse }} @@ -528,8 +533,8 @@ var customAPITemplateFuncs = func() template.FuncMap { }, "sortByTime": func(key, layout, order string, results []decoratedGJSONResult) []decoratedGJSONResult { sort.Slice(results, func(a, b int) bool { - timeA := customAPIFuncParseTime(layout, results[a].String(key)) - timeB := customAPIFuncParseTime(layout, results[b].String(key)) + timeA := customAPIFuncParseTimeInLocation(layout, results[a].String(key), time.UTC) + timeB := customAPIFuncParseTimeInLocation(layout, results[b].String(key), time.UTC) if order == "asc" { return timeA.Before(timeB) @@ -566,7 +571,7 @@ var customAPITemplateFuncs = func() template.FuncMap { return funcs }() -func customAPIFuncParseTime(layout, value string) time.Time { +func customAPIFuncParseTimeInLocation(layout, value string, loc *time.Location) time.Time { switch strings.ToLower(layout) { case "unix": asInt, err := strconv.ParseInt(value, 10, 64) @@ -585,7 +590,7 @@ func customAPIFuncParseTime(layout, value string) time.Time { layout = time.DateOnly } - parsed, err := time.Parse(layout, value) + parsed, err := time.ParseInLocation(layout, value, loc) if err != nil { return time.Unix(0, 0) } From ea04aec0b4efafe965ee8083dc5b525013367f88 Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Tue, 22 Apr 2025 23:07:07 +0100 Subject: [PATCH 09/12] Add target property to search widget --- docs/configuration.md | 4 ++++ internal/glance/static/js/main.js | 3 ++- internal/glance/templates/search.html | 2 +- internal/glance/widget-search.go | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 3999d8cd..b8121f03 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -958,6 +958,7 @@ Preview: | search-engine | string | no | duckduckgo | | new-tab | boolean | no | false | | autofocus | boolean | no | false | +| target | string | no | _blank | | placeholder | string | no | Type here to search… | | bangs | array | no | | @@ -975,6 +976,9 @@ When set to `true`, swaps the shortcuts for showing results in the same or new t ##### `autofocus` 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` When set, modifies the text displayed in the input field before typing. diff --git a/internal/glance/static/js/main.js b/internal/glance/static/js/main.js index 41d2ae3a..dca713bb 100644 --- a/internal/glance/static/js/main.js +++ b/internal/glance/static/js/main.js @@ -104,6 +104,7 @@ function setupSearchBoxes() { for (let i = 0; i < searchWidgets.length; i++) { const widget = searchWidgets[i]; const defaultSearchUrl = widget.dataset.defaultSearchUrl; + const target = widget.dataset.target || "_blank"; const newTab = widget.dataset.newTab === "true"; const inputElement = widget.getElementsByClassName("search-input")[0]; const bangElement = widget.getElementsByClassName("search-bang")[0]; @@ -143,7 +144,7 @@ function setupSearchBoxes() { const url = searchUrlTemplate.replace("!QUERY!", encodeURIComponent(query)); if (newTab && !event.ctrlKey || !newTab && event.ctrlKey) { - window.open(url, '_blank').focus(); + window.open(url, target).focus(); } else { window.location.href = url; } diff --git a/internal/glance/templates/search.html b/internal/glance/templates/search.html index 6e8fc43d..ae981c63 100644 --- a/internal/glance/templates/search.html +++ b/internal/glance/templates/search.html @@ -3,7 +3,7 @@ {{ define "widget-content-classes" }}widget-content-frameless{{ end }} {{ define "widget-content" }} -