Skip to content

Commit

Permalink
feat: resendEmailVerificationCode mutation
Browse files Browse the repository at this point in the history
  • Loading branch information
ayaanqui committed Apr 22, 2024
1 parent a81ded1 commit 3d31dbc
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 71 deletions.
99 changes: 95 additions & 4 deletions graph/generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions graph/resolver/user.resolvers.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion graph/user.graphql
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
extend type Mutation {
createAccount(input: CreateAccountInput!): User!
verifyEmail(verificationCode: String!): User!
verifyEmail(verificationCode: String!): User!
resendEmailVerificationCode(email: String!): Boolean!
}

extend type Query {
Expand Down
160 changes: 94 additions & 66 deletions services/auth_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,72 +50,100 @@ func (service Service) CreateEmailVerification(ctx context.Context, user gmodel.
err := query.QueryContext(ctx, service.DbOrTxQueryable(), &email_verification)
return email_verification, err
}

func (service Service) FindEmailVerificationByCode(ctx context.Context, verification_code string) (model.EmailVerification, error) {
qb := table.EmailVerification.
SELECT(table.EmailVerification.AllColumns).
WHERE(table.EmailVerification.Code.EQ(postgres.String(verification_code))).
LIMIT(1)
var email_verification model.EmailVerification
if err := qb.QueryContext(ctx, service.DbOrTxQueryable(), &email_verification); err != nil {
return model.EmailVerification{}, fmt.Errorf("invalid email verification code")
}
return email_verification, nil
}

func (service Service) VerifyUserEmail(ctx context.Context, verification_code string) (gmodel.User, error) {
var err error
service.TX, err = service.DB.BeginTx(ctx, nil)
if err != nil {
service.TX.Rollback()
return gmodel.User{}, err
}

email_verification, err := service.FindEmailVerificationByCode(ctx, verification_code)
if err != nil {
service.TX.Rollback()
return gmodel.User{}, err
}

if time.Until(email_verification.CreatedAt).Abs() > time.Hour {
service.TX.Rollback()
// Delete verification entry since it's expired
del_query := table.EmailVerification.
DELETE().
WHERE(table.EmailVerification.ID.EQ(postgres.Int(email_verification.ID)))
if _, err := del_query.ExecContext(ctx, service.DB); err != nil {
return gmodel.User{}, err
}
return gmodel.User{}, fmt.Errorf("verification code has expired")
}

update := table.User.
UPDATE(table.User.Active, table.User.UpdatedAt).
SET(postgres.Bool(true), postgres.DateT(time.Now())).
WHERE(table.User.ID.EQ(postgres.Int(email_verification.UserID)))
if _, err := update.ExecContext(ctx, service.TX); err != nil {
service.TX.Rollback()
return gmodel.User{}, fmt.Errorf("could not update user email verification status to verified")
}

// Remove email_verification row
delete := table.EmailVerification.
DELETE().
WHERE(postgres.AND(
table.EmailVerification.ID.EQ(postgres.Int(email_verification.ID)),
table.EmailVerification.Code.EQ(postgres.String(verification_code)),
))
if _, err := delete.ExecContext(ctx, service.TX); err != nil {
service.TX.Rollback()
return gmodel.User{}, fmt.Errorf("could not delete email verification entry")
}

if err := service.TX.Commit(); err != nil {
return gmodel.User{}, fmt.Errorf("could not commit changes")
}
service.TX = nil
return service.FindUserById(ctx, email_verification.UserID)
}

func (service Service) ResendEmailVerification(ctx context.Context, user gmodel.User) (email_verification model.EmailVerification, err error) {
if user.Active {
return model.EmailVerification{}, fmt.Errorf("user already has a verified email address")
}
service.TX, err = service.DB.BeginTx(ctx, nil)
if err != nil {
return model.EmailVerification{}, err
}

_, err = table.EmailVerification.DELETE().
WHERE(table.EmailVerification.UserID.EQ(postgres.Int(user.ID))).
ExecContext(ctx, service.TX)
if err != nil {
service.TX.Rollback()
return model.EmailVerification{}, fmt.Errorf("user email verification entry deletion failed")
}

email_verification, err = service.CreateEmailVerification(ctx, user)
if err != nil {
service.TX.Rollback()
return model.EmailVerification{}, err
}
if err := service.TX.Commit(); err != nil {
return model.EmailVerification{}, fmt.Errorf("could not commit changes")
}
return email_verification, nil
}

func (service Service) FindEmailVerificationByCode(ctx context.Context, verification_code string) (model.EmailVerification, error) {
qb := table.EmailVerification.
SELECT(table.EmailVerification.AllColumns).
WHERE(table.EmailVerification.Code.EQ(postgres.String(verification_code))).
LIMIT(1)
var email_verification model.EmailVerification
if err := qb.QueryContext(ctx, service.DbOrTxQueryable(), &email_verification); err != nil {
return model.EmailVerification{}, fmt.Errorf("invalid email verification code")
}
return email_verification, nil
}

func (service Service) VerifyUserEmail(ctx context.Context, verification_code string) (gmodel.User, error) {
var err error
service.TX, err = service.DB.BeginTx(ctx, nil)
if err != nil {
service.TX.Rollback()
return gmodel.User{}, err
}

email_verification, err := service.FindEmailVerificationByCode(ctx, verification_code)
if err != nil {
service.TX.Rollback()
return gmodel.User{}, err
}

if time.Until(email_verification.CreatedAt).Abs() > time.Hour {
service.TX.Rollback()
// Delete verification entry since it's expired
del_query := table.EmailVerification.
DELETE().
WHERE(table.EmailVerification.ID.EQ(postgres.Int(email_verification.ID)))
if _, err := del_query.ExecContext(ctx, service.DB); err != nil {
return gmodel.User{}, err
}
return gmodel.User{}, fmt.Errorf("verification code has expired")
}

update := table.User.
UPDATE(table.User.Active, table.User.UpdatedAt).
SET(postgres.Bool(true), postgres.DateT(time.Now())).
WHERE(table.User.ID.EQ(postgres.Int(email_verification.UserID)))
if _, err := update.ExecContext(ctx, service.TX); err != nil {
service.TX.Rollback()
return gmodel.User{}, fmt.Errorf("could not update user email verification status to verified")
}

// Remove email_verification row
delete := table.EmailVerification.
DELETE().
WHERE(postgres.AND(
table.EmailVerification.ID.EQ(postgres.Int(email_verification.ID)),
table.EmailVerification.Code.EQ(postgres.String(verification_code)),
))
if _, err := delete.ExecContext(ctx, service.TX); err != nil {
service.TX.Rollback()
return gmodel.User{}, fmt.Errorf("could not delete email verification entry")
}

if err := service.TX.Commit(); err != nil {
return gmodel.User{}, fmt.Errorf("could not commit changes")
}
service.TX = nil
return service.FindUserById(ctx, email_verification.UserID)
}

func (Service) HashPassword(password string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
Expand Down

0 comments on commit 3d31dbc

Please sign in to comment.