diff options
Diffstat (limited to 'internal/db')
-rw-r--r-- | internal/db/repo.go | 2 | ||||
-rw-r--r-- | internal/db/user.go | 40 | ||||
-rw-r--r-- | internal/db/users.go | 34 | ||||
-rw-r--r-- | internal/db/users_test.go | 67 |
4 files changed, 102 insertions, 41 deletions
diff --git a/internal/db/repo.go b/internal/db/repo.go index 7c307144..9f889bc2 100644 --- a/internal/db/repo.go +++ b/internal/db/repo.go @@ -336,7 +336,7 @@ func (repo *Repository) UploadAvatar(data []byte) error { } defer fw.Close() - m := resize.Resize(avatar.AVATAR_SIZE, avatar.AVATAR_SIZE, img, resize.NearestNeighbor) + m := resize.Resize(avatar.DefaultSize, avatar.DefaultSize, img, resize.NearestNeighbor) if err = png.Encode(fw, m); err != nil { return fmt.Errorf("encode image: %v", err) } diff --git a/internal/db/user.go b/internal/db/user.go index bf432a6a..6a2aa296 100644 --- a/internal/db/user.go +++ b/internal/db/user.go @@ -5,27 +5,22 @@ package db import ( - "bytes" "context" "encoding/hex" "fmt" - "image" _ "image/jpeg" - "image/png" "os" "path/filepath" "strings" "time" "unicode/utf8" - "github.com/nfnt/resize" "github.com/unknwon/com" log "unknwon.dev/clog/v2" "xorm.io/xorm" "github.com/gogs/git-module" - "gogs.io/gogs/internal/avatar" "gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/db/errors" "gogs.io/gogs/internal/errutil" @@ -58,41 +53,6 @@ func (u *User) AfterSet(colName string, _ xorm.Cell) { } } -// UploadAvatar saves custom avatar for user. -// FIXME: split uploads to different subdirs in case we have massive number of users. -func (u *User) UploadAvatar(data []byte) error { - img, _, err := image.Decode(bytes.NewReader(data)) - if err != nil { - return fmt.Errorf("decode image: %v", err) - } - - _ = os.MkdirAll(conf.Picture.AvatarUploadPath, os.ModePerm) - fw, err := os.Create(userutil.CustomAvatarPath(u.ID)) - if err != nil { - return fmt.Errorf("create custom avatar directory: %v", err) - } - defer fw.Close() - - m := resize.Resize(avatar.AVATAR_SIZE, avatar.AVATAR_SIZE, img, resize.NearestNeighbor) - if err = png.Encode(fw, m); err != nil { - return fmt.Errorf("encode image: %v", err) - } - - return nil -} - -// DeleteAvatar deletes the user's custom avatar. -func (u *User) DeleteAvatar() error { - avatarPath := userutil.CustomAvatarPath(u.ID) - log.Trace("DeleteAvatar [%d]: %s", u.ID, avatarPath) - if err := os.Remove(avatarPath); err != nil { - return err - } - - u.UseCustomAvatar = false - return UpdateUser(u) -} - // IsAdminOfRepo returns true if user has admin or higher access of repository. func (u *User) IsAdminOfRepo(repo *Repository) bool { return Perms.Authorize(context.TODO(), u.ID, repo.ID, AccessModeAdmin, diff --git a/internal/db/users.go b/internal/db/users.go index fa327157..ca755fc6 100644 --- a/internal/db/users.go +++ b/internal/db/users.go @@ -7,6 +7,7 @@ package db import ( "context" "fmt" + "os" "strings" "time" @@ -45,6 +46,9 @@ type UsersStore interface { // ErrUserAlreadyExist when a user with same name already exists, or // ErrEmailAlreadyUsed if the email has been used by another user. Create(ctx context.Context, username, email string, opts CreateUserOptions) (*User, error) + // DeleteCustomAvatar deletes the current user custom avatar and falls back to + // use look up avatar by email. + DeleteCustomAvatar(ctx context.Context, userID int64) error // GetByEmail returns the user (not organization) with given email. It ignores // records with unverified emails and returns ErrUserNotExist when not found. GetByEmail(ctx context.Context, email string) (*User, error) @@ -64,6 +68,8 @@ type UsersStore interface { // Results are paginated by given page and page size, and sorted by the time of // follow in descending order. ListFollowings(ctx context.Context, userID int64, page, pageSize int) ([]*User, error) + // UseCustomAvatar uses the given avatar as the user custom avatar. + UseCustomAvatar(ctx context.Context, userID int64, avatar []byte) error } var Users UsersStore @@ -267,6 +273,18 @@ func (db *users) Create(ctx context.Context, username, email string, opts Create return user, db.WithContext(ctx).Create(user).Error } +func (db *users) DeleteCustomAvatar(ctx context.Context, userID int64) error { + _ = os.Remove(userutil.CustomAvatarPath(userID)) + return db.WithContext(ctx). + Model(&User{}). + Where("id = ?", userID). + Updates(map[string]interface{}{ + "use_custom_avatar": false, + "updated_unix": db.NowFunc().Unix(), + }). + Error +} + var _ errutil.NotFound = (*ErrUserNotExist)(nil) type ErrUserNotExist struct { @@ -397,6 +415,22 @@ func (db *users) ListFollowings(ctx context.Context, userID int64, page, pageSiz return users, tx.Find(&users).Error } +func (db *users) UseCustomAvatar(ctx context.Context, userID int64, avatar []byte) error { + err := userutil.SaveAvatar(userID, avatar) + if err != nil { + return errors.Wrap(err, "save avatar") + } + + return db.WithContext(ctx). + Model(&User{}). + Where("id = ?", userID). + Updates(map[string]interface{}{ + "use_custom_avatar": true, + "updated_unix": db.NowFunc().Unix(), + }). + Error +} + // UserType indicates the type of the user account. type UserType int diff --git a/internal/db/users_test.go b/internal/db/users_test.go index 1a33151a..efd5681c 100644 --- a/internal/db/users_test.go +++ b/internal/db/users_test.go @@ -7,6 +7,7 @@ package db import ( "context" "fmt" + "os" "testing" "time" @@ -16,6 +17,9 @@ import ( "gogs.io/gogs/internal/auth" "gogs.io/gogs/internal/dbtest" "gogs.io/gogs/internal/errutil" + "gogs.io/gogs/internal/osutil" + "gogs.io/gogs/internal/userutil" + "gogs.io/gogs/public" ) func TestUsers(t *testing.T) { @@ -35,12 +39,14 @@ func TestUsers(t *testing.T) { }{ {"Authenticate", usersAuthenticate}, {"Create", usersCreate}, + {"DeleteCustomAvatar", usersDeleteCustomAvatar}, {"GetByEmail", usersGetByEmail}, {"GetByID", usersGetByID}, {"GetByUsername", usersGetByUsername}, {"HasForkedRepository", usersHasForkedRepository}, {"ListFollowers", usersListFollowers}, {"ListFollowings", usersListFollowings}, + {"UseCustomAvatar", usersUseCustomAvatar}, } { t.Run(tc.name, func(t *testing.T) { t.Cleanup(func() { @@ -186,6 +192,42 @@ func usersCreate(t *testing.T, db *users) { assert.Equal(t, db.NowFunc().Format(time.RFC3339), user.Updated.UTC().Format(time.RFC3339)) } +func usersDeleteCustomAvatar(t *testing.T, db *users) { + ctx := context.Background() + + alice, err := db.Create(ctx, "alice", "alice@example.com", CreateUserOptions{}) + require.NoError(t, err) + + avatar, err := public.Files.ReadFile("img/avatar_default.png") + require.NoError(t, err) + + avatarPath := userutil.CustomAvatarPath(alice.ID) + _ = os.Remove(avatarPath) + defer func() { _ = os.Remove(avatarPath) }() + + err = db.UseCustomAvatar(ctx, alice.ID, avatar) + require.NoError(t, err) + + // Make sure avatar is saved and the user flag is updated. + got := osutil.IsFile(avatarPath) + assert.True(t, got) + + alice, err = db.GetByID(ctx, alice.ID) + require.NoError(t, err) + assert.True(t, alice.UseCustomAvatar) + + // Delete avatar should remove the file and revert the user flag. + err = db.DeleteCustomAvatar(ctx, alice.ID) + require.NoError(t, err) + + got = osutil.IsFile(avatarPath) + assert.False(t, got) + + alice, err = db.GetByID(ctx, alice.ID) + require.NoError(t, err) + assert.False(t, alice.UseCustomAvatar) +} + func usersGetByEmail(t *testing.T, db *users) { ctx := context.Background() @@ -366,3 +408,28 @@ func usersListFollowings(t *testing.T, db *users) { require.Len(t, got, 1) assert.Equal(t, alice.ID, got[0].ID) } + +func usersUseCustomAvatar(t *testing.T, db *users) { + ctx := context.Background() + + alice, err := db.Create(ctx, "alice", "alice@example.com", CreateUserOptions{}) + require.NoError(t, err) + + avatar, err := public.Files.ReadFile("img/avatar_default.png") + require.NoError(t, err) + + avatarPath := userutil.CustomAvatarPath(alice.ID) + _ = os.Remove(avatarPath) + defer func() { _ = os.Remove(avatarPath) }() + + err = db.UseCustomAvatar(ctx, alice.ID, avatar) + require.NoError(t, err) + + // Make sure avatar is saved and the user flag is updated. + got := osutil.IsFile(avatarPath) + assert.True(t, got) + + alice, err = db.GetByID(ctx, alice.ID) + require.NoError(t, err) + assert.True(t, alice.UseCustomAvatar) +} |