refactor(api/v2): dedup the admin user-mutation handlers
The patch-admin, patch-status and delete-user handlers each repeated the same session open/load/commit/rollback scaffold. Extract it into adminMutateUser, which owns the transaction and takes a closure for each handler's distinct guard-and-write step.
This commit is contained in:
parent
5579daa452
commit
5b3ee89edd
|
|
@ -138,28 +138,18 @@ func adminUsersPatchAdmin(_ context.Context, in *struct {
|
||||||
return nil, translateDomainError(models.ErrInvalidData{Message: "is_admin is required"})
|
return nil, translateDomainError(models.ErrInvalidData{Message: "is_admin is required"})
|
||||||
}
|
}
|
||||||
|
|
||||||
s := db.NewSession()
|
target, err := adminMutateUser(in.ID, func(s *xorm.Session, target *user.User) error {
|
||||||
defer s.Close()
|
if !*in.Body.IsAdmin {
|
||||||
|
if err := user.GuardLastAdmin(s, target); err != nil {
|
||||||
target, err := adminLoadUser(s, in.ID)
|
return err
|
||||||
if err != nil {
|
}
|
||||||
return nil, translateDomainError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !*in.Body.IsAdmin {
|
|
||||||
if err := user.GuardLastAdmin(s, target); err != nil {
|
|
||||||
_ = s.Rollback()
|
|
||||||
return nil, translateDomainError(err)
|
|
||||||
}
|
}
|
||||||
}
|
target.IsAdmin = *in.Body.IsAdmin
|
||||||
|
_, err := s.ID(target.ID).Cols("is_admin").Update(target)
|
||||||
target.IsAdmin = *in.Body.IsAdmin
|
return err
|
||||||
if _, err := s.ID(target.ID).Cols("is_admin").Update(target); err != nil {
|
})
|
||||||
_ = s.Rollback()
|
if err != nil {
|
||||||
return nil, translateDomainError(err)
|
return nil, err
|
||||||
}
|
|
||||||
if err := s.Commit(); err != nil {
|
|
||||||
return nil, translateDomainError(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return adminUserResponse(target) //nolint:contextcheck // see adminUserResponse.
|
return adminUserResponse(target) //nolint:contextcheck // see adminUserResponse.
|
||||||
|
|
@ -177,28 +167,17 @@ func adminUsersPatchStatus(_ context.Context, in *struct {
|
||||||
return nil, translateDomainError(models.ErrInvalidData{Message: "invalid status"})
|
return nil, translateDomainError(models.ErrInvalidData{Message: "invalid status"})
|
||||||
}
|
}
|
||||||
|
|
||||||
s := db.NewSession()
|
target, err := adminMutateUser(in.ID, func(s *xorm.Session, target *user.User) error {
|
||||||
defer s.Close()
|
// Any non-Active status blocks login, so moving an admin out of Active is equivalent to demotion.
|
||||||
|
if target.IsAdmin && newStatus != user.StatusActive {
|
||||||
target, err := adminLoadUser(s, in.ID)
|
if err := user.GuardLastAdmin(s, target); err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return nil, translateDomainError(err)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Any non-Active status blocks login, so moving an admin out of Active is equivalent to demotion.
|
|
||||||
if target.IsAdmin && newStatus != user.StatusActive {
|
|
||||||
if err := user.GuardLastAdmin(s, target); err != nil {
|
|
||||||
_ = s.Rollback()
|
|
||||||
return nil, translateDomainError(err)
|
|
||||||
}
|
}
|
||||||
}
|
return user.SetUserStatus(s, target, newStatus)
|
||||||
|
})
|
||||||
if err := user.SetUserStatus(s, target, newStatus); err != nil {
|
if err != nil {
|
||||||
_ = s.Rollback()
|
return nil, err
|
||||||
return nil, translateDomainError(err)
|
|
||||||
}
|
|
||||||
if err := s.Commit(); err != nil {
|
|
||||||
return nil, translateDomainError(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh locally since GetUserByID refuses disabled accounts.
|
// Refresh locally since GetUserByID refuses disabled accounts.
|
||||||
|
|
@ -218,33 +197,41 @@ func adminUsersDelete(_ context.Context, in *struct {
|
||||||
return nil, translateDomainError(models.ErrInvalidData{Message: "invalid mode, expected 'now' or 'scheduled'"})
|
return nil, translateDomainError(models.ErrInvalidData{Message: "invalid mode, expected 'now' or 'scheduled'"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, err := adminMutateUser(in.ID, func(s *xorm.Session, target *user.User) error {
|
||||||
|
if err := user.GuardLastAdmin(s, target); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if mode == "now" {
|
||||||
|
return models.DeleteUser(s, target)
|
||||||
|
}
|
||||||
|
return user.RequestDeletion(s, target)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &emptyBody{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// adminMutateUser opens a session, loads the user by ID, runs mutate against it,
|
||||||
|
// then commits — owning the transaction so each handler only supplies its
|
||||||
|
// distinct guard-and-write step. mutate must not commit or rollback. Errors
|
||||||
|
// (load, mutate, commit) are translated to RFC 9457 responses.
|
||||||
|
func adminMutateUser(id int64, mutate func(s *xorm.Session, target *user.User) error) (*user.User, error) {
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
target, err := adminLoadUser(s, in.ID)
|
target, err := adminLoadUser(s, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, translateDomainError(err)
|
return nil, translateDomainError(err)
|
||||||
}
|
}
|
||||||
|
if err := mutate(s, target); err != nil {
|
||||||
if err := user.GuardLastAdmin(s, target); err != nil {
|
|
||||||
_ = s.Rollback()
|
_ = s.Rollback()
|
||||||
return nil, translateDomainError(err)
|
return nil, translateDomainError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if mode == "now" {
|
|
||||||
err = models.DeleteUser(s, target)
|
|
||||||
} else {
|
|
||||||
err = user.RequestDeletion(s, target)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
_ = s.Rollback()
|
|
||||||
return nil, translateDomainError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.Commit(); err != nil {
|
if err := s.Commit(); err != nil {
|
||||||
return nil, translateDomainError(err)
|
return nil, translateDomainError(err)
|
||||||
}
|
}
|
||||||
return &emptyBody{}, nil
|
return target, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// adminLoadUser fetches a user by ID, returning ErrUserDoesNotExist for an
|
// adminLoadUser fetches a user by ID, returning ErrUserDoesNotExist for an
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue