Files

160 lines
4.0 KiB
Go
Raw Permalink Normal View History

2019-12-28 00:27:59 +06:00
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
2019-12-28 00:27:59 +06:00
2025-06-27 07:59:55 +02:00
package v1_11
2019-12-28 00:27:59 +06:00
import (
"crypto/md5"
"fmt"
"io"
2020-05-29 23:18:24 +01:00
"math"
2019-12-28 00:27:59 +06:00
"os"
"path/filepath"
2020-05-29 23:18:24 +01:00
"time"
2019-12-28 00:27:59 +06:00
2022-10-12 07:18:26 +02:00
"code.gitea.io/gitea/modules/container"
2019-12-28 00:27:59 +06:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
2019-12-28 00:27:59 +06:00
"xorm.io/xorm"
)
2022-11-02 16:54:36 +08:00
func RenameExistingUserAvatarName(x *xorm.Engine) error {
2019-12-28 00:27:59 +06:00
sess := x.NewSession()
defer sess.Close()
type User struct {
ID int64 `xorm:"pk autoincr"`
LowerName string `xorm:"UNIQUE NOT NULL"`
Avatar string
}
2020-05-29 23:18:24 +01:00
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
count, err := x.Count(new(User))
if err != nil {
return err
}
log.Info("%d User Avatar(s) to migrate ...", count)
2022-10-12 07:18:26 +02:00
deleteList := make(container.Set[string])
2019-12-28 00:27:59 +06:00
start := 0
2020-05-29 23:18:24 +01:00
migrated := 0
2019-12-28 00:27:59 +06:00
for {
if err := sess.Begin(); err != nil {
return fmt.Errorf("session.Begin: %w", err)
2019-12-28 00:27:59 +06:00
}
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]: %w", start, err)
2019-12-28 00:27:59 +06:00
}
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 stat, err := os.Stat(filepath.Join(setting.Avatar.Storage.Path, oldAvatar)); err != nil || !stat.Mode().IsRegular() {
2019-12-27 22:57:38 +00:00
if err == nil {
err = fmt.Errorf("Error: \"%s\" is not a regular file", oldAvatar)
}
2019-12-28 00:27:59 +06:00
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] %w", user.LowerName, err)
2019-12-28 00:27:59 +06:00
} 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: %w", user.LowerName, err)
2019-12-28 00:27:59 +06:00
}
deleteList.Add(filepath.Join(setting.Avatar.Storage.Path, oldAvatar))
2020-05-29 23:18:24 +01:00
migrated++
select {
case <-ticker.C:
log.Info(
"%d/%d (%2.0f%%) User Avatar(s) migrated (%d old avatars to be deleted) in %d batches. %d Remaining ...",
migrated,
count,
float64(migrated)/float64(count)*100,
len(deleteList),
int(math.Ceil(float64(migrated)/float64(50))),
count-int64(migrated))
default:
}
2019-12-28 00:27:59 +06:00
}
if err := sess.Commit(); err != nil {
_ = sess.Rollback()
return fmt.Errorf("commit session: %w", err)
2019-12-28 00:27:59 +06:00
}
}
2020-05-29 23:18:24 +01:00
deleteCount := len(deleteList)
log.Info("Deleting %d old avatars ...", deleteCount)
i := 0
2019-12-28 00:27:59 +06:00
for file := range deleteList {
if err := util.Remove(file); err != nil {
log.Warn("util.Remove: %v", err)
2019-12-28 00:27:59 +06:00
}
2020-05-29 23:18:24 +01:00
i++
select {
case <-ticker.C:
log.Info(
"%d/%d (%2.0f%%) Old User Avatar(s) deleted. %d Remaining ...",
i,
deleteCount,
float64(i)/float64(deleteCount)*100,
deleteCount-i)
default:
}
2019-12-28 00:27:59 +06:00
}
2020-05-29 23:18:24 +01:00
log.Info("Completed migrating %d User Avatar(s) and deleting %d Old Avatars", count, deleteCount)
2019-12-28 00:27:59 +06:00
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.Avatar.Storage.Path, oldAvatar))
2019-12-28 00:27:59 +06:00
if err != nil {
return "", fmt.Errorf("os.Open: %w", err)
2019-12-28 00:27:59 +06:00
}
defer fr.Close()
data, err := io.ReadAll(fr)
2019-12-28 00:27:59 +06:00
if err != nil {
return "", fmt.Errorf("io.ReadAll: %w", err)
2019-12-28 00:27:59 +06:00
}
2025-06-18 03:48:09 +02:00
newAvatar := fmt.Sprintf("%x", md5.Sum(fmt.Appendf(nil, "%d-%x", userID, md5.Sum(data))))
2019-12-28 00:27:59 +06:00
if newAvatar == oldAvatar {
return newAvatar, nil
}
if err := os.WriteFile(filepath.Join(setting.Avatar.Storage.Path, newAvatar), data, 0o666); err != nil {
return "", fmt.Errorf("os.WriteFile: %w", err)
2019-12-28 00:27:59 +06:00
}
return newAvatar, nil
}