You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

115 lines
3.0 KiB

  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package migrations
  5. import (
  6. "crypto/md5"
  7. "fmt"
  8. "io/ioutil"
  9. "os"
  10. "path/filepath"
  11. "code.gitea.io/gitea/modules/log"
  12. "code.gitea.io/gitea/modules/setting"
  13. "xorm.io/xorm"
  14. )
  15. func renameExistingUserAvatarName(x *xorm.Engine) error {
  16. sess := x.NewSession()
  17. defer sess.Close()
  18. type User struct {
  19. ID int64 `xorm:"pk autoincr"`
  20. LowerName string `xorm:"UNIQUE NOT NULL"`
  21. Avatar string
  22. }
  23. deleteList := make(map[string]struct{})
  24. start := 0
  25. for {
  26. if err := sess.Begin(); err != nil {
  27. return fmt.Errorf("session.Begin: %v", err)
  28. }
  29. users := make([]*User, 0, 50)
  30. if err := sess.Table("user").Asc("id").Limit(50, start).Find(&users); err != nil {
  31. return fmt.Errorf("select users from id [%d]: %v", start, err)
  32. }
  33. if len(users) == 0 {
  34. _ = sess.Rollback()
  35. break
  36. }
  37. log.Info("select users [%d - %d]", start, start+len(users))
  38. start += 50
  39. for _, user := range users {
  40. oldAvatar := user.Avatar
  41. if stat, err := os.Stat(filepath.Join(setting.AvatarUploadPath, oldAvatar)); err != nil || !stat.Mode().IsRegular() {
  42. if err == nil {
  43. err = fmt.Errorf("Error: \"%s\" is not a regular file", oldAvatar)
  44. }
  45. log.Warn("[user: %s] os.Stat: %v", user.LowerName, err)
  46. // avatar doesn't exist in the storage
  47. // no need to move avatar and update database
  48. // we can just skip this
  49. continue
  50. }
  51. newAvatar, err := copyOldAvatarToNewLocation(user.ID, oldAvatar)
  52. if err != nil {
  53. _ = sess.Rollback()
  54. return fmt.Errorf("[user: %s] %v", user.LowerName, err)
  55. } else if newAvatar == oldAvatar {
  56. continue
  57. }
  58. user.Avatar = newAvatar
  59. if _, err := sess.ID(user.ID).Cols("avatar").Update(user); err != nil {
  60. _ = sess.Rollback()
  61. return fmt.Errorf("[user: %s] user table update: %v", user.LowerName, err)
  62. }
  63. deleteList[filepath.Join(setting.AvatarUploadPath, oldAvatar)] = struct{}{}
  64. }
  65. if err := sess.Commit(); err != nil {
  66. _ = sess.Rollback()
  67. return fmt.Errorf("commit session: %v", err)
  68. }
  69. }
  70. for file := range deleteList {
  71. if err := os.Remove(file); err != nil {
  72. log.Warn("os.Remove: %v", err)
  73. }
  74. }
  75. return nil
  76. }
  77. // copyOldAvatarToNewLocation copies oldAvatar to newAvatarLocation
  78. // and returns newAvatar location
  79. func copyOldAvatarToNewLocation(userID int64, oldAvatar string) (string, error) {
  80. fr, err := os.Open(filepath.Join(setting.AvatarUploadPath, oldAvatar))
  81. if err != nil {
  82. return "", fmt.Errorf("os.Open: %v", err)
  83. }
  84. defer fr.Close()
  85. data, err := ioutil.ReadAll(fr)
  86. if err != nil {
  87. return "", fmt.Errorf("ioutil.ReadAll: %v", err)
  88. }
  89. newAvatar := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%d-%x", userID, md5.Sum(data)))))
  90. if newAvatar == oldAvatar {
  91. return newAvatar, nil
  92. }
  93. if err := ioutil.WriteFile(filepath.Join(setting.AvatarUploadPath, newAvatar), data, 0666); err != nil {
  94. return "", fmt.Errorf("ioutil.WriteFile: %v", err)
  95. }
  96. return newAvatar, nil
  97. }