-
-
Notifications
You must be signed in to change notification settings - Fork 5.8k
Avatar refactor, move avatar code from models
to models.avatars
, remove duplicated code
#17123
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 21 commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
cc8a453
avatar-refactor
wxiaoguang c258ca3
fix unit test
wxiaoguang fa167dd
fix avatar link
wxiaoguang bb53198
fix unit test
wxiaoguang a74873a
optimize http cache header
wxiaoguang 239f5b8
fix avatar size
wxiaoguang 758b2a6
fix avatar size
wxiaoguang 4ec07df
avatar-refactor
wxiaoguang d4e9afb
fix unit test
wxiaoguang 9eb3043
fix unit test
wxiaoguang 60d0dfd
fix appsuburl
wxiaoguang 851167a
fix unit test
wxiaoguang 3641257
Merge remote-tracking branch 'go-gitea/main' into avatar-refactor
wxiaoguang 91151ed
fix merge, set cacheableRedirect cache time to 5m
wxiaoguang 2b39ae5
fmt
wxiaoguang ca6ec29
Merge remote-tracking branch 'go-gitea/main' into avatar-refactor
wxiaoguang 7c65d1e
Merge branch 'main' into avatar-refactor
6543 3852ca4
Merge remote-tracking branch 'go-gitea/main' into avatar-refactor
wxiaoguang ef27c55
clean up
wxiaoguang bbcf279
Merge branch 'main' into avatar-refactor
wxiaoguang f594a5f
fix
wxiaoguang 4ab7616
Merge branch 'main' into avatar-refactor
6543 225c97d
Merge branch 'main' into avatar-refactor
lunny 5053319
Merge branch 'main' into avatar-refactor
6543 43a757c
Update modules/httpcache/httpcache.go
6543 9f7baec
Merge branch 'main' into avatar-refactor
6543 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
// Copyright 2021 The Gitea Authors. All rights reserved. | ||
// Use of this source code is governed by a MIT-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package avatars | ||
|
||
import ( | ||
"context" | ||
"net/url" | ||
"path" | ||
"strconv" | ||
"strings" | ||
|
||
"code.gitea.io/gitea/models/db" | ||
"code.gitea.io/gitea/modules/base" | ||
"code.gitea.io/gitea/modules/cache" | ||
"code.gitea.io/gitea/modules/log" | ||
"code.gitea.io/gitea/modules/setting" | ||
) | ||
|
||
// DefaultAvatarPixelSize is the default size in pixels of a rendered avatar | ||
const DefaultAvatarPixelSize = 28 | ||
|
||
// AvatarRenderedSizeFactor is the factor by which the default size is increased for finer rendering | ||
const AvatarRenderedSizeFactor = 4 | ||
|
||
// EmailHash represents a pre-generated hash map (mainly used by LibravatarURL, it queries email server's DNS records) | ||
type EmailHash struct { | ||
Hash string `xorm:"pk varchar(32)"` | ||
Email string `xorm:"UNIQUE NOT NULL"` | ||
} | ||
|
||
func init() { | ||
db.RegisterModel(new(EmailHash)) | ||
} | ||
|
||
// DefaultAvatarLink the default avatar link | ||
func DefaultAvatarLink() string { | ||
u, err := url.Parse(setting.AppSubURL) | ||
if err != nil { | ||
log.Error("GetUserByEmail: %v", err) | ||
return "" | ||
} | ||
|
||
u.Path = path.Join(u.Path, "/assets/img/avatar_default.png") | ||
return u.String() | ||
} | ||
|
||
// HashEmail hashes email address to MD5 string. https://en.gravatar.com/site/implement/hash/ | ||
func HashEmail(email string) string { | ||
return base.EncodeMD5(strings.ToLower(strings.TrimSpace(email))) | ||
} | ||
|
||
// GetEmailForHash converts a provided md5sum to the email | ||
func GetEmailForHash(md5Sum string) (string, error) { | ||
return cache.GetString("Avatar:"+md5Sum, func() (string, error) { | ||
emailHash := EmailHash{ | ||
Hash: strings.ToLower(strings.TrimSpace(md5Sum)), | ||
} | ||
|
||
_, err := db.GetEngine(db.DefaultContext).Get(&emailHash) | ||
return emailHash.Email, err | ||
}) | ||
} | ||
|
||
// LibravatarURL returns the URL for the given email. Slow due to the DNS lookup. | ||
// This function should only be called if a federated avatar service is enabled. | ||
func LibravatarURL(email string) (*url.URL, error) { | ||
urlStr, err := setting.LibravatarService.FromEmail(email) | ||
if err != nil { | ||
log.Error("LibravatarService.FromEmail(email=%s): error %v", email, err) | ||
return nil, err | ||
} | ||
u, err := url.Parse(urlStr) | ||
if err != nil { | ||
log.Error("Failed to parse libravatar url(%s): error %v", urlStr, err) | ||
return nil, err | ||
} | ||
return u, nil | ||
} | ||
|
||
// saveEmailHash returns an avatar link for a provided email, | ||
// the email and hash are saved into database, which will be used by GetEmailForHash later | ||
func saveEmailHash(email string) string { | ||
lowerEmail := strings.ToLower(strings.TrimSpace(email)) | ||
emailHash := HashEmail(lowerEmail) | ||
_, _ = cache.GetString("Avatar:"+emailHash, func() (string, error) { | ||
emailHash := &EmailHash{ | ||
Email: lowerEmail, | ||
Hash: emailHash, | ||
} | ||
// OK we're going to open a session just because I think that that might hide away any problems with postgres reporting errors | ||
if err := db.WithTx(func(ctx context.Context) error { | ||
has, err := db.GetEngine(ctx).Where("email = ? AND hash = ?", emailHash.Email, emailHash.Hash).Get(new(EmailHash)) | ||
if has || err != nil { | ||
// Seriously we don't care about any DB problems just return the lowerEmail - we expect the transaction to fail most of the time | ||
return nil | ||
} | ||
_, _ = db.GetEngine(ctx).Insert(emailHash) | ||
return nil | ||
}); err != nil { | ||
// Seriously we don't care about any DB problems just return the lowerEmail - we expect the transaction to fail most of the time | ||
return lowerEmail, nil | ||
} | ||
return lowerEmail, nil | ||
}) | ||
return emailHash | ||
} | ||
|
||
// GenerateUserAvatarFastLink returns a fast link (302) to the user's avatar: "/user/avatar/${User.Name}/${size}" | ||
func GenerateUserAvatarFastLink(userName string, size int) string { | ||
if size < 0 { | ||
size = 0 | ||
} | ||
return setting.AppSubURL + "/user/avatar/" + userName + "/" + strconv.Itoa(size) | ||
} | ||
|
||
// GenerateUserAvatarImageLink returns a link for `User.Avatar` image file: "/avatars/${User.Avatar}" | ||
func GenerateUserAvatarImageLink(userAvatar string, size int) string { | ||
if size > 0 { | ||
return setting.AppSubURL + "/avatars/" + userAvatar + "?size=" + strconv.Itoa(size) | ||
} | ||
return setting.AppSubURL + "/avatars/" + userAvatar | ||
} | ||
|
||
// generateRecognizedAvatarURL generate a recognized avatar (Gravatar/Libravatar) URL, it modifies the URL so the parameter is passed by a copy | ||
func generateRecognizedAvatarURL(u url.URL, size int) string { | ||
urlQuery := u.Query() | ||
urlQuery.Set("d", "identicon") | ||
if size > 0 { | ||
urlQuery.Set("s", strconv.Itoa(size)) | ||
} | ||
u.RawQuery = urlQuery.Encode() | ||
return u.String() | ||
} | ||
|
||
// generateEmailAvatarLink returns a email avatar link. | ||
// if final is true, it may use a slow path (eg: query DNS). | ||
// if final is false, it always uses a fast path. | ||
func generateEmailAvatarLink(email string, size int, final bool) string { | ||
email = strings.TrimSpace(email) | ||
if email == "" { | ||
wxiaoguang marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return DefaultAvatarLink() | ||
} | ||
|
||
var err error | ||
if setting.EnableFederatedAvatar && setting.LibravatarService != nil { | ||
emailHash := saveEmailHash(email) | ||
if final { | ||
// for final link, we can spend more time on slow external query | ||
var avatarURL *url.URL | ||
if avatarURL, err = LibravatarURL(email); err != nil { | ||
return DefaultAvatarLink() | ||
} | ||
return generateRecognizedAvatarURL(*avatarURL, size) | ||
} | ||
// for non-final link, we should return fast (use a 302 redirection link) | ||
urlStr := setting.AppSubURL + "/avatar/" + emailHash | ||
if size > 0 { | ||
urlStr += "?size=" + strconv.Itoa(size) | ||
} | ||
return urlStr | ||
} else if !setting.DisableGravatar { | ||
// copy GravatarSourceURL, because we will modify its Path. | ||
avatarURLCopy := *setting.GravatarSourceURL | ||
avatarURLCopy.Path = path.Join(avatarURLCopy.Path, HashEmail(email)) | ||
return generateRecognizedAvatarURL(avatarURLCopy, size) | ||
} | ||
return DefaultAvatarLink() | ||
} | ||
|
||
//GenerateEmailAvatarFastLink returns a avatar link (fast, the link may be a delegated one: "/avatar/${hash}") | ||
func GenerateEmailAvatarFastLink(email string, size int) string { | ||
return generateEmailAvatarLink(email, size, false) | ||
} | ||
|
||
//GenerateEmailAvatarFinalLink returns a avatar final link (maybe slow) | ||
func GenerateEmailAvatarFinalLink(email string, size int) string { | ||
return generateEmailAvatarLink(email, size, true) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
// Use of this source code is governed by a MIT-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package models | ||
package avatars | ||
|
||
import ( | ||
"net/url" | ||
|
@@ -44,11 +44,11 @@ func TestSizedAvatarLink(t *testing.T) { | |
|
||
disableGravatar() | ||
assert.Equal(t, "/testsuburl/assets/img/avatar_default.png", | ||
SizedAvatarLink("[email protected]", 100)) | ||
GenerateEmailAvatarFastLink("[email protected]", 100)) | ||
|
||
enableGravatar(t) | ||
assert.Equal(t, | ||
"https://secure.gravatar.com/avatar/353cbad9b58e69c96154ad99f92bedc7?d=identicon&s=100", | ||
SizedAvatarLink("[email protected]", 100), | ||
GenerateEmailAvatarFastLink("[email protected]", 100), | ||
) | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.