mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-05-31 22:58:32 +00:00
optimistic updates for account state
This commit is contained in:
parent
c10248be6d
commit
36d9b2885d
|
@ -28,7 +28,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||
)
|
||||
|
||||
func (p *Processor) AccountApprove(
|
||||
|
@ -47,14 +46,13 @@ func (p *Processor) AccountApprove(
|
|||
return nil, gtserror.NewErrorNotFound(err, err.Error())
|
||||
}
|
||||
|
||||
if !*user.Approved {
|
||||
// Mark user as approved.
|
||||
user.Approved = util.Ptr(true)
|
||||
if err := p.state.DB.UpdateUser(ctx, user, "approved"); err != nil {
|
||||
err := gtserror.Newf("db error updating user %s: %w", user.ID, err)
|
||||
return nil, gtserror.NewErrorInternalError(err)
|
||||
}
|
||||
// Get a lock on the account URI,
|
||||
// to ensure it's not also being
|
||||
// rejected at the same time!
|
||||
unlock := p.state.ClientLocks.Lock(user.Account.URI)
|
||||
defer unlock()
|
||||
|
||||
if !*user.Approved {
|
||||
// Process approval side effects asynschronously.
|
||||
p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{
|
||||
APObjectType: ap.ActorPerson,
|
||||
|
@ -71,5 +69,11 @@ func (p *Processor) AccountApprove(
|
|||
return nil, gtserror.NewErrorInternalError(err)
|
||||
}
|
||||
|
||||
// Optimistically set approved to true and
|
||||
// clear sign-up IP to reflect state that
|
||||
// will be produced by side effects.
|
||||
apiAccount.Approved = true
|
||||
apiAccount.IP = nil
|
||||
|
||||
return apiAccount, nil
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
type AdminApproveTestSuite struct {
|
||||
|
@ -53,13 +54,20 @@ func (suite *AdminApproveTestSuite) TestApprove() {
|
|||
// Account should be approved.
|
||||
suite.NotNil(acct)
|
||||
suite.True(acct.Approved)
|
||||
suite.Nil(acct.IP)
|
||||
|
||||
// Check DB entry too.
|
||||
dbUser, err := suite.state.DB.GetUserByID(ctx, targetUser.ID)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
// Wait for processor to
|
||||
// handle side effects.
|
||||
var (
|
||||
dbUser *gtsmodel.User
|
||||
err error
|
||||
)
|
||||
if !testrig.WaitFor(func() bool {
|
||||
dbUser, err = suite.state.DB.GetUserByID(ctx, targetUser.ID)
|
||||
return err == nil && dbUser != nil && *dbUser.Approved
|
||||
}) {
|
||||
suite.FailNow("waiting for approved user")
|
||||
}
|
||||
suite.True(*dbUser.Approved)
|
||||
}
|
||||
|
||||
func TestAdminApproveTestSuite(t *testing.T) {
|
|
@ -71,18 +71,12 @@ func (p *Processor) AccountReject(
|
|||
return nil, gtserror.NewErrorInternalError(err)
|
||||
}
|
||||
|
||||
// Remove the account.
|
||||
if err := p.state.DB.DeleteAccount(ctx, accountID); err != nil {
|
||||
err := gtserror.Newf("db error deleting account %s: %w", accountID, err)
|
||||
return nil, gtserror.NewErrorInternalError(err)
|
||||
}
|
||||
|
||||
// Remove the user.
|
||||
if err := p.state.DB.DeleteUserByID(ctx, user.ID); err != nil {
|
||||
err := gtserror.Newf("db error deleting user %s: %w", user.ID, err)
|
||||
return nil, gtserror.NewErrorInternalError(err)
|
||||
}
|
||||
// Set approved to false on the API model, to
|
||||
// reflect the changes that will occur
|
||||
// asynchronously in the processor.
|
||||
apiAccount.Approved = false
|
||||
|
||||
// Ensure we an email address.
|
||||
var email string
|
||||
if user.Email != "" {
|
||||
email = user.Email
|
||||
|
@ -90,7 +84,8 @@ func (p *Processor) AccountReject(
|
|||
email = user.UnconfirmedEmail
|
||||
}
|
||||
|
||||
// Create and store a denied user entry.
|
||||
// Create a denied user entry for
|
||||
// the worker to process + store.
|
||||
deniedUser := >smodel.DeniedUser{
|
||||
ID: user.ID,
|
||||
Email: email,
|
||||
|
@ -105,11 +100,6 @@ func (p *Processor) AccountReject(
|
|||
Message: message,
|
||||
}
|
||||
|
||||
if err := p.state.DB.PutDeniedUser(ctx, deniedUser); err != nil {
|
||||
err := gtserror.Newf("db error putting denied user %s: %w", deniedUser.ID, err)
|
||||
return nil, gtserror.NewErrorInternalError(err)
|
||||
}
|
||||
|
||||
// Process rejection side effects asynschronously.
|
||||
p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{
|
||||
APObjectType: ap.ActorPerson,
|
||||
|
|
|
@ -23,6 +23,8 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
type AdminRejectTestSuite struct {
|
||||
|
@ -51,13 +53,20 @@ func (suite *AdminRejectTestSuite) TestReject() {
|
|||
if errWithCode != nil {
|
||||
suite.FailNow(errWithCode.Error())
|
||||
}
|
||||
|
||||
suite.NotNil(acct)
|
||||
suite.False(acct.Approved)
|
||||
|
||||
// Should be a denied user entry now.
|
||||
deniedUser, err := suite.state.DB.GetDeniedUserByID(ctx, targetUser.ID)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
// Wait for processor to
|
||||
// handle side effects.
|
||||
var (
|
||||
deniedUser *gtsmodel.DeniedUser
|
||||
err error
|
||||
)
|
||||
if !testrig.WaitFor(func() bool {
|
||||
deniedUser, err = suite.state.DB.GetDeniedUserByID(ctx, targetUser.ID)
|
||||
return deniedUser != nil && err == nil
|
||||
}) {
|
||||
suite.FailNow("waiting for denied user")
|
||||
}
|
||||
|
||||
// Ensure fields as expected.
|
||||
|
|
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/processing/account"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||
)
|
||||
|
||||
// clientAPI wraps processing functions
|
||||
|
@ -700,6 +701,15 @@ func (p *clientAPI) AcceptAccount(ctx context.Context, cMsg messages.FromClientA
|
|||
return gtserror.Newf("%T not parseable as *gtsmodel.User", cMsg.GTSModel)
|
||||
}
|
||||
|
||||
// Mark user as approved + clear sign-up IP.
|
||||
newUser.Approved = util.Ptr(true)
|
||||
newUser.SignUpIP = nil
|
||||
if err := p.state.DB.UpdateUser(ctx, newUser, "approved", "sign_up_ip"); err != nil {
|
||||
// Error now means we should return without
|
||||
// sending email + let admin try to approve again.
|
||||
return gtserror.Newf("db error updating user %s: %w", newUser.ID, err)
|
||||
}
|
||||
|
||||
// Send "your sign-up has been approved" email to the new user.
|
||||
if err := p.surface.emailUserSignupApproved(ctx, newUser); err != nil {
|
||||
log.Errorf(ctx, "error emailing: %v", err)
|
||||
|
@ -709,20 +719,40 @@ func (p *clientAPI) AcceptAccount(ctx context.Context, cMsg messages.FromClientA
|
|||
}
|
||||
|
||||
func (p *clientAPI) RejectAccount(ctx context.Context, cMsg messages.FromClientAPI) error {
|
||||
denied, ok := cMsg.GTSModel.(*gtsmodel.DeniedUser)
|
||||
deniedUser, ok := cMsg.GTSModel.(*gtsmodel.DeniedUser)
|
||||
if !ok {
|
||||
return gtserror.Newf("%T not parseable as *gtsmodel.DeniedUser", cMsg.GTSModel)
|
||||
}
|
||||
|
||||
if !*denied.SendEmail {
|
||||
// No need to send an
|
||||
// email. Nothing to do.
|
||||
return nil
|
||||
// Remove the account.
|
||||
if err := p.state.DB.DeleteAccount(ctx, cMsg.TargetAccount.ID); err != nil {
|
||||
log.Errorf(ctx,
|
||||
"db error deleting account %s: %v",
|
||||
cMsg.TargetAccount.ID, err,
|
||||
)
|
||||
}
|
||||
|
||||
// Send "your sign-up has been rejected" email to the denied user.
|
||||
if err := p.surface.emailUserSignupRejected(ctx, denied); err != nil {
|
||||
log.Errorf(ctx, "error emailing: %v", err)
|
||||
// Remove the user.
|
||||
if err := p.state.DB.DeleteUserByID(ctx, deniedUser.ID); err != nil {
|
||||
log.Errorf(ctx,
|
||||
"db error deleting user %s: %v",
|
||||
deniedUser.ID, err,
|
||||
)
|
||||
}
|
||||
|
||||
// Store the deniedUser entry.
|
||||
if err := p.state.DB.PutDeniedUser(ctx, deniedUser); err != nil {
|
||||
log.Errorf(ctx,
|
||||
"db error putting denied user %s: %v",
|
||||
deniedUser.ID, err,
|
||||
)
|
||||
}
|
||||
|
||||
if *deniedUser.SendEmail {
|
||||
// Send "your sign-up has been rejected" email to the denied user.
|
||||
if err := p.surface.emailUserSignupRejected(ctx, deniedUser); err != nil {
|
||||
log.Errorf(ctx, "error emailing: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
Loading…
Reference in a new issue