Skip to content

Avatars and Repo avatars support storing in minio #12516

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 21 commits into from
Oct 14, 2020
Merged
25 changes: 23 additions & 2 deletions cmd/migrate_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,20 @@ func migrateLFS(dstStorage storage.ObjectStorage) error {
})
}

func migrateAvatars(dstStorage storage.ObjectStorage) error {
return models.IterateUser(func(user *models.User) error {
_, err := storage.Copy(dstStorage, user.CustomAvatarRelativePath(), storage.Avatars, user.CustomAvatarRelativePath())
return err
})
}

func migrateRepoAvatars(dstStorage storage.ObjectStorage) error {
return models.IterateRepository(func(repo *models.Repository) error {
_, err := storage.Copy(dstStorage, repo.CustomAvatarRelativePath(), storage.RepoAvatars, repo.CustomAvatarRelativePath())
return err
})
}

func runMigrateStorage(ctx *cli.Context) error {
if err := initDB(); err != nil {
return err
Expand Down Expand Up @@ -142,9 +156,8 @@ func runMigrateStorage(ctx *cli.Context) error {
UseSSL: ctx.Bool("minio-use-ssl"),
})
default:
return fmt.Errorf("Unsupported attachments storage type: %s", ctx.String("storage"))
return fmt.Errorf("Unsupported storage type: %s", ctx.String("storage"))
}

if err != nil {
return err
}
Expand All @@ -159,6 +172,14 @@ func runMigrateStorage(ctx *cli.Context) error {
if err := migrateLFS(dstStorage); err != nil {
return err
}
case "avatars":
if err := migrateAvatars(dstStorage); err != nil {
return err
}
case "repo-avatars":
if err := migrateRepoAvatars(dstStorage); err != nil {
return err
}
default:
return fmt.Errorf("Unsupported storage: %s", ctx.String("type"))
}
Expand Down
13 changes: 9 additions & 4 deletions docs/content/doc/advanced/config-cheat-sheet.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -564,16 +564,21 @@ Define allowed algorithms and their minimum key length (use -1 to disable a type
- `DISABLE_GRAVATAR`: **false**: Enable this to use local avatars only.
- `ENABLE_FEDERATED_AVATAR`: **false**: Enable support for federated avatars (see
[http://www.libravatar.org](http://www.libravatar.org)).

- `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`.
- `AVATAR_UPLOAD_PATH`: **data/avatars**: Path to store user avatar image files.
- `AVATAR_MAX_WIDTH`: **4096**: Maximum avatar image width in pixels.
- `AVATAR_MAX_HEIGHT`: **3072**: Maximum avatar image height in pixels.
- `AVATAR_MAX_FILE_SIZE`: **1048576** (1Mb): Maximum avatar image file size in bytes.

- `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`.
- `REPOSITORY_AVATAR_UPLOAD_PATH`: **data/repo-avatars**: Path to store repository avatar image files.
- `REPOSITORY_AVATAR_FALLBACK`: **none**: How Gitea deals with missing repository avatars
- none = no avatar will be displayed
- random = random avatar will be generated
- image = default image will be used (which is set in `REPOSITORY_AVATAR_DEFAULT_IMAGE`)
- image = default image will be used (which is set in `REPOSITORY_AVATAR_FALLBACK_IMAGE`)
- `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)
- `AVATAR_MAX_WIDTH`: **4096**: Maximum avatar image width in pixels.
- `AVATAR_MAX_HEIGHT`: **3072**: Maximum avatar image height in pixels.
- `AVATAR_MAX_FILE_SIZE`: **1048576** (1Mb): Maximum avatar image file size in bytes.


## Project (`project`)

Expand Down
14 changes: 14 additions & 0 deletions docs/content/doc/advanced/config-cheat-sheet.zh-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,20 @@ menu:
- `DISABLE_GRAVATAR`: 开启则只使用内部头像。
- `ENABLE_FEDERATED_AVATAR`: 启用头像联盟支持 (参见 http://www.libravatar.org)

- `AVATAR_STORAGE_TYPE`: **local**: 头像存储类型,可以为 `local` 或 `minio`,分别支持本地文件系统和 minio 兼容的API。
- `AVATAR_UPLOAD_PATH`: **data/avatars**: 存储头像的文件系统路径。
- `AVATAR_MAX_WIDTH`: **4096**: 头像最大宽度,单位像素。
- `AVATAR_MAX_HEIGHT`: **3072**: 头像最大高度,单位像素。
- `AVATAR_MAX_FILE_SIZE`: **1048576** (1Mb): 头像最大大小。

- `REPOSITORY_AVATAR_STORAGE_TYPE`: **local**: 仓库头像存储类型,可以为 `local` 或 `minio`,分别支持本地文件系统和 minio 兼容的API。
- `REPOSITORY_AVATAR_UPLOAD_PATH`: **data/repo-avatars**: 存储仓库头像的路径。
- `REPOSITORY_AVATAR_FALLBACK`: **none**: 当头像丢失时的处理方式
- none = 不显示头像
- random = 显示随机生成的头像
- image = 显示默认头像,通过 `REPOSITORY_AVATAR_FALLBACK_IMAGE` 设置
- `REPOSITORY_AVATAR_FALLBACK_IMAGE`: **/img/repo_default.png**: 默认仓库头像

## Attachment (`attachment`)

- `ENABLED`: 是否允许用户上传附件。
Expand Down
3 changes: 2 additions & 1 deletion integrations/mysql.ini.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ LFS_MINIO_BASE_PATH = lfs/
LFS_MINIO_USE_SSL = false

[attachment]
STORE_TYPE = minio
STORAGE_TYPE = minio
SERVE_DIRECT = false
MINIO_ENDPOINT = minio:9000
MINIO_ACCESS_KEY_ID = 123456
Expand Down Expand Up @@ -87,6 +87,7 @@ ENABLE_NOTIFY_MAIL = true
[picture]
DISABLE_GRAVATAR = false
ENABLE_FEDERATED_AVATAR = false

AVATAR_UPLOAD_PATH = integrations/gitea-integration-mysql/data/avatars
REPOSITORY_AVATAR_UPLOAD_PATH = integrations/gitea-integration-mysql/data/repo-avatars

Expand Down
8 changes: 4 additions & 4 deletions models/migrations/v115.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func renameExistingUserAvatarName(x *xorm.Engine) error {
for _, user := range users {
oldAvatar := user.Avatar

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

deleteList[filepath.Join(setting.AvatarUploadPath, oldAvatar)] = struct{}{}
deleteList[filepath.Join(setting.Avatar.Path, oldAvatar)] = struct{}{}
migrated++
select {
case <-ticker.C:
Expand Down Expand Up @@ -135,7 +135,7 @@ func renameExistingUserAvatarName(x *xorm.Engine) error {
// copyOldAvatarToNewLocation copies oldAvatar to newAvatarLocation
// and returns newAvatar location
func copyOldAvatarToNewLocation(userID int64, oldAvatar string) (string, error) {
fr, err := os.Open(filepath.Join(setting.AvatarUploadPath, oldAvatar))
fr, err := os.Open(filepath.Join(setting.Avatar.Path, oldAvatar))
if err != nil {
return "", fmt.Errorf("os.Open: %v", err)
}
Expand All @@ -151,7 +151,7 @@ func copyOldAvatarToNewLocation(userID int64, oldAvatar string) (string, error)
return newAvatar, nil
}

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

Expand Down
10 changes: 4 additions & 6 deletions models/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import (

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"

"github.com/unknwon/com"
"xorm.io/builder"
"xorm.io/xorm"
)
Expand Down Expand Up @@ -310,11 +310,9 @@ func deleteOrg(e *xorm.Session, u *User) error {
}

if len(u.Avatar) > 0 {
avatarPath := u.CustomAvatarPath()
if com.IsExist(avatarPath) {
if err := util.Remove(avatarPath); err != nil {
return fmt.Errorf("Failed to remove %s: %v", avatarPath, err)
}
avatarPath := u.CustomAvatarRelativePath()
if err := storage.Avatars.Delete(avatarPath); err != nil {
return fmt.Errorf("Failed to remove %s: %v", avatarPath, err)
}
}

Expand Down
Loading