diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 42813b1a..55058fe2 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -784,12 +784,22 @@ func TestForceRefreshInterval(t *testing.T) { t.Fatalf(`Parsing failure: %v`, err) } - expected := 42 + expected := 42 * time.Second result := opts.ForceRefreshInterval() if result != expected { t.Fatalf(`Unexpected FORCE_REFRESH_INTERVAL value, got %v instead of %v`, result, expected) } + + sorted := opts.SortedOptions(false) + i := slices.IndexFunc(sorted, func(opt *option) bool { + return opt.Key == "FORCE_REFRESH_INTERVAL" + }) + + expectedSerialized := 42 + if got := sorted[i].Value; got != expectedSerialized { + t.Fatalf(`Unexpected value in option output, got %q instead of %q`, got, expectedSerialized) + } } func TestDefaultBatchSizeValue(t *testing.T) { diff --git a/internal/config/options.go b/internal/config/options.go index 626d0000..32b1b466 100644 --- a/internal/config/options.go +++ b/internal/config/options.go @@ -29,7 +29,7 @@ const ( defaultBasePath = "" defaultWorkerPoolSize = 16 defaultPollingFrequency = 60 - defaultForceRefreshInterval = 30 + defaultForceRefreshInterval = 30 * time.Second defaultBatchSize = 100 defaultPollingScheduler = "round_robin" defaultSchedulerEntryFrequencyMinInterval = 5 * time.Minute @@ -130,7 +130,7 @@ type options struct { cleanupArchiveUnreadDays int cleanupArchiveBatchSize int cleanupRemoveSessionsDays int - forceRefreshInterval int + forceRefreshInterval time.Duration batchSize int schedulerEntryFrequencyMinInterval time.Duration schedulerEntryFrequencyMaxInterval time.Duration @@ -392,7 +392,7 @@ func (o *options) WorkerPoolSize() int { } // ForceRefreshInterval returns the force refresh interval -func (o *options) ForceRefreshInterval() int { +func (o *options) ForceRefreshInterval() time.Duration { return o.forceRefreshInterval } @@ -769,7 +769,7 @@ func (o *options) SortedOptions(redactSecret bool) []*option { "OAUTH2_REDIRECT_URL": o.oauth2RedirectURL, "OAUTH2_USER_CREATION": o.oauth2UserCreationAllowed, "DISABLE_LOCAL_AUTH": o.disableLocalAuth, - "FORCE_REFRESH_INTERVAL": o.forceRefreshInterval, + "FORCE_REFRESH_INTERVAL": int(o.forceRefreshInterval.Seconds()), "POLLING_FREQUENCY": o.pollingFrequency, "POLLING_LIMIT_PER_HOST": o.pollingLimitPerHost, "POLLING_PARSING_ERROR_LIMIT": o.pollingParsingErrorLimit, diff --git a/internal/config/parser.go b/internal/config/parser.go index 3f913ff5..d3ffd602 100644 --- a/internal/config/parser.go +++ b/internal/config/parser.go @@ -139,7 +139,7 @@ func (p *parser) parseLines(lines []string) (err error) { case "WORKER_POOL_SIZE": p.opts.workerPoolSize = parseInt(value, defaultWorkerPoolSize) case "FORCE_REFRESH_INTERVAL": - p.opts.forceRefreshInterval = parseInt(value, defaultForceRefreshInterval) + p.opts.forceRefreshInterval = parseInterval(value, time.Second, defaultForceRefreshInterval) case "BATCH_SIZE": p.opts.batchSize = parseInt(value, defaultBatchSize) case "POLLING_FREQUENCY": diff --git a/internal/http/request/context.go b/internal/http/request/context.go index 236881e7..1094bcde 100644 --- a/internal/http/request/context.go +++ b/internal/http/request/context.go @@ -6,6 +6,7 @@ package request // import "miniflux.app/v2/internal/http/request" import ( "net/http" "strconv" + "time" "miniflux.app/v2/internal/model" ) @@ -135,13 +136,13 @@ func FlashErrorMessage(r *http.Request) string { } // LastForceRefresh returns the last force refresh timestamp. -func LastForceRefresh(r *http.Request) int64 { +func LastForceRefresh(r *http.Request) time.Time { jsonStringValue := getContextStringValue(r, LastForceRefreshContextKey) timestamp, err := strconv.ParseInt(jsonStringValue, 10, 64) if err != nil { - return 0 + return time.Time{} } - return timestamp + return time.Unix(timestamp, 0) } // ClientIP returns the client IP address stored in the context. diff --git a/internal/ui/category_refresh.go b/internal/ui/category_refresh.go index c0016784..6f2983a6 100644 --- a/internal/ui/category_refresh.go +++ b/internal/ui/category_refresh.go @@ -32,9 +32,9 @@ func (h *handler) refreshCategory(w http.ResponseWriter, r *http.Request) int64 sess := session.New(h.store, request.SessionID(r)) // Avoid accidental and excessive refreshes. - if time.Now().UTC().Unix()-request.LastForceRefresh(r) < int64(config.Opts.ForceRefreshInterval())*60 { - time := config.Opts.ForceRefreshInterval() - sess.NewFlashErrorMessage(printer.Plural("alert.too_many_feeds_refresh", time, time)) + if time.Since(request.LastForceRefresh(r)) < config.Opts.ForceRefreshInterval() { + seconds := int(config.Opts.ForceRefreshInterval().Seconds()) + sess.NewFlashErrorMessage(printer.Plural("alert.too_many_feeds_refresh", seconds, seconds)) } else { userID := request.UserID(r) // We allow the end-user to force refresh all its feeds in this category diff --git a/internal/ui/feed_refresh.go b/internal/ui/feed_refresh.go index ca8f2c1a..99a110cd 100644 --- a/internal/ui/feed_refresh.go +++ b/internal/ui/feed_refresh.go @@ -37,9 +37,9 @@ func (h *handler) refreshAllFeeds(w http.ResponseWriter, r *http.Request) { sess := session.New(h.store, request.SessionID(r)) // Avoid accidental and excessive refreshes. - if time.Now().UTC().Unix()-request.LastForceRefresh(r) < int64(config.Opts.ForceRefreshInterval())*60 { - time := config.Opts.ForceRefreshInterval() - sess.NewFlashErrorMessage(printer.Plural("alert.too_many_feeds_refresh", time, time)) + if time.Since(request.LastForceRefresh(r)) < config.Opts.ForceRefreshInterval() { + seconds := int(config.Opts.ForceRefreshInterval().Seconds()) + sess.NewFlashErrorMessage(printer.Plural("alert.too_many_feeds_refresh", seconds, seconds)) } else { userID := request.UserID(r) // We allow the end-user to force refresh all its feeds