forgejo/models/migrations/v115.go
Masudur Rahman e3a5b83212 Fix user avatar name (#8547)
Migrate avatar names to include user ID and the md5 hash.
2019-12-27 18:27:59 +00:00

112 lines
2.9 KiB
Go

// 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
}