From 610c44c64c82c8f503b8e275b1511d5acd22e7ab Mon Sep 17 00:00:00 2001 From: selfhst-bot Date: Mon, 9 Mar 2026 20:04:57 -0400 Subject: [PATCH] v4.0.1 (CACHE_TTL fix) --- CHANGELOG.md | 7 +++++++ build/VERSION | 2 +- build/main.go | 31 +++++++++++++++++++++++++++++-- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bf3b5e5..6d41b572 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# v4.0.1 + +## What's Changed + +* Replaced hard-coded cache age values with the `CACHE_TTL` variable added in v4.0.0 ([#748](https://github.com/selfhst/icons/issues/748)) +* Updated background routine to periodically purge stale assets based on `CACHE_TTL` time + # v4.0.0 This release initially set out to address a request to support icon extensions in URLs, but quickly grew into a number of under the hood optimization/security fixes and a new ```hybrid``` mode that allows users with local collections to set remote icons as a fallback. diff --git a/build/VERSION b/build/VERSION index 0c89fc92..cc868b62 100755 --- a/build/VERSION +++ b/build/VERSION @@ -1 +1 @@ -4.0.0 \ No newline at end of file +4.0.1 \ No newline at end of file diff --git a/build/main.go b/build/main.go index 8f4fc7e8..8c32ae1a 100755 --- a/build/main.go +++ b/build/main.go @@ -79,6 +79,16 @@ func (c *Cache) Get(key string) (string, bool) { return item.Content, true } +func (c *Cache) cleanup() { + c.mutex.Lock() + defer c.mutex.Unlock() + for k, v := range c.items { + if time.Since(v.Timestamp) > c.ttl { + delete(c.items, k) + } + } +} + func (c *Cache) Set(key, value string) { c.mutex.Lock() defer c.mutex.Unlock() @@ -432,7 +442,7 @@ func handleIcon(w http.ResponseWriter, r *http.Request) { return } w.Header().Set("Content-Type", contentType) - w.Header().Set("Cache-Control", "public, max-age=3600") + w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", int(config.CacheTTL.Seconds()))) w.Header().Set("ETag", etag) w.Header().Set("X-Cache", "HIT") serveContent(w, r, contentType, cached) @@ -525,7 +535,7 @@ func handleIcon(w http.ResponseWriter, r *http.Request) { return } w.Header().Set("Content-Type", contentType) - w.Header().Set("Cache-Control", "public, max-age=3600") + w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", int(config.CacheTTL.Seconds()))) w.Header().Set("ETag", etag) w.Header().Set("X-Cache", "MISS") serveContent(w, r, contentType, iconContent) @@ -578,6 +588,7 @@ func handleCustomIcon(w http.ResponseWriter, r *http.Request) { if cached, found := cache.Get(cacheKey); found { logf(logLevelDebug, "[CACHE] Serving cached custom icon: \"%s\" %v", filename, formatDuration(time.Since(start))) w.Header().Set("Content-Type", contentType) + w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", int(config.CacheTTL.Seconds()))) w.Header().Set("ETag", etag) w.Header().Set("X-Cache", "HIT") serveContent(w, r, contentType, cached) @@ -596,6 +607,7 @@ func handleCustomIcon(w http.ResponseWriter, r *http.Request) { logf(logLevelInfo, "[SUCCESS] Serving custom icon: \"%s\" (%s) %v", filename, contentType, formatDuration(time.Since(start))) w.Header().Set("Content-Type", contentType) + w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", int(config.CacheTTL.Seconds()))) w.Header().Set("ETag", etag) w.Header().Set("X-Cache", "MISS") serveContent(w, r, contentType, string(data)) @@ -660,6 +672,20 @@ func main() { quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) + cleanupCtx, cleanupCancel := context.WithCancel(context.Background()) + go func() { + ticker := time.NewTicker(config.CacheTTL) + defer ticker.Stop() + for { + select { + case <-ticker.C: + cache.cleanup() + case <-cleanupCtx.Done(): + return + } + } + }() + go func() { if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("Server error: %v", err) @@ -667,6 +693,7 @@ func main() { }() <-quit + cleanupCancel() log.Println("Shutting down...") ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)