-
Notifications
You must be signed in to change notification settings - Fork 0
/
confirm-email.go
133 lines (107 loc) · 3.42 KB
/
confirm-email.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package usersvc
import (
"context"
"log"
"strings"
"time"
"github.com/sisukasco/commons/http_utils"
"github.com/sisukasco/commons/stringid"
"github.com/sisukasco/henki/pkg/db"
"github.com/cbroglie/mustache"
)
type ConfirmEmailInfo struct {
FirstName string
Link string
}
func (usvc *UserService) SendEmailConfirmationRequest(ctx context.Context, userID string) error {
user, err := usvc.svc.DB.Q.GetUser(ctx, userID)
if err != nil {
return http_utils.InternalServerError("Error fetching records").WithInternalError(err)
}
if user.ConfirmedAt.Valid {
//already confirmed
return nil
}
if user.ConfirmationSentAt.Valid {
last := time.Now().Sub(user.ConfirmationSentAt.Time).Minutes()
if last < 60 { //If email was sent in last 50 minutes, dont send again
return nil
}
}
usvc.PostConfirmationEmail(user.ID)
return nil
}
func (usvc *UserService) PostConfirmationEmail(userID string) {
usvc.wg.Add(1)
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
go func() {
defer cancel()
defer usvc.wg.Done()
usvc.sendConfirmationEmail(ctx, userID)
}()
}
type ConfirmEmailResponse struct {
ResetPasswordToken string `json:"reset_token"`
Status string `json:"status"`
}
func (usvc *UserService) ConfirmUserEmail(ctx context.Context, code string) (*ConfirmEmailResponse, error) {
code = strings.TrimSpace(code)
uinfo, err := usvc.svc.DB.Q.GetUserByConfirmationToken(ctx, code)
if err != nil || len(uinfo.ID) <= 2 {
return nil, http_utils.UnprocessableEntityError("Confirmation code does not match").WithInternalError(err)
}
err = usvc.svc.DB.Q.ConfirmUserEmail(ctx, code)
if err != nil {
return nil, http_utils.InternalServerError("Error while accessing").WithInternalError(err)
}
resp := &ConfirmEmailResponse{}
if uinfo.Resetpassword {
token, err := usvc.generatePasswordResetToken(ctx, uinfo.ID)
if err != nil {
return nil, err
}
resp.ResetPasswordToken = token
}
resp.Status = "ok"
return resp, nil
}
//sendConfirmationEmail is called from the task (in the job queue)
// So this function is called asynchronously
func (usvc *UserService) sendConfirmationEmail(ctx context.Context, userID string) {
user, err := usvc.svc.DB.Q.GetUser(ctx, userID)
if err != nil {
log.Printf("Error while sending email user not found %v", err)
return
}
confirmCode := stringid.RandString(12)
for i := 0; i < 100; i++ {
exists, err := usvc.svc.DB.Q.DoesConfirmationTokenExist(ctx, confirmCode)
if !exists && err == nil {
break
}
confirmCode = stringid.RandString(12)
}
err = usvc.svc.DB.Q.UpdateConfirmationToken(ctx,
db.UpdateConfirmationTokenParams{ID: user.ID, ConfirmationToken: confirmCode})
if err != nil {
log.Printf("Error while sending email Error updating confirm code %v", err)
return
}
site := usvc.svc.Konf.String("client.url")
subj := usvc.svc.Konf.String("emails.signup-confirmation.subject")
confirmationEmail := usvc.svc.Konf.String("emails.signup-confirmation.body")
confLink := site + "/auth/confirm/?code=" + confirmCode
e := ConfirmEmailInfo{user.FirstName, confLink}
eb, err := mustache.Render(confirmationEmail, &e)
if err != nil {
log.Printf("Error while sending email- mustache rendering error %v", err)
return
}
mailer := usvc.getMailer()
err = mailer.SendEmail(user.Email, subj, eb)
if err != nil {
log.Printf("Error while sending email- SendEmail error %v", err)
return
}
log.Println("confirmation email sent")
}