feat(user): add avatar cache flushing (#1041)

This commit is contained in:
kolaente
2025-06-27 14:01:43 +02:00
committed by GitHub
parent 2cf2f98f1d
commit 0ecbd9e1a3
12 changed files with 80 additions and 1 deletions

View File

@@ -28,6 +28,7 @@ import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/modules/auth"
"code.vikunja.io/api/pkg/modules/avatar"
"code.vikunja.io/api/pkg/modules/avatar/upload"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/api/pkg/utils"
@@ -187,6 +188,7 @@ func AuthenticateUserInLDAP(s *xorm.Session, username, password string, syncGrou
if err != nil {
return nil, err
}
avatar.FlushAllCaches(u)
}
}

View File

@@ -30,6 +30,7 @@ import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/modules/auth"
"code.vikunja.io/api/pkg/modules/avatar"
"code.vikunja.io/api/pkg/modules/avatar/upload"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/api/pkg/utils"
@@ -250,6 +251,8 @@ func syncUserAvatarFromOpenID(s *xorm.Session, u *user.User, pictureURL string)
return fmt.Errorf("error storing avatar: %w", err)
}
avatar.FlushAllCaches(u)
return nil
}

View File

@@ -16,10 +16,40 @@
package avatar
import "code.vikunja.io/api/pkg/user"
import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/modules/avatar/empty"
"code.vikunja.io/api/pkg/modules/avatar/gravatar"
"code.vikunja.io/api/pkg/modules/avatar/initials"
"code.vikunja.io/api/pkg/modules/avatar/ldap"
"code.vikunja.io/api/pkg/modules/avatar/marble"
"code.vikunja.io/api/pkg/modules/avatar/openid"
"code.vikunja.io/api/pkg/modules/avatar/upload"
"code.vikunja.io/api/pkg/user"
)
// Provider defines the avatar provider interface
type Provider interface {
// GetAvatar is the method used to get an actual avatar for a user
GetAvatar(user *user.User, size int64) (avatar []byte, mimeType string, err error)
// FlushCache removes cached avatar data for the user
FlushCache(u *user.User) error
}
// FlushAllCaches removes cached avatars for the given user for all providers
func FlushAllCaches(u *user.User) {
providers := []Provider{
&upload.Provider{},
&gravatar.Provider{},
&initials.Provider{},
&ldap.Provider{},
&openid.Provider{},
&marble.Provider{},
&empty.Provider{},
}
for _, p := range providers {
if err := p.FlushCache(u); err != nil {
log.Errorf("Error flushing avatar cache: %v", err)
}
}
}

View File

@@ -22,6 +22,9 @@ import "code.vikunja.io/api/pkg/user"
type Provider struct {
}
// FlushCache is a no-op for the empty provider
func (p *Provider) FlushCache(_ *user.User) error { return nil }
const defaultAvatar string = `<?xml version="1.0" encoding="UTF-8"?>
<svg width="128" height="128" version="1.1" viewBox="0 0 33.867 33.867" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:xlink="http://www.w3.org/1999/xlink">
<metadata>

View File

@@ -41,6 +41,11 @@ type avatar struct {
type Provider struct {
}
// FlushCache removes all gravatar cache entries for a user
func (g *Provider) FlushCache(u *user.User) error {
return keyvalue.DelPrefix(keyPrefix + u.Username + "_")
}
const keyPrefix = "gravatar_avatar_"
// GetAvatar implements getting the avatar for the user

View File

@@ -39,6 +39,14 @@ import (
type Provider struct {
}
// FlushCache removes cached initials avatars for a user
func (p *Provider) FlushCache(u *user.User) error {
if err := keyvalue.Del(getCacheKey("full", u.ID)); err != nil {
return err
}
return keyvalue.DelPrefix(getCacheKey("resized", u.ID))
}
var (
avatarBgColors = []*color.RGBA{
{R: 69, G: 189, B: 243, A: 255},

View File

@@ -28,3 +28,8 @@ func (p *Provider) GetAvatar(user *user.User, size int64) (avatar []byte, mimeTy
return up.GetAvatar(user, size)
}
func (p *Provider) FlushCache(u *user.User) error {
up := upload.Provider{}
return up.FlushCache(u)
}

View File

@@ -27,6 +27,9 @@ import (
type Provider struct {
}
// FlushCache is a no-op for the marble provider
func (p *Provider) FlushCache(_ *user.User) error { return nil }
const avatarSize = 80
var colors = []string{

View File

@@ -28,3 +28,8 @@ func (p *Provider) GetAvatar(user *user.User, size int64) (avatar []byte, mimeTy
return up.GetAvatar(user, size)
}
func (p *Provider) FlushCache(u *user.User) error {
up := upload.Provider{}
return up.FlushCache(u)
}

View File

@@ -36,6 +36,12 @@ import (
type Provider struct {
}
// FlushCache removes cached avatars for a user
func (p *Provider) FlushCache(u *user.User) error {
InvalidateCache(u)
return nil
}
// CachedAvatar represents a cached avatar with its content and mime type
type CachedAvatar struct {
Content []byte