Skip to content

Commit 80a6b0f

Browse files
authored
Avatars and Repo avatars support storing in minio (go-gitea#12516)
* Avatar support minio * Support repo avatar minio storage * Add missing migration * Fix bug * Fix test * Add test for minio store type on avatars and repo avatars; Add documents * Fix bug * Fix bug * Add back missed avatar link method * refactor codes * Simplify the codes * Code improvements * Fix lint * Fix test mysql * Fix test mysql * Fix test mysql * Fix settings * Fix test * fix test * Fix bug
1 parent 93f7525 commit 80a6b0f

21 files changed

+705
-477
lines changed

cmd/migrate_storage.go

+23-2
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,20 @@ func migrateLFS(dstStorage storage.ObjectStorage) error {
9191
})
9292
}
9393

94+
func migrateAvatars(dstStorage storage.ObjectStorage) error {
95+
return models.IterateUser(func(user *models.User) error {
96+
_, err := storage.Copy(dstStorage, user.CustomAvatarRelativePath(), storage.Avatars, user.CustomAvatarRelativePath())
97+
return err
98+
})
99+
}
100+
101+
func migrateRepoAvatars(dstStorage storage.ObjectStorage) error {
102+
return models.IterateRepository(func(repo *models.Repository) error {
103+
_, err := storage.Copy(dstStorage, repo.CustomAvatarRelativePath(), storage.RepoAvatars, repo.CustomAvatarRelativePath())
104+
return err
105+
})
106+
}
107+
94108
func runMigrateStorage(ctx *cli.Context) error {
95109
if err := initDB(); err != nil {
96110
return err
@@ -142,9 +156,8 @@ func runMigrateStorage(ctx *cli.Context) error {
142156
UseSSL: ctx.Bool("minio-use-ssl"),
143157
})
144158
default:
145-
return fmt.Errorf("Unsupported attachments storage type: %s", ctx.String("storage"))
159+
return fmt.Errorf("Unsupported storage type: %s", ctx.String("storage"))
146160
}
147-
148161
if err != nil {
149162
return err
150163
}
@@ -159,6 +172,14 @@ func runMigrateStorage(ctx *cli.Context) error {
159172
if err := migrateLFS(dstStorage); err != nil {
160173
return err
161174
}
175+
case "avatars":
176+
if err := migrateAvatars(dstStorage); err != nil {
177+
return err
178+
}
179+
case "repo-avatars":
180+
if err := migrateRepoAvatars(dstStorage); err != nil {
181+
return err
182+
}
162183
default:
163184
return fmt.Errorf("Unsupported storage: %s", ctx.String("type"))
164185
}

docs/content/doc/advanced/config-cheat-sheet.en-us.md

+9-4
Original file line numberDiff line numberDiff line change
@@ -564,16 +564,21 @@ Define allowed algorithms and their minimum key length (use -1 to disable a type
564564
- `DISABLE_GRAVATAR`: **false**: Enable this to use local avatars only.
565565
- `ENABLE_FEDERATED_AVATAR`: **false**: Enable support for federated avatars (see
566566
[http://www.libravatar.org](http://www.libravatar.org)).
567+
568+
- `AVATAR_STORAGE_TYPE`: **default**: Storage type defined in `[storage.xxx]`. Default is `default` which will read `[storage]` if no section `[storage]` will be a type `local`.
567569
- `AVATAR_UPLOAD_PATH`: **data/avatars**: Path to store user avatar image files.
570+
- `AVATAR_MAX_WIDTH`: **4096**: Maximum avatar image width in pixels.
571+
- `AVATAR_MAX_HEIGHT`: **3072**: Maximum avatar image height in pixels.
572+
- `AVATAR_MAX_FILE_SIZE`: **1048576** (1Mb): Maximum avatar image file size in bytes.
573+
574+
- `REPOSITORY_AVATAR_STORAGE_TYPE`: **default**: Storage type defined in `[storage.xxx]`. Default is `default` which will read `[storage]` if no section `[storage]` will be a type `local`.
568575
- `REPOSITORY_AVATAR_UPLOAD_PATH`: **data/repo-avatars**: Path to store repository avatar image files.
569576
- `REPOSITORY_AVATAR_FALLBACK`: **none**: How Gitea deals with missing repository avatars
570577
- none = no avatar will be displayed
571578
- random = random avatar will be generated
572-
- image = default image will be used (which is set in `REPOSITORY_AVATAR_DEFAULT_IMAGE`)
579+
- image = default image will be used (which is set in `REPOSITORY_AVATAR_FALLBACK_IMAGE`)
573580
- `REPOSITORY_AVATAR_FALLBACK_IMAGE`: **/img/repo_default.png**: Image used as default repository avatar (if `REPOSITORY_AVATAR_FALLBACK` is set to image and none was uploaded)
574-
- `AVATAR_MAX_WIDTH`: **4096**: Maximum avatar image width in pixels.
575-
- `AVATAR_MAX_HEIGHT`: **3072**: Maximum avatar image height in pixels.
576-
- `AVATAR_MAX_FILE_SIZE`: **1048576** (1Mb): Maximum avatar image file size in bytes.
581+
577582

578583
## Project (`project`)
579584

docs/content/doc/advanced/config-cheat-sheet.zh-cn.md

+14
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,20 @@ menu:
182182
- `DISABLE_GRAVATAR`: 开启则只使用内部头像。
183183
- `ENABLE_FEDERATED_AVATAR`: 启用头像联盟支持 (参见 http://www.libravatar.org)
184184

185+
- `AVATAR_STORAGE_TYPE`: **local**: 头像存储类型,可以为 `local``minio`,分别支持本地文件系统和 minio 兼容的API。
186+
- `AVATAR_UPLOAD_PATH`: **data/avatars**: 存储头像的文件系统路径。
187+
- `AVATAR_MAX_WIDTH`: **4096**: 头像最大宽度,单位像素。
188+
- `AVATAR_MAX_HEIGHT`: **3072**: 头像最大高度,单位像素。
189+
- `AVATAR_MAX_FILE_SIZE`: **1048576** (1Mb): 头像最大大小。
190+
191+
- `REPOSITORY_AVATAR_STORAGE_TYPE`: **local**: 仓库头像存储类型,可以为 `local``minio`,分别支持本地文件系统和 minio 兼容的API。
192+
- `REPOSITORY_AVATAR_UPLOAD_PATH`: **data/repo-avatars**: 存储仓库头像的路径。
193+
- `REPOSITORY_AVATAR_FALLBACK`: **none**: 当头像丢失时的处理方式
194+
- none = 不显示头像
195+
- random = 显示随机生成的头像
196+
- image = 显示默认头像,通过 `REPOSITORY_AVATAR_FALLBACK_IMAGE` 设置
197+
- `REPOSITORY_AVATAR_FALLBACK_IMAGE`: **/img/repo_default.png**: 默认仓库头像
198+
185199
## Attachment (`attachment`)
186200

187201
- `ENABLED`: 是否允许用户上传附件。

integrations/mysql.ini.tmpl

+2-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ LFS_MINIO_BASE_PATH = lfs/
5858
LFS_MINIO_USE_SSL = false
5959

6060
[attachment]
61-
STORE_TYPE = minio
61+
STORAGE_TYPE = minio
6262
SERVE_DIRECT = false
6363
MINIO_ENDPOINT = minio:9000
6464
MINIO_ACCESS_KEY_ID = 123456
@@ -87,6 +87,7 @@ ENABLE_NOTIFY_MAIL = true
8787
[picture]
8888
DISABLE_GRAVATAR = false
8989
ENABLE_FEDERATED_AVATAR = false
90+
9091
AVATAR_UPLOAD_PATH = integrations/gitea-integration-mysql/data/avatars
9192
REPOSITORY_AVATAR_UPLOAD_PATH = integrations/gitea-integration-mysql/data/repo-avatars
9293

models/migrations/v115.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func renameExistingUserAvatarName(x *xorm.Engine) error {
6161
for _, user := range users {
6262
oldAvatar := user.Avatar
6363

64-
if stat, err := os.Stat(filepath.Join(setting.AvatarUploadPath, oldAvatar)); err != nil || !stat.Mode().IsRegular() {
64+
if stat, err := os.Stat(filepath.Join(setting.Avatar.Path, oldAvatar)); err != nil || !stat.Mode().IsRegular() {
6565
if err == nil {
6666
err = fmt.Errorf("Error: \"%s\" is not a regular file", oldAvatar)
6767
}
@@ -86,7 +86,7 @@ func renameExistingUserAvatarName(x *xorm.Engine) error {
8686
return fmt.Errorf("[user: %s] user table update: %v", user.LowerName, err)
8787
}
8888

89-
deleteList[filepath.Join(setting.AvatarUploadPath, oldAvatar)] = struct{}{}
89+
deleteList[filepath.Join(setting.Avatar.Path, oldAvatar)] = struct{}{}
9090
migrated++
9191
select {
9292
case <-ticker.C:
@@ -135,7 +135,7 @@ func renameExistingUserAvatarName(x *xorm.Engine) error {
135135
// copyOldAvatarToNewLocation copies oldAvatar to newAvatarLocation
136136
// and returns newAvatar location
137137
func copyOldAvatarToNewLocation(userID int64, oldAvatar string) (string, error) {
138-
fr, err := os.Open(filepath.Join(setting.AvatarUploadPath, oldAvatar))
138+
fr, err := os.Open(filepath.Join(setting.Avatar.Path, oldAvatar))
139139
if err != nil {
140140
return "", fmt.Errorf("os.Open: %v", err)
141141
}
@@ -151,7 +151,7 @@ func copyOldAvatarToNewLocation(userID int64, oldAvatar string) (string, error)
151151
return newAvatar, nil
152152
}
153153

154-
if err := ioutil.WriteFile(filepath.Join(setting.AvatarUploadPath, newAvatar), data, 0666); err != nil {
154+
if err := ioutil.WriteFile(filepath.Join(setting.Avatar.Path, newAvatar), data, 0666); err != nil {
155155
return "", fmt.Errorf("ioutil.WriteFile: %v", err)
156156
}
157157

models/org.go

+4-6
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ import (
1111

1212
"code.gitea.io/gitea/modules/log"
1313
"code.gitea.io/gitea/modules/setting"
14+
"code.gitea.io/gitea/modules/storage"
1415
"code.gitea.io/gitea/modules/structs"
1516
"code.gitea.io/gitea/modules/util"
1617

17-
"github.com/unknwon/com"
1818
"xorm.io/builder"
1919
"xorm.io/xorm"
2020
)
@@ -310,11 +310,9 @@ func deleteOrg(e *xorm.Session, u *User) error {
310310
}
311311

312312
if len(u.Avatar) > 0 {
313-
avatarPath := u.CustomAvatarPath()
314-
if com.IsExist(avatarPath) {
315-
if err := util.Remove(avatarPath); err != nil {
316-
return fmt.Errorf("Failed to remove %s: %v", avatarPath, err)
317-
}
313+
avatarPath := u.CustomAvatarRelativePath()
314+
if err := storage.Avatars.Delete(avatarPath); err != nil {
315+
return fmt.Errorf("Failed to remove %s: %v", avatarPath, err)
318316
}
319317
}
320318

0 commit comments

Comments
 (0)