mirror of
				https://github.com/go-gitea/gitea
				synced 2025-11-04 05:18:25 +00:00 
			
		
		
		
	Fix user avatar name (#8547)
Migrate avatar names to include user ID and the md5 hash.
This commit is contained in:
		@@ -284,6 +284,8 @@ var migrations = []Migration{
 | 
				
			|||||||
	NewMigration("new feature: change target branch of pull requests", featureChangeTargetBranch),
 | 
						NewMigration("new feature: change target branch of pull requests", featureChangeTargetBranch),
 | 
				
			||||||
	// v114 -> v115
 | 
						// v114 -> v115
 | 
				
			||||||
	NewMigration("Remove authentication credentials from stored URL", sanitizeOriginalURL),
 | 
						NewMigration("Remove authentication credentials from stored URL", sanitizeOriginalURL),
 | 
				
			||||||
 | 
						// v115 -> v116
 | 
				
			||||||
 | 
						NewMigration("add user_id prefix to existing user avatar name", renameExistingUserAvatarName),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Migrate database to current version
 | 
					// Migrate database to current version
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										112
									
								
								models/migrations/v115.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								models/migrations/v115.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,112 @@
 | 
				
			|||||||
 | 
					// Copyright 2019 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 migrations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"crypto/md5"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"xorm.io/xorm"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func renameExistingUserAvatarName(x *xorm.Engine) error {
 | 
				
			||||||
 | 
						sess := x.NewSession()
 | 
				
			||||||
 | 
						defer sess.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						type User struct {
 | 
				
			||||||
 | 
							ID        int64  `xorm:"pk autoincr"`
 | 
				
			||||||
 | 
							LowerName string `xorm:"UNIQUE NOT NULL"`
 | 
				
			||||||
 | 
							Avatar    string
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						deleteList := make(map[string]struct{})
 | 
				
			||||||
 | 
						start := 0
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							if err := sess.Begin(); err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("session.Begin: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							users := make([]*User, 0, 50)
 | 
				
			||||||
 | 
							if err := sess.Table("user").Asc("id").Limit(50, start).Find(&users); err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("select users from id [%d]: %v", start, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if len(users) == 0 {
 | 
				
			||||||
 | 
								_ = sess.Rollback()
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							log.Info("select users [%d - %d]", start, start+len(users))
 | 
				
			||||||
 | 
							start += 50
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for _, user := range users {
 | 
				
			||||||
 | 
								oldAvatar := user.Avatar
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if _, err := os.Stat(filepath.Join(setting.AvatarUploadPath, oldAvatar)); err != nil {
 | 
				
			||||||
 | 
									log.Warn("[user: %s] os.Stat: %v", user.LowerName, err)
 | 
				
			||||||
 | 
									// avatar doesn't exist in the storage
 | 
				
			||||||
 | 
									// no need to move avatar and update database
 | 
				
			||||||
 | 
									// we can just skip this
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								newAvatar, err := copyOldAvatarToNewLocation(user.ID, oldAvatar)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									_ = sess.Rollback()
 | 
				
			||||||
 | 
									return fmt.Errorf("[user: %s] %v", user.LowerName, err)
 | 
				
			||||||
 | 
								} else if newAvatar == oldAvatar {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								user.Avatar = newAvatar
 | 
				
			||||||
 | 
								if _, err := sess.ID(user.ID).Cols("avatar").Update(user); err != nil {
 | 
				
			||||||
 | 
									_ = sess.Rollback()
 | 
				
			||||||
 | 
									return fmt.Errorf("[user: %s] user table update: %v", user.LowerName, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								deleteList[filepath.Join(setting.AvatarUploadPath, oldAvatar)] = struct{}{}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err := sess.Commit(); err != nil {
 | 
				
			||||||
 | 
								_ = sess.Rollback()
 | 
				
			||||||
 | 
								return fmt.Errorf("commit session: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for file := range deleteList {
 | 
				
			||||||
 | 
							if err := os.Remove(file); err != nil {
 | 
				
			||||||
 | 
								log.Warn("os.Remove: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("os.Open: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer fr.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, err := ioutil.ReadAll(fr)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("ioutil.ReadAll: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						newAvatar := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%d-%x", userID, md5.Sum(data)))))
 | 
				
			||||||
 | 
						if newAvatar == oldAvatar {
 | 
				
			||||||
 | 
							return newAvatar, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := ioutil.WriteFile(filepath.Join(setting.AvatarUploadPath, newAvatar), data, 0666); err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("ioutil.WriteFile: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return newAvatar, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -521,7 +521,11 @@ func (u *User) UploadAvatar(data []byte) error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	u.UseCustomAvatar = true
 | 
						u.UseCustomAvatar = true
 | 
				
			||||||
	u.Avatar = fmt.Sprintf("%x", md5.Sum(data))
 | 
						// Different users can upload same image as avatar
 | 
				
			||||||
 | 
						// If we prefix it with u.ID, it will be separated
 | 
				
			||||||
 | 
						// Otherwise, if any of the users delete his avatar
 | 
				
			||||||
 | 
						// Other users will lose their avatars too.
 | 
				
			||||||
 | 
						u.Avatar = fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%d-%x", u.ID, md5.Sum(data)))))
 | 
				
			||||||
	if err = updateUser(sess, u); err != nil {
 | 
						if err = updateUser(sess, u); err != nil {
 | 
				
			||||||
		return fmt.Errorf("updateUser: %v", err)
 | 
							return fmt.Errorf("updateUser: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user