make avatar lookup occur at image request (#10540)
speed up page generation by making avatar lookup occur at the browser not at page generation * Protect against evil email address ".." * hash the complete email address Signed-off-by: Andrew Thornton <art27@cantab.net> Co-Authored-By: Lauris BH <lauris@nix.lv>
This commit is contained in:
parent
a3f90948d8
commit
e6baa656f7
13 changed files with 154 additions and 21 deletions
|
@ -193,11 +193,32 @@ func SizedAvatarLink(email string, size int) string {
|
|||
return avatarURL.String()
|
||||
}
|
||||
|
||||
// AvatarLink returns relative avatar link to the site domain by given email,
|
||||
// which includes app sub-url as prefix. However, it is possible
|
||||
// to return full URL if user enables Gravatar-like service.
|
||||
func AvatarLink(email string) string {
|
||||
return SizedAvatarLink(email, DefaultAvatarSize)
|
||||
// SizedAvatarLinkWithDomain returns a sized link to the avatar for the given email
|
||||
// address.
|
||||
func SizedAvatarLinkWithDomain(email string, size int) string {
|
||||
var avatarURL *url.URL
|
||||
if setting.EnableFederatedAvatar && setting.LibravatarService != nil {
|
||||
var err error
|
||||
avatarURL, err = libravatarURL(email)
|
||||
if err != nil {
|
||||
return DefaultAvatarLink()
|
||||
}
|
||||
} else if !setting.DisableGravatar {
|
||||
// copy GravatarSourceURL, because we will modify its Path.
|
||||
copyOfGravatarSourceURL := *setting.GravatarSourceURL
|
||||
avatarURL = ©OfGravatarSourceURL
|
||||
avatarURL.Path = path.Join(avatarURL.Path, HashEmail(email))
|
||||
} else {
|
||||
return DefaultAvatarLink()
|
||||
}
|
||||
|
||||
vals := avatarURL.Query()
|
||||
vals.Set("d", "identicon")
|
||||
if size != DefaultAvatarSize {
|
||||
vals.Set("s", strconv.Itoa(size))
|
||||
}
|
||||
avatarURL.RawQuery = vals.Encode()
|
||||
return avatarURL.String()
|
||||
}
|
||||
|
||||
// FileSize calculates the file size and generate user-friendly string.
|
||||
|
|
|
@ -90,17 +90,6 @@ func TestSizedAvatarLink(t *testing.T) {
|
|||
)
|
||||
}
|
||||
|
||||
func TestAvatarLink(t *testing.T) {
|
||||
disableGravatar()
|
||||
assert.Equal(t, "/img/avatar_default.png", AvatarLink("gitea@example.com"))
|
||||
|
||||
enableGravatar(t)
|
||||
assert.Equal(t,
|
||||
"https://secure.gravatar.com/avatar/353cbad9b58e69c96154ad99f92bedc7?d=identicon",
|
||||
AvatarLink("gitea@example.com"),
|
||||
)
|
||||
}
|
||||
|
||||
func TestFileSize(t *testing.T) {
|
||||
var size int64 = 512
|
||||
assert.Equal(t, "512 B", FileSize(size))
|
||||
|
|
28
modules/cache/cache.go
vendored
28
modules/cache/cache.go
vendored
|
@ -41,6 +41,34 @@ func NewContext() error {
|
|||
return err
|
||||
}
|
||||
|
||||
// GetString returns the key value from cache with callback when no key exists in cache
|
||||
func GetString(key string, getFunc func() (string, error)) (string, error) {
|
||||
if conn == nil || setting.CacheService.TTL == 0 {
|
||||
return getFunc()
|
||||
}
|
||||
if !conn.IsExist(key) {
|
||||
var (
|
||||
value string
|
||||
err error
|
||||
)
|
||||
if value, err = getFunc(); err != nil {
|
||||
return value, err
|
||||
}
|
||||
err = conn.Put(key, value, int64(setting.CacheService.TTL.Seconds()))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
value := conn.Get(key)
|
||||
if v, ok := value.(string); ok {
|
||||
return v, nil
|
||||
}
|
||||
if v, ok := value.(fmt.Stringer); ok {
|
||||
return v.String(), nil
|
||||
}
|
||||
return fmt.Sprintf("%s", conn.Get(key)), nil
|
||||
}
|
||||
|
||||
// GetInt returns key value from cache with callback when no key exists in cache
|
||||
func GetInt(key string, getFunc func() (int, error)) (int, error) {
|
||||
if conn == nil || setting.CacheService.TTL == 0 {
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
|
@ -124,7 +123,7 @@ func (pc *PushCommits) AvatarLink(email string) string {
|
|||
var err error
|
||||
u, err = models.GetUserByEmail(email)
|
||||
if err != nil {
|
||||
pc.avatars[email] = base.AvatarLink(email)
|
||||
pc.avatars[email] = models.AvatarLink(email)
|
||||
if !models.IsErrUserNotExist(err) {
|
||||
log.Error("GetUserByEmail: %v", err)
|
||||
return ""
|
||||
|
|
|
@ -6,6 +6,8 @@ package repository
|
|||
|
||||
import (
|
||||
"container/list"
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -114,7 +116,7 @@ func TestPushCommits_AvatarLink(t *testing.T) {
|
|||
pushCommits.AvatarLink("user2@example.com"))
|
||||
|
||||
assert.Equal(t,
|
||||
"https://secure.gravatar.com/avatar/19ade630b94e1e0535b3df7387434154?d=identicon",
|
||||
"/avatar/"+fmt.Sprintf("%x", md5.Sum([]byte("nonexistent@example.com"))),
|
||||
pushCommits.AvatarLink("nonexistent@example.com"))
|
||||
}
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ func NewFuncMap() []template.FuncMap {
|
|||
"AllowedReactions": func() []string {
|
||||
return setting.UI.Reactions
|
||||
},
|
||||
"AvatarLink": base.AvatarLink,
|
||||
"AvatarLink": models.AvatarLink,
|
||||
"Safe": Safe,
|
||||
"SafeJS": SafeJS,
|
||||
"Str2html": Str2html,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue