diff --git a/AGENTS.md b/AGENTS.md index f5e6876c7..3439f908f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -194,6 +194,7 @@ Note: Across our public documentation, official images, and in production, the c - Go: run `make fmt-go swag-fmt` to reformat the backend code + Swagger annotations (see `Makefile` for additional targets) - Doc comments for packages and exported identifiers must be complete sentences that begin with the name of the thing being described and end with a period. + - All newly added functions, including unexported helpers, must have a concise doc comment that explains their behavior. - For short examples inside comments, indent code rather than using backticks; godoc treats indented blocks as preformatted. - Branding: Always spell the product name as `PhotoPrism`; this proper noun is an exception to generic naming rules. - Every Go package must contain a `.go` file in its root (for example, `internal/auth/jwt/jwt.go`) with the standard license header and a short package description comment explaining its purpose. diff --git a/internal/ai/tensorflow/util.go b/internal/ai/tensorflow/util.go index 050c20063..ce51f9dac 100644 --- a/internal/ai/tensorflow/util.go +++ b/internal/ai/tensorflow/util.go @@ -1,12 +1,12 @@ package tensorflow -import "math/rand" +import "math/rand/v2" func randomString(length int) string { const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" result := make([]byte, length) for i := range result { - result[i] = charset[rand.Intn(len(charset))] + result[i] = charset[rand.IntN(len(charset))] } return string(result) } diff --git a/internal/auth/jwt/verifier.go b/internal/auth/jwt/verifier.go index dc46dfba0..c74ba5590 100644 --- a/internal/auth/jwt/verifier.go +++ b/internal/auth/jwt/verifier.go @@ -7,7 +7,7 @@ import ( "encoding/json" "errors" "fmt" - "math/rand" + "math/rand/v2" "net/http" "os" "path/filepath" @@ -49,7 +49,7 @@ const ( ) // randInt63n is defined for deterministic testing of jitter (overridable in tests). -var randInt63n = rand.Int63n +var randInt63n = rand.Int64N // cacheEntry stores the JWKS material cached on disk and in memory. type cacheEntry struct { diff --git a/internal/config/config.go b/internal/config/config.go index f022903cf..7c9fe15c9 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -25,8 +25,10 @@ Additional information can be found in our Developer Guide: package config import ( + "context" "crypto/tls" "fmt" + "math/rand/v2" "net/http" "os" "path/filepath" @@ -75,6 +77,8 @@ type Config struct { db *gorm.DB dbVersion string hub *hub.Config + hubCancel context.CancelFunc + hubLock sync.Mutex token string serial string env string @@ -622,8 +626,22 @@ func (c *Config) SetLogLevel(level logrus.Level) { SetLogLevel(level) } +// stopHubTicker stops the periodic hub renewal ticker if it is running. +func (c *Config) stopHubTicker() { + c.hubLock.Lock() + cancel := c.hubCancel + c.hubCancel = nil + c.hubLock.Unlock() + + if cancel != nil { + cancel() + } +} + // Shutdown shuts down the active processes and closes the database connection. func (c *Config) Shutdown() { + c.stopHubTicker() + // App is no longer accepting requests. c.ready.Store(false) @@ -820,13 +838,24 @@ func (c *Config) initHub() { c.hub.Propagate() - ticker := time.NewTicker(time.Hour * 24) + ctx, cancel := context.WithCancel(context.Background()) + + c.hubLock.Lock() + c.hubCancel = cancel + c.hubLock.Unlock() + + d := 23*time.Hour + time.Duration(float64(2*time.Hour)*rand.Float64()) + ticker := time.NewTicker(d) go func() { + defer ticker.Stop() + for { select { case <-ticker.C: c.RenewApiKeys() + case <-ctx.Done(): + return } } }()