fix(avatar): use sha256 and inline the federated avatar lookup (#38843)

- Hash emails with sha256. Gravatar moved to sha256, and both it and
libravatar.org serve the same image for either hash.
- Drop `strk.kbt.io/projects/go/libravatar` for a 46 line inline SRV
lookup. It could not bound or cancel its DNS query and panicked on an
unexpected resolver error. The replacement carries the request context
and a 3s timeout.
- Fix federated avatars querying DNS for every avatar on every render.
`loadAvatarSetting` compared a cache field that was never assigned, so
each call rebuilt the resolver and dropped its cache. That cache is
gone, both settings are read where they are used.
- Migration 348 recreates `email_hash` with a 64 char hash column and a
`hash_type` column, so a later algorithm change can tell old rows apart.
The MD5 rows are unreachable and their `UNIQUE` email index would reject
the SHA256 replacements.
- Fix a re-saved avatar form replacing an uploaded avatar with a random
one.
- Remove the `duoshuo` `GRAVATAR_SOURCE` alias, that service shut down
in 2017.
- Remove dead i18n key.

Fixes: https://github.com/go-gitea/gitea/issues/34284
Fixes: https://github.com/go-gitea/gitea/issues/28110
Docs: https://gitea.com/gitea/docs/pulls/499
Signed-off-by: silverwind <me@silverwind.io>
This commit is contained in:
silverwind
2026-08-11 01:13:28 +02:00
committed by GitHub
parent 52d0e18dac
commit e3ee28f15b
17 changed files with 221 additions and 216 deletions
-1
View File
@@ -58,7 +58,6 @@ func UpdateAvatarSetting(ctx *context.Context, form forms.AvatarForm) error {
// SettingsAvatar save new POSTed repository avatar
func SettingsAvatar(ctx *context.Context) {
form := web.GetForm[*forms.AvatarForm](ctx)
form.Source = forms.AvatarLocal
if err := UpdateAvatarSetting(ctx, *form); err != nil {
ctx.Flash.Error(err.Error())
} else {
+1 -6
View File
@@ -14,7 +14,6 @@ import (
"path/filepath"
"strings"
"gitea.dev/models/avatars"
"gitea.dev/models/db"
"gitea.dev/models/organization"
repo_model "gitea.dev/models/repo"
@@ -127,11 +126,7 @@ func ProfilePost(ctx *context.Context) {
func UpdateAvatarSetting(ctx *context.Context, form *forms.AvatarForm, ctxUser *user_model.User) error {
ctxUser.UseCustomAvatar = form.Source == forms.AvatarLocal
if len(form.Gravatar) > 0 {
if form.Avatar != nil {
ctxUser.Avatar = avatars.HashEmail(form.Gravatar)
} else {
ctxUser.Avatar = ""
}
ctxUser.Avatar = "" // UploadAvatar sets the real storage path when a file is uploaded
ctxUser.AvatarEmail = form.Gravatar
}