Add resolver to resend verify email link

Resolves #20
This commit is contained in:
Lakhan Samani 2021-07-18 12:56:17 +05:30
parent e2fc610762
commit 2840a085ca
8 changed files with 193 additions and 11 deletions

View File

@ -22,6 +22,7 @@ type Manager interface {
GetVerificationByToken(token string) (VerificationRequest, error) GetVerificationByToken(token string) (VerificationRequest, error)
DeleteToken(email string) error DeleteToken(email string) error
GetVerificationRequests() ([]VerificationRequest, error) GetVerificationRequests() ([]VerificationRequest, error)
GetVerificationByEmail(email string) (VerificationRequest, error)
} }
type manager struct { type manager struct {

View File

@ -41,6 +41,18 @@ func (mgr *manager) GetVerificationByToken(token string) (VerificationRequest, e
return verification, nil return verification, nil
} }
func (mgr *manager) GetVerificationByEmail(email string) (VerificationRequest, error) {
var verification VerificationRequest
result := mgr.db.Where("email = ?", email).First(&verification)
if result.Error != nil {
log.Println(`Error getting verification token:`, result.Error)
return verification, result.Error
}
return verification, nil
}
func (mgr *manager) DeleteToken(email string) error { func (mgr *manager) DeleteToken(email string) error {
var verification VerificationRequest var verification VerificationRequest
result := mgr.db.Where("email = ?", email).Delete(&verification) result := mgr.db.Where("email = ?", email).Delete(&verification)

View File

@ -56,11 +56,12 @@ type ComplexityRoot struct {
} }
Mutation struct { Mutation struct {
Login func(childComplexity int, params model.LoginInput) int Login func(childComplexity int, params model.LoginInput) int
Logout func(childComplexity int) int Logout func(childComplexity int) int
Signup func(childComplexity int, params model.SignUpInput) int ResendVerifyEmail func(childComplexity int, params model.ResendVerifyEmailInput) int
UpdateProfile func(childComplexity int, params model.UpdateProfileInput) int Signup func(childComplexity int, params model.SignUpInput) int
VerifyEmail func(childComplexity int, params model.VerifyEmailInput) int UpdateProfile func(childComplexity int, params model.UpdateProfileInput) int
VerifyEmail func(childComplexity int, params model.VerifyEmailInput) int
} }
Query struct { Query struct {
@ -104,6 +105,7 @@ type MutationResolver interface {
Logout(ctx context.Context) (*model.Response, error) Logout(ctx context.Context) (*model.Response, error)
UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model.Response, error) UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model.Response, error)
VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.LoginResponse, error) VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.LoginResponse, error)
ResendVerifyEmail(ctx context.Context, params model.ResendVerifyEmailInput) (*model.Response, error)
} }
type QueryResolver interface { type QueryResolver interface {
Users(ctx context.Context) ([]*model.User, error) Users(ctx context.Context) ([]*model.User, error)
@ -188,6 +190,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Mutation.Logout(childComplexity), true return e.complexity.Mutation.Logout(childComplexity), true
case "Mutation.resendVerifyEmail":
if e.complexity.Mutation.ResendVerifyEmail == nil {
break
}
args, err := ec.field_Mutation_resendVerifyEmail_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Mutation.ResendVerifyEmail(childComplexity, args["params"].(model.ResendVerifyEmailInput)), true
case "Mutation.signup": case "Mutation.signup":
if e.complexity.Mutation.Signup == nil { if e.complexity.Mutation.Signup == nil {
break break
@ -504,6 +518,10 @@ input VerifyEmailInput {
token: String! token: String!
} }
input ResendVerifyEmailInput {
email: String!
}
input UpdateProfileInput { input UpdateProfileInput {
oldPassword: String oldPassword: String
newPassword: String newPassword: String
@ -520,6 +538,7 @@ type Mutation {
logout: Response! logout: Response!
updateProfile(params: UpdateProfileInput!): Response! updateProfile(params: UpdateProfileInput!): Response!
verifyEmail(params: VerifyEmailInput!): LoginResponse! verifyEmail(params: VerifyEmailInput!): LoginResponse!
resendVerifyEmail(params: ResendVerifyEmailInput!): Response!
} }
type Query { type Query {
@ -551,6 +570,21 @@ func (ec *executionContext) field_Mutation_login_args(ctx context.Context, rawAr
return args, nil return args, nil
} }
func (ec *executionContext) field_Mutation_resendVerifyEmail_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
var arg0 model.ResendVerifyEmailInput
if tmp, ok := rawArgs["params"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("params"))
arg0, err = ec.unmarshalNResendVerifyEmailInput2githubᚗcomᚋyauthdevᚋyauthᚋserverᚋgraphᚋmodelᚐResendVerifyEmailInput(ctx, tmp)
if err != nil {
return nil, err
}
}
args["params"] = arg0
return args, nil
}
func (ec *executionContext) field_Mutation_signup_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { func (ec *executionContext) field_Mutation_signup_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error var err error
args := map[string]interface{}{} args := map[string]interface{}{}
@ -1053,6 +1087,48 @@ func (ec *executionContext) _Mutation_verifyEmail(ctx context.Context, field gra
return ec.marshalNLoginResponse2ᚖgithubᚗcomᚋyauthdevᚋyauthᚋserverᚋgraphᚋmodelᚐLoginResponse(ctx, field.Selections, res) return ec.marshalNLoginResponse2ᚖgithubᚗcomᚋyauthdevᚋyauthᚋserverᚋgraphᚋmodelᚐLoginResponse(ctx, field.Selections, res)
} }
func (ec *executionContext) _Mutation_resendVerifyEmail(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Mutation",
Field: field,
Args: nil,
IsMethod: true,
IsResolver: true,
}
ctx = graphql.WithFieldContext(ctx, fc)
rawArgs := field.ArgumentMap(ec.Variables)
args, err := ec.field_Mutation_resendVerifyEmail_args(ctx, rawArgs)
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
fc.Args = args
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Mutation().ResendVerifyEmail(rctx, args["params"].(model.ResendVerifyEmailInput))
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(*model.Response)
fc.Result = res
return ec.marshalNResponse2ᚖgithubᚗcomᚋyauthdevᚋyauthᚋserverᚋgraphᚋmodelᚐResponse(ctx, field.Selections, res)
}
func (ec *executionContext) _Query_users(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { func (ec *executionContext) _Query_users(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -2967,6 +3043,26 @@ func (ec *executionContext) unmarshalInputLoginInput(ctx context.Context, obj in
return it, nil return it, nil
} }
func (ec *executionContext) unmarshalInputResendVerifyEmailInput(ctx context.Context, obj interface{}) (model.ResendVerifyEmailInput, error) {
var it model.ResendVerifyEmailInput
var asMap = obj.(map[string]interface{})
for k, v := range asMap {
switch k {
case "email":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
it.Email, err = ec.unmarshalNString2string(ctx, v)
if err != nil {
return it, err
}
}
}
return it, nil
}
func (ec *executionContext) unmarshalInputSignUpInput(ctx context.Context, obj interface{}) (model.SignUpInput, error) { func (ec *executionContext) unmarshalInputSignUpInput(ctx context.Context, obj interface{}) (model.SignUpInput, error) {
var it model.SignUpInput var it model.SignUpInput
var asMap = obj.(map[string]interface{}) var asMap = obj.(map[string]interface{})
@ -3228,6 +3324,11 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ invalids++
} }
case "resendVerifyEmail":
out.Values[i] = ec._Mutation_resendVerifyEmail(ctx, field)
if out.Values[i] == graphql.Null {
invalids++
}
default: default:
panic("unknown field " + strconv.Quote(field.Name)) panic("unknown field " + strconv.Quote(field.Name))
} }
@ -3733,6 +3834,11 @@ func (ec *executionContext) marshalNLoginResponse2ᚖgithubᚗcomᚋyauthdevᚋy
return ec._LoginResponse(ctx, sel, v) return ec._LoginResponse(ctx, sel, v)
} }
func (ec *executionContext) unmarshalNResendVerifyEmailInput2githubᚗcomᚋyauthdevᚋyauthᚋserverᚋgraphᚋmodelᚐResendVerifyEmailInput(ctx context.Context, v interface{}) (model.ResendVerifyEmailInput, error) {
res, err := ec.unmarshalInputResendVerifyEmailInput(ctx, v)
return res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) marshalNResponse2githubᚗcomᚋyauthdevᚋyauthᚋserverᚋgraphᚋmodelᚐResponse(ctx context.Context, sel ast.SelectionSet, v model.Response) graphql.Marshaler { func (ec *executionContext) marshalNResponse2githubᚗcomᚋyauthdevᚋyauthᚋserverᚋgraphᚋmodelᚐResponse(ctx context.Context, sel ast.SelectionSet, v model.Response) graphql.Marshaler {
return ec._Response(ctx, sel, &v) return ec._Response(ctx, sel, &v)
} }

View File

@ -19,6 +19,10 @@ type LoginResponse struct {
User *User `json:"user"` User *User `json:"user"`
} }
type ResendVerifyEmailInput struct {
Email string `json:"email"`
}
type Response struct { type Response struct {
Message string `json:"message"` Message string `json:"message"`
} }

View File

@ -60,6 +60,10 @@ input VerifyEmailInput {
token: String! token: String!
} }
input ResendVerifyEmailInput {
email: String!
}
input UpdateProfileInput { input UpdateProfileInput {
oldPassword: String oldPassword: String
newPassword: String newPassword: String
@ -76,6 +80,7 @@ type Mutation {
logout: Response! logout: Response!
updateProfile(params: UpdateProfileInput!): Response! updateProfile(params: UpdateProfileInput!): Response!
verifyEmail(params: VerifyEmailInput!): LoginResponse! verifyEmail(params: VerifyEmailInput!): LoginResponse!
resendVerifyEmail(params: ResendVerifyEmailInput!): Response!
} }
type Query { type Query {

View File

@ -31,6 +31,10 @@ func (r *mutationResolver) VerifyEmail(ctx context.Context, params model.VerifyE
return resolvers.VerifyEmail(ctx, params) return resolvers.VerifyEmail(ctx, params)
} }
func (r *mutationResolver) ResendVerifyEmail(ctx context.Context, params model.ResendVerifyEmailInput) (*model.Response, error) {
return resolvers.ResendVerifyEmail(ctx, params)
}
func (r *queryResolver) Users(ctx context.Context) ([]*model.User, error) { func (r *queryResolver) Users(ctx context.Context) ([]*model.User, error) {
return resolvers.Users(ctx) return resolvers.Users(ctx)
} }

View File

@ -0,0 +1,49 @@
package resolvers
import (
"context"
"fmt"
"log"
"strings"
"time"
"github.com/yauthdev/yauth/server/db"
"github.com/yauthdev/yauth/server/graph/model"
"github.com/yauthdev/yauth/server/utils"
)
func ResendVerifyEmail(ctx context.Context, params model.ResendVerifyEmailInput) (*model.Response, error) {
var res *model.Response
params.Email = strings.ToLower(params.Email)
if !utils.IsValidEmail(params.Email) {
return res, fmt.Errorf("invalid email")
}
verificationRequest, err := db.Mgr.GetVerificationByEmail(params.Email)
if err != nil {
return res, fmt.Errorf(`verification request not found`)
}
token, err := utils.CreateVerificationToken(params.Email, verificationRequest.Identifier)
if err != nil {
log.Println(`Error generating token`, err)
}
db.Mgr.AddVerification(db.VerificationRequest{
Token: token,
Identifier: verificationRequest.Identifier,
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
Email: params.Email,
})
// exec it as go routin so that we can reduce the api latency
go func() {
utils.SendVerificationMail(params.Email, token)
}()
res = &model.Response{
Message: `Verification email has been sent. Please check your inbox`,
}
return res, nil
}

View File

@ -27,12 +27,13 @@ func VerificationRequests(ctx context.Context) ([]*model.VerificationRequest, er
for _, verificationRequest := range verificationRequests { for _, verificationRequest := range verificationRequests {
res = append(res, &model.VerificationRequest{ res = append(res, &model.VerificationRequest{
ID: fmt.Sprintf("%d", verificationRequest.ID), ID: fmt.Sprintf("%d", verificationRequest.ID),
Email: &verificationRequest.Email, Email: &verificationRequest.Email,
Token: &verificationRequest.Token, Token: &verificationRequest.Token,
Expires: &verificationRequest.ExpiresAt, Identifier: &verificationRequest.Identifier,
CreatedAt: &verificationRequest.CreatedAt, Expires: &verificationRequest.ExpiresAt,
UpdatedAt: &verificationRequest.UpdatedAt, CreatedAt: &verificationRequest.CreatedAt,
UpdatedAt: &verificationRequest.UpdatedAt,
}) })
} }