@@ -32,6 +32,8 @@ var (
|
||||
GITHUB_CLIENT_SECRET = ""
|
||||
// FACEBOOK_CLIENT_ID = ""
|
||||
// FACEBOOK_CLIENT_SECRET = ""
|
||||
FORGOT_PASSWORD_URI = ""
|
||||
VERIFY_EMAIL_URI = ""
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -60,7 +62,8 @@ func init() {
|
||||
GITHUB_CLIENT_SECRET = os.Getenv("GITHUB_CLIENT_SECRET")
|
||||
// FACEBOOK_CLIENT_ID = os.Getenv("FACEBOOK_CLIENT_ID")
|
||||
// FACEBOOK_CLIENT_SECRET = os.Getenv("FACEBOOK_CLIENT_SECRET")
|
||||
|
||||
FORGOT_PASSWORD_URI = strings.TrimPrefix(os.Getenv("FORGOT_PASSWORD_URI"), "/")
|
||||
VERIFY_EMAIL_URI = strings.TrimPrefix(os.Getenv("VERIFY_EMAIL_URI"), "/")
|
||||
if YAUTH_ADMIN_SECRET == "" {
|
||||
panic("Yauth admin secret is required")
|
||||
}
|
||||
|
@@ -5,11 +5,13 @@ type VerificationType int
|
||||
const (
|
||||
BasicAuthSignup VerificationType = iota
|
||||
UpdateEmail
|
||||
ForgotPassword
|
||||
)
|
||||
|
||||
func (d VerificationType) String() string {
|
||||
return [...]string{
|
||||
"basic_auth_signup",
|
||||
"update_email",
|
||||
"forgot_password",
|
||||
}[d]
|
||||
}
|
||||
|
@@ -56,12 +56,14 @@ type ComplexityRoot struct {
|
||||
}
|
||||
|
||||
Mutation struct {
|
||||
Login func(childComplexity int, params model.LoginInput) int
|
||||
Logout func(childComplexity int) int
|
||||
ResendVerifyEmail func(childComplexity int, params model.ResendVerifyEmailInput) int
|
||||
Signup func(childComplexity int, params model.SignUpInput) int
|
||||
UpdateProfile func(childComplexity int, params model.UpdateProfileInput) int
|
||||
VerifyEmail func(childComplexity int, params model.VerifyEmailInput) int
|
||||
ForgotPassword func(childComplexity int, params model.ForgotPasswordInput) int
|
||||
ForgotPasswordRequest func(childComplexity int, params model.ForgotPasswordRequestInput) int
|
||||
Login func(childComplexity int, params model.LoginInput) int
|
||||
Logout func(childComplexity int) int
|
||||
ResendVerifyEmail func(childComplexity int, params model.ResendVerifyEmailInput) int
|
||||
Signup func(childComplexity int, params model.SignUpInput) int
|
||||
UpdateProfile func(childComplexity int, params model.UpdateProfileInput) int
|
||||
VerifyEmail func(childComplexity int, params model.VerifyEmailInput) int
|
||||
}
|
||||
|
||||
Query struct {
|
||||
@@ -106,6 +108,8 @@ type MutationResolver interface {
|
||||
UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model.Response, error)
|
||||
VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.LoginResponse, error)
|
||||
ResendVerifyEmail(ctx context.Context, params model.ResendVerifyEmailInput) (*model.Response, error)
|
||||
ForgotPasswordRequest(ctx context.Context, params model.ForgotPasswordRequestInput) (*model.Response, error)
|
||||
ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*model.Response, error)
|
||||
}
|
||||
type QueryResolver interface {
|
||||
Users(ctx context.Context) ([]*model.User, error)
|
||||
@@ -171,6 +175,30 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
return e.complexity.LoginResponse.User(childComplexity), true
|
||||
|
||||
case "Mutation.forgotPassword":
|
||||
if e.complexity.Mutation.ForgotPassword == nil {
|
||||
break
|
||||
}
|
||||
|
||||
args, err := ec.field_Mutation_forgotPassword_args(context.TODO(), rawArgs)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return e.complexity.Mutation.ForgotPassword(childComplexity, args["params"].(model.ForgotPasswordInput)), true
|
||||
|
||||
case "Mutation.forgotPasswordRequest":
|
||||
if e.complexity.Mutation.ForgotPasswordRequest == nil {
|
||||
break
|
||||
}
|
||||
|
||||
args, err := ec.field_Mutation_forgotPasswordRequest_args(context.TODO(), rawArgs)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return e.complexity.Mutation.ForgotPasswordRequest(childComplexity, args["params"].(model.ForgotPasswordRequestInput)), true
|
||||
|
||||
case "Mutation.login":
|
||||
if e.complexity.Mutation.Login == nil {
|
||||
break
|
||||
@@ -532,6 +560,16 @@ input UpdateProfileInput {
|
||||
email: String
|
||||
}
|
||||
|
||||
input ForgotPasswordRequestInput {
|
||||
email: String!
|
||||
}
|
||||
|
||||
input ForgotPasswordInput {
|
||||
token: String!
|
||||
newPassword: String!
|
||||
confirmPassword: String!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
signup(params: SignUpInput!): Response!
|
||||
login(params: LoginInput!): LoginResponse!
|
||||
@@ -539,6 +577,8 @@ type Mutation {
|
||||
updateProfile(params: UpdateProfileInput!): Response!
|
||||
verifyEmail(params: VerifyEmailInput!): LoginResponse!
|
||||
resendVerifyEmail(params: ResendVerifyEmailInput!): Response!
|
||||
forgotPasswordRequest(params: ForgotPasswordRequestInput!): Response!
|
||||
forgotPassword(params: ForgotPasswordInput!): Response!
|
||||
}
|
||||
|
||||
type Query {
|
||||
@@ -555,6 +595,36 @@ var parsedSchema = gqlparser.MustLoadSchema(sources...)
|
||||
|
||||
// region ***************************** args.gotpl *****************************
|
||||
|
||||
func (ec *executionContext) field_Mutation_forgotPasswordRequest_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||
var err error
|
||||
args := map[string]interface{}{}
|
||||
var arg0 model.ForgotPasswordRequestInput
|
||||
if tmp, ok := rawArgs["params"]; ok {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("params"))
|
||||
arg0, err = ec.unmarshalNForgotPasswordRequestInput2githubᚗcomᚋyauthdevᚋyauthᚋserverᚋgraphᚋmodelᚐForgotPasswordRequestInput(ctx, tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
args["params"] = arg0
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Mutation_forgotPassword_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||
var err error
|
||||
args := map[string]interface{}{}
|
||||
var arg0 model.ForgotPasswordInput
|
||||
if tmp, ok := rawArgs["params"]; ok {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("params"))
|
||||
arg0, err = ec.unmarshalNForgotPasswordInput2githubᚗcomᚋyauthdevᚋyauthᚋserverᚋgraphᚋmodelᚐForgotPasswordInput(ctx, tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
args["params"] = arg0
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Mutation_login_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||
var err error
|
||||
args := map[string]interface{}{}
|
||||
@@ -1129,6 +1199,90 @@ func (ec *executionContext) _Mutation_resendVerifyEmail(ctx context.Context, fie
|
||||
return ec.marshalNResponse2ᚖgithubᚗcomᚋyauthdevᚋyauthᚋserverᚋgraphᚋmodelᚐResponse(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Mutation_forgotPasswordRequest(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_forgotPasswordRequest_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().ForgotPasswordRequest(rctx, args["params"].(model.ForgotPasswordRequestInput))
|
||||
})
|
||||
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) _Mutation_forgotPassword(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_forgotPassword_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().ForgotPassword(rctx, args["params"].(model.ForgotPasswordInput))
|
||||
})
|
||||
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) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
@@ -3015,6 +3169,62 @@ func (ec *executionContext) ___Type_ofType(ctx context.Context, field graphql.Co
|
||||
|
||||
// region **************************** input.gotpl *****************************
|
||||
|
||||
func (ec *executionContext) unmarshalInputForgotPasswordInput(ctx context.Context, obj interface{}) (model.ForgotPasswordInput, error) {
|
||||
var it model.ForgotPasswordInput
|
||||
var asMap = obj.(map[string]interface{})
|
||||
|
||||
for k, v := range asMap {
|
||||
switch k {
|
||||
case "token":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("token"))
|
||||
it.Token, err = ec.unmarshalNString2string(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "newPassword":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("newPassword"))
|
||||
it.NewPassword, err = ec.unmarshalNString2string(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "confirmPassword":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("confirmPassword"))
|
||||
it.ConfirmPassword, err = ec.unmarshalNString2string(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return it, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalInputForgotPasswordRequestInput(ctx context.Context, obj interface{}) (model.ForgotPasswordRequestInput, error) {
|
||||
var it model.ForgotPasswordRequestInput
|
||||
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) unmarshalInputLoginInput(ctx context.Context, obj interface{}) (model.LoginInput, error) {
|
||||
var it model.LoginInput
|
||||
var asMap = obj.(map[string]interface{})
|
||||
@@ -3329,6 +3539,16 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "forgotPasswordRequest":
|
||||
out.Values[i] = ec._Mutation_forgotPasswordRequest(ctx, field)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "forgotPassword":
|
||||
out.Values[i] = ec._Mutation_forgotPassword(ctx, field)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
}
|
||||
@@ -3800,6 +4020,16 @@ func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.Se
|
||||
return res
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNForgotPasswordInput2githubᚗcomᚋyauthdevᚋyauthᚋserverᚋgraphᚋmodelᚐForgotPasswordInput(ctx context.Context, v interface{}) (model.ForgotPasswordInput, error) {
|
||||
res, err := ec.unmarshalInputForgotPasswordInput(ctx, v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNForgotPasswordRequestInput2githubᚗcomᚋyauthdevᚋyauthᚋserverᚋgraphᚋmodelᚐForgotPasswordRequestInput(ctx context.Context, v interface{}) (model.ForgotPasswordRequestInput, error) {
|
||||
res, err := ec.unmarshalInputForgotPasswordRequestInput(ctx, v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNID2string(ctx context.Context, v interface{}) (string, error) {
|
||||
res, err := graphql.UnmarshalID(v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
|
@@ -7,6 +7,16 @@ type Error struct {
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
type ForgotPasswordInput struct {
|
||||
Token string `json:"token"`
|
||||
NewPassword string `json:"newPassword"`
|
||||
ConfirmPassword string `json:"confirmPassword"`
|
||||
}
|
||||
|
||||
type ForgotPasswordRequestInput struct {
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
type LoginInput struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
|
@@ -74,6 +74,16 @@ input UpdateProfileInput {
|
||||
email: String
|
||||
}
|
||||
|
||||
input ForgotPasswordRequestInput {
|
||||
email: String!
|
||||
}
|
||||
|
||||
input ForgotPasswordInput {
|
||||
token: String!
|
||||
newPassword: String!
|
||||
confirmPassword: String!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
signup(params: SignUpInput!): Response!
|
||||
login(params: LoginInput!): LoginResponse!
|
||||
@@ -81,6 +91,8 @@ type Mutation {
|
||||
updateProfile(params: UpdateProfileInput!): Response!
|
||||
verifyEmail(params: VerifyEmailInput!): LoginResponse!
|
||||
resendVerifyEmail(params: ResendVerifyEmailInput!): Response!
|
||||
forgotPasswordRequest(params: ForgotPasswordRequestInput!): Response!
|
||||
forgotPassword(params: ForgotPasswordInput!): Response!
|
||||
}
|
||||
|
||||
type Query {
|
||||
|
@@ -35,6 +35,14 @@ func (r *mutationResolver) ResendVerifyEmail(ctx context.Context, params model.R
|
||||
return resolvers.ResendVerifyEmail(ctx, params)
|
||||
}
|
||||
|
||||
func (r *mutationResolver) ForgotPasswordRequest(ctx context.Context, params model.ForgotPasswordRequestInput) (*model.Response, error) {
|
||||
return resolvers.ForgotPasswordRequest(ctx, params)
|
||||
}
|
||||
|
||||
func (r *mutationResolver) ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*model.Response, error) {
|
||||
return resolvers.ForgotPassword(ctx, params)
|
||||
}
|
||||
|
||||
func (r *queryResolver) Users(ctx context.Context) ([]*model.User, error) {
|
||||
return resolvers.Users(ctx)
|
||||
}
|
||||
|
47
server/resolvers/forgotPassword.go
Normal file
47
server/resolvers/forgotPassword.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package resolvers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/yauthdev/yauth/server/db"
|
||||
"github.com/yauthdev/yauth/server/graph/model"
|
||||
"github.com/yauthdev/yauth/server/utils"
|
||||
)
|
||||
|
||||
func ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*model.Response, error) {
|
||||
var res *model.Response
|
||||
|
||||
if params.NewPassword != params.ConfirmPassword {
|
||||
return res, fmt.Errorf(`passwords don't match`)
|
||||
}
|
||||
|
||||
_, err := db.Mgr.GetVerificationByToken(params.Token)
|
||||
if err != nil {
|
||||
return res, fmt.Errorf(`invalid token`)
|
||||
}
|
||||
|
||||
// verify if token exists in db
|
||||
claim, err := utils.VerifyVerificationToken(params.Token)
|
||||
if err != nil {
|
||||
return res, fmt.Errorf(`invalid token`)
|
||||
}
|
||||
|
||||
user, err := db.Mgr.GetUserByEmail(claim.Email)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
password, _ := utils.HashPassword(params.NewPassword)
|
||||
user.Password = password
|
||||
|
||||
// delete from verification table
|
||||
db.Mgr.DeleteToken(claim.Email)
|
||||
db.Mgr.UpdateUser(user)
|
||||
|
||||
res = &model.Response{
|
||||
Message: `Password updated successfully.`,
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
50
server/resolvers/forgotPasswordRequest.go
Normal file
50
server/resolvers/forgotPasswordRequest.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package resolvers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/yauthdev/yauth/server/db"
|
||||
"github.com/yauthdev/yauth/server/enum"
|
||||
"github.com/yauthdev/yauth/server/graph/model"
|
||||
"github.com/yauthdev/yauth/server/utils"
|
||||
)
|
||||
|
||||
func ForgotPasswordRequest(ctx context.Context, params model.ForgotPasswordRequestInput) (*model.Response, error) {
|
||||
var res *model.Response
|
||||
params.Email = strings.ToLower(params.Email)
|
||||
|
||||
if !utils.IsValidEmail(params.Email) {
|
||||
return res, fmt.Errorf("invalid email")
|
||||
}
|
||||
|
||||
_, err := db.Mgr.GetUserByEmail(params.Email)
|
||||
if err != nil {
|
||||
return res, fmt.Errorf(`user with this email not found`)
|
||||
}
|
||||
|
||||
token, err := utils.CreateVerificationToken(params.Email, enum.ForgotPassword.String())
|
||||
if err != nil {
|
||||
log.Println(`Error generating token`, err)
|
||||
}
|
||||
db.Mgr.AddVerification(db.VerificationRequest{
|
||||
Token: token,
|
||||
Identifier: enum.ForgotPassword.String(),
|
||||
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.SendForgotPasswordMail(params.Email, token)
|
||||
}()
|
||||
|
||||
res = &model.Response{
|
||||
Message: `Please check your inbox! We have sent a password reset link.`,
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
@@ -26,7 +26,32 @@ func SendVerificationMail(toEmail, token string) error {
|
||||
<a href="%s">Click here to verify</a>
|
||||
</body>
|
||||
</html>
|
||||
`, constants.FRONTEND_URL+"/verify?token="+token)
|
||||
`, constants.FRONTEND_URL+"/"+constants.VERIFY_EMAIL_URI+"?token="+token)
|
||||
bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
|
||||
|
||||
return sender.SendMail(Receiver, Subject, bodyMessage)
|
||||
}
|
||||
|
||||
// SendForgotPasswordMail to send verification email
|
||||
func SendForgotPasswordMail(toEmail, token string) error {
|
||||
sender := email.NewSender()
|
||||
|
||||
// The receiver needs to be in slice as the receive supports multiple receiver
|
||||
Receiver := []string{toEmail}
|
||||
|
||||
Subject := "Reset Password"
|
||||
message := fmt.Sprintf(`
|
||||
<!DOCTYPE HTML PULBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html"; charset=ISO-8859-1">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Please use the link below to reset password </h1><br/>
|
||||
<a href="%s">Reset Password</a>
|
||||
</body>
|
||||
</html>
|
||||
`, constants.FRONTEND_URL+"/"+constants.FORGOT_PASSWORD_URI+"?token="+token)
|
||||
bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
|
||||
|
||||
return sender.SendMail(Receiver, Subject, bodyMessage)
|
||||
|
Reference in New Issue
Block a user