mirror of
https://github.com/muety/wakapi.git
synced 2025-12-05 22:20:24 -08:00
fix: delete user cache keys upon user deletion (resolve #853)
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@ package mocks
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/mock"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type BaseRepositoryMock struct {
|
||||
@@ -23,5 +24,10 @@ func (m *BaseRepositoryMock) GetTableDDLSqlite(s string) (string, error) {
|
||||
return args.Get(0).(string), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *BaseRepositoryMock) RunInTx(f func(db *gorm.DB) error) error {
|
||||
args := m.Called(f)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *BaseRepositoryMock) VacuumOrOptimize() {
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package mocks
|
||||
import (
|
||||
"github.com/muety/wakapi/models"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type KeyValueServiceMock struct {
|
||||
@@ -34,6 +35,21 @@ func (m *KeyValueServiceMock) DeleteString(s string) error {
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *KeyValueServiceMock) DeleteStringTx(s string, d *gorm.DB) error {
|
||||
args := m.Called(s, d)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *KeyValueServiceMock) DeleteWildcard(s string) error {
|
||||
args := m.Called(s)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *KeyValueServiceMock) DeleteWildcardTx(s string, d *gorm.DB) error {
|
||||
args := m.Called(s, d)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *KeyValueServiceMock) ReplaceKeySuffix(s1, s2 string) error {
|
||||
args := m.Called(s1, s2)
|
||||
return args.Error(0)
|
||||
|
||||
@@ -110,11 +110,6 @@ func (m *UserServiceMock) ResetApiKey(user *models.User) (*models.User, error) {
|
||||
return args.Get(0).(*models.User), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *UserServiceMock) ToggleBadges(user *models.User) (*models.User, error) {
|
||||
args := m.Called(user)
|
||||
return args.Get(0).(*models.User), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *UserServiceMock) SetWakatimeApiCredentials(user *models.User, s1, s2 string) (*models.User, error) {
|
||||
args := m.Called(user, s1, s2)
|
||||
return args.Get(0).(*models.User), args.Error(1)
|
||||
|
||||
@@ -49,6 +49,10 @@ func (r *BaseRepository) GetTableDDLSqlite(tableName string) (result string, err
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (r *BaseRepository) RunInTx(f func(tx *gorm.DB) error) error {
|
||||
return r.db.Transaction(f)
|
||||
}
|
||||
|
||||
func (r *BaseRepository) VacuumOrOptimize() {
|
||||
// sqlite and postgres require manual vacuuming regularly to reclaim free storage from deleted records
|
||||
// see https://www.postgresql.org/docs/current/sql-vacuum.html and https://www.sqlite.org/lang_vacuum.html
|
||||
|
||||
@@ -3,6 +3,7 @@ package repositories
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/muety/wakapi/models"
|
||||
"github.com/muety/wakapi/utils"
|
||||
@@ -65,13 +66,15 @@ func (r *KeyValueRepository) PutString(kv *models.KeyStringValue) error {
|
||||
}
|
||||
|
||||
func (r *KeyValueRepository) DeleteString(key string) error {
|
||||
result := r.db.
|
||||
Delete(&models.KeyStringValue{}, &models.KeyStringValue{Key: key})
|
||||
return r.DeleteStringTx(key, r.db)
|
||||
}
|
||||
|
||||
func (r *KeyValueRepository) DeleteStringTx(key string, tx *gorm.DB) error {
|
||||
result := tx.Delete(&models.KeyStringValue{}, &models.KeyStringValue{Key: key})
|
||||
|
||||
if err := result.Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if result.RowsAffected != 1 {
|
||||
return errors.New("nothing deleted")
|
||||
}
|
||||
@@ -79,6 +82,16 @@ func (r *KeyValueRepository) DeleteString(key string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *KeyValueRepository) DeleteWildcard(pattern string) error {
|
||||
return r.DeleteWildcardTx(pattern, r.db)
|
||||
}
|
||||
|
||||
func (r *KeyValueRepository) DeleteWildcardTx(pattern string, tx *gorm.DB) error {
|
||||
return tx.
|
||||
Where(utils.QuoteSql(r.db, "%s like ?", "key"), strings.ReplaceAll(pattern, "*", "%")).
|
||||
Delete(&models.KeyStringValue{}).Error
|
||||
}
|
||||
|
||||
// ReplaceKeySuffix will search for key-value pairs whose key ends with suffixOld and replace it with suffixNew instead.
|
||||
func (r *KeyValueRepository) ReplaceKeySuffix(suffixOld, suffixNew string) error {
|
||||
if dialector := r.db.Dialector.Name(); dialector == "mysql" || dialector == "postgres" {
|
||||
|
||||
@@ -4,12 +4,14 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/muety/wakapi/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type IBaseRepository interface {
|
||||
GetDialector() string
|
||||
GetTableDDLMysql(string) (string, error)
|
||||
GetTableDDLSqlite(string) (string, error)
|
||||
RunInTx(func(*gorm.DB) error) error
|
||||
VacuumOrOptimize()
|
||||
}
|
||||
|
||||
@@ -75,6 +77,9 @@ type IKeyValueRepository interface {
|
||||
GetString(string) (*models.KeyStringValue, error)
|
||||
PutString(*models.KeyStringValue) error
|
||||
DeleteString(string) error
|
||||
DeleteStringTx(string, *gorm.DB) error
|
||||
DeleteWildcard(string) error
|
||||
DeleteWildcardTx(string, *gorm.DB) error
|
||||
Search(string) ([]*models.KeyStringValue, error)
|
||||
ReplaceKeySuffix(string, string) error
|
||||
}
|
||||
@@ -123,6 +128,7 @@ type IUserRepository interface {
|
||||
Update(*models.User) (*models.User, error)
|
||||
UpdateField(*models.User, string, interface{}) (*models.User, error)
|
||||
Delete(*models.User) error
|
||||
DeleteTx(*models.User, *gorm.DB) error
|
||||
}
|
||||
|
||||
type ILeaderboardRepository interface {
|
||||
|
||||
@@ -190,7 +190,11 @@ func (r *UserRepository) UpdateField(user *models.User, key string, value interf
|
||||
}
|
||||
|
||||
func (r *UserRepository) Delete(user *models.User) error {
|
||||
return r.db.Delete(user).Error
|
||||
return r.DeleteTx(user, r.db)
|
||||
}
|
||||
|
||||
func (r *UserRepository) DeleteTx(user *models.User, tx *gorm.DB) error {
|
||||
return tx.Delete(user).Error
|
||||
}
|
||||
|
||||
func (r *UserRepository) getByLoggedIn(t time.Time, after bool) ([]*models.User, error) {
|
||||
|
||||
@@ -785,7 +785,7 @@ func (h *SettingsHandler) actionDeleteUser(w http.ResponseWriter, r *http.Reques
|
||||
user := middlewares.GetPrincipal(r)
|
||||
go func(user *models.User, r *http.Request) {
|
||||
slog.Info("deleting user shortly", "userID", user.ID)
|
||||
time.Sleep(5 * time.Minute)
|
||||
//time.Sleep(5 * time.Minute)
|
||||
if err := h.userSrvc.Delete(user); err != nil {
|
||||
conf.Log().Request(r).Error("failed to delete user", "userID", user.ID, "error", err)
|
||||
} else {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"github.com/muety/wakapi/config"
|
||||
"github.com/muety/wakapi/models"
|
||||
"github.com/muety/wakapi/repositories"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type KeyValueService struct {
|
||||
@@ -45,6 +46,18 @@ func (srv *KeyValueService) DeleteString(key string) error {
|
||||
return srv.repository.DeleteString(key)
|
||||
}
|
||||
|
||||
func (srv *KeyValueService) DeleteStringTx(key string, tx *gorm.DB) error {
|
||||
return srv.repository.DeleteStringTx(key, tx)
|
||||
}
|
||||
|
||||
func (srv *KeyValueService) DeleteWildcard(key string) error {
|
||||
return srv.repository.DeleteWildcard(key)
|
||||
}
|
||||
|
||||
func (srv *KeyValueService) DeleteWildcardTx(key string, tx *gorm.DB) error {
|
||||
return srv.repository.DeleteWildcardTx(key, tx)
|
||||
}
|
||||
|
||||
func (srv *KeyValueService) ReplaceKeySuffix(suffixOld, suffixNew string) error {
|
||||
return srv.repository.ReplaceKeySuffix(suffixOld, suffixNew)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/muety/wakapi/models"
|
||||
"github.com/muety/wakapi/models/types"
|
||||
"github.com/muety/wakapi/utils"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type IAggregationService interface {
|
||||
@@ -68,6 +69,9 @@ type IKeyValueService interface {
|
||||
GetByPrefix(string) ([]*models.KeyStringValue, error)
|
||||
PutString(*models.KeyStringValue) error
|
||||
DeleteString(string) error
|
||||
DeleteStringTx(string, *gorm.DB) error
|
||||
DeleteWildcard(string) error
|
||||
DeleteWildcardTx(string, *gorm.DB) error
|
||||
ReplaceKeySuffix(string, string) error
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/muety/wakapi/repositories"
|
||||
"github.com/muety/wakapi/utils"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type UserService struct {
|
||||
@@ -305,9 +306,18 @@ func (srv *UserService) Delete(user *models.User) error {
|
||||
|
||||
user.ReportsWeekly = false
|
||||
srv.notifyUpdate(user)
|
||||
srv.notifyDelete(user)
|
||||
|
||||
return srv.repository.Delete(user)
|
||||
return srv.repository.RunInTx(func(tx *gorm.DB) error {
|
||||
if err := srv.repository.DeleteTx(user, tx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := srv.keyValueService.DeleteWildcardTx(fmt.Sprintf("*_%s", user.ID), tx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
srv.notifyDelete(user)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (srv *UserService) MapUsersById(users []*models.User) map[string]*models.User {
|
||||
|
||||
Reference in New Issue
Block a user