@@ -9,18 +9,21 @@ import (
99 "github.com/gin-gonic/gin"
1010 "github.com/gotify/server/v3/auth"
1111 "github.com/gotify/server/v3/auth/password"
12+ "github.com/gotify/server/v3/database"
1213 "github.com/gotify/server/v3/model"
1314)
1415
16+ var errCannotDeleteLastAdmin = errors .New ("cannot delete last admin" )
17+
1518// The UserDatabase interface for encapsulating database access.
16- type UserDatabase interface {
17- GetUsers () ([]* model.User , error )
19+ type UserDatabase [T UserDatabase [T ]] interface {
20+ Txn (fn func (txdb T ) error ) error
21+ GetUsers (condition ... any ) ([]* model.User , error )
1822 GetUserByID (id uint ) (* model.User , error )
1923 GetUserByName (name string ) (* model.User , error )
2024 DeleteUserByID (id uint ) error
2125 UpdateUser (user * model.User ) error
2226 CreateUser (user * model.User ) error
23- CountUser (condition ... any ) (int64 , error )
2427}
2528
2629// UserChangeNotifier notifies listeners for user changes.
@@ -59,7 +62,7 @@ func (c *UserChangeNotifier) fireUserAdded(uid uint) error {
5962
6063// The UserAPI provides handlers for managing users.
6164type UserAPI struct {
62- DB UserDatabase
65+ DB database. GormDatabase
6366 PasswordStrength int
6467 UserChangeNotifier * UserChangeNotifier
6568 Registration bool
@@ -343,19 +346,26 @@ func (a *UserAPI) DeleteUserByID(ctx *gin.Context) {
343346 return
344347 }
345348 if user != nil {
346- adminCount , err := a .DB .CountUser (& model.User {Admin : true })
347- if success := successOrAbort (ctx , 500 , err ); ! success {
348- return
349- }
350- if user .Admin && adminCount == 1 {
351- ctx .AbortWithError (400 , errors .New ("cannot delete last admin" ))
352- return
353- }
354- if err := a .UserChangeNotifier .fireUserDeleted (id ); err != nil {
355- ctx .AbortWithError (500 , err )
356- return
349+ for range 3 {
350+ err = a .DB .Txn (func (txdb * database.GormDatabase ) error {
351+ if err := txdb .DeleteUserByID (id ); err != nil {
352+ return err
353+ }
354+ anotherAdmin , err := txdb .GetUsers (& model.User {Admin : true })
355+ if err != nil {
356+ return err
357+ }
358+ if user .Admin && len (anotherAdmin ) == 0 {
359+ ctx .AbortWithError (400 , errCannotDeleteLastAdmin )
360+ return errCannotDeleteLastAdmin
361+ }
362+ return a .UserChangeNotifier .fireUserDeleted (id )
363+ })
364+ if err == nil || ctx .IsAborted () {
365+ return
366+ }
357367 }
358- successOrAbort (ctx , 500 , a . DB . DeleteUserByID ( id ) )
368+ successOrAbort (ctx , 500 , err )
359369 } else {
360370 ctx .AbortWithError (404 , errors .New ("user does not exist" ))
361371 }
@@ -470,15 +480,7 @@ func (a *UserAPI) UpdateUserByID(ctx *gin.Context) {
470480 return
471481 }
472482 if dbUser != nil {
473- adminCount , err := a .DB .CountUser (& model.User {Admin : true })
474- if success := successOrAbort (ctx , 500 , err ); ! success {
475- return
476- }
477- if ! updatedUser .Admin && dbUser .Admin && adminCount == 1 {
478- ctx .AbortWithError (400 , errors .New ("cannot delete last admin" ))
479- return
480- }
481-
483+ dbUserWasAdmin := dbUser .Admin
482484 dbUser .Name = updatedUser .Name
483485 dbUser .Admin = updatedUser .Admin
484486
@@ -494,10 +496,30 @@ func (a *UserAPI) UpdateUserByID(ctx *gin.Context) {
494496 }
495497 dbUser .Pass = pw
496498 }
497- if success := successOrAbort (ctx , 500 , a .DB .UpdateUser (dbUser )); ! success {
498- return
499+
500+ for range 3 {
501+ err = a .DB .Txn (func (txdb * database.GormDatabase ) error {
502+ if err := txdb .UpdateUser (dbUser ); err != nil {
503+ return err
504+ }
505+
506+ anotherAdmin , err := txdb .GetUsers (& model.User {Admin : true })
507+ if err != nil {
508+ return err
509+ }
510+ if ! updatedUser .Admin && dbUserWasAdmin && len (anotherAdmin ) == 0 {
511+ ctx .AbortWithError (400 , errCannotDeleteLastAdmin )
512+ return errCannotDeleteLastAdmin
513+ }
514+
515+ return nil
516+ })
517+
518+ if err == nil || ctx .IsAborted () {
519+ return
520+ }
499521 }
500- ctx . JSON ( 200 , toExternalUser ( dbUser ) )
522+ successOrAbort ( ctx , 500 , err )
501523 } else {
502524 ctx .AbortWithError (404 , errors .New ("user does not exist" ))
503525 }
0 commit comments