-
Notifications
You must be signed in to change notification settings - Fork 0
/
mail.go
186 lines (153 loc) · 5.54 KB
/
mail.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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
package controllers
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"html/template"
"io/ioutil"
"net/http"
"time"
log "github.com/Sirupsen/logrus"
"github.com/gin-gonic/gin"
"gopkg.in/guregu/null.v3"
"twreporter.org/go-api/services"
"twreporter.org/go-api/utils"
)
type activationReqBody struct {
Email string `json:"email" binding:"required"`
ActivateLink string `json:"activate_link" binding:"required"`
}
type donationSuccessReqBody struct {
Address string `json:"address"`
Amount uint `json:"amount" binding:"required"`
CardInfoLastFour string `json:"card_info_last_four"`
CardInfoType string `json:"card_info_type"`
Currency string `json:"currency"`
DonationTimestamp null.Int `json:"donation_timestamp"`
DonationLink string `json:"donation_link" binding:"required"`
DonationMethod string `json:"donation_method" binding:"required"`
DonationType string `json:"donation_type" binding:"required"`
Email string `json:"email" binding:"required"`
Name string `json:"name"`
NationalID string `json:"national_id"`
OrderNumber string `json:"order_number" binding:"required"`
PhoneNumber string `json:"phone_number"`
}
// NewMailController is used to new *MailController
func NewMailController(svc services.MailService, t *template.Template) *MailController {
return &MailController{
HTMLTemplate: t,
MailService: svc,
}
}
// MailController is the data structure holding HTML template and mail service
type MailController struct {
HTMLTemplate *template.Template
MailService services.MailService
}
// LoadTemplateFiles is a wrapper function to parse template files
func (contrl *MailController) LoadTemplateFiles(filenames ...string) {
contrl.HTMLTemplate = template.Must(template.ParseFiles(filenames...))
}
// SendActivation retrieves email and activation link from rqeuest body,
// and invoke MailService to send activation mail
func (contrl *MailController) SendActivation(c *gin.Context) (int, gin.H, error) {
const subject = "登入報導者"
var err error
var failData gin.H
var mailBody string
var out bytes.Buffer
var reqBody activationReqBody
var valid bool
if failData, valid = bindRequestJSONBody(c, &reqBody); valid == false {
return http.StatusBadRequest, gin.H{"status": "fail", "data": failData}, nil
}
if err = contrl.HTMLTemplate.ExecuteTemplate(&out, "signin.tmpl", struct {
Href string
}{
reqBody.ActivateLink,
}); err != nil {
log.Error(err)
return http.StatusInternalServerError, gin.H{"status": "error", "message": "can not create activate mail body"}, nil
}
mailBody = out.String()
if err = contrl.MailService.Send(reqBody.Email, subject, mailBody); err != nil {
log.Error(err)
return http.StatusInternalServerError, gin.H{"status": "error", "message": fmt.Sprintf("can not send activate mail to %s", reqBody.Email)}, nil
}
return http.StatusNoContent, gin.H{}, nil
}
func (contrl *MailController) SendDonationSuccessMail(c *gin.Context) (int, gin.H, error) {
const subject = "感謝您成為報導者的贊助夥伴"
const taipeiLocationName = "Asia/Taipei"
var donationDatetime time.Time
var err error
var failData gin.H
var location *time.Location
var mailBody string
var out bytes.Buffer
var reqBody donationSuccessReqBody
var valid bool
// parse requst JSON into struct
if failData, valid = bindRequestJSONBody(c, &reqBody); valid == false {
return http.StatusBadRequest, gin.H{"status": "fail", "data": failData}, nil
}
if reqBody.Currency == "" {
// give default Currency
reqBody.Currency = "TWD"
}
if reqBody.DonationTimestamp.Valid {
donationDatetime = time.Unix(reqBody.DonationTimestamp.Int64, 0)
} else {
donationDatetime = time.Now()
}
location, _ = time.LoadLocation(taipeiLocationName)
var templateData = struct {
donationSuccessReqBody
DonationDatetime string
}{
reqBody,
donationDatetime.In(location).Format("2006-01-02 15:04:05 UTC+8"),
}
if err = contrl.HTMLTemplate.ExecuteTemplate(&out, "success-donation.tmpl", templateData); err != nil {
log.Error(err)
return http.StatusInternalServerError, gin.H{"status": "error", "message": "can not create donation success mail body"}, nil
}
mailBody = out.String()
// send email through mail service
if err = contrl.MailService.Send(reqBody.Email, subject, mailBody); err != nil {
log.Error(err)
return http.StatusInternalServerError, gin.H{"status": "error", "message": fmt.Sprintf("can not send donation success mail to %s", reqBody.Email)}, nil
}
return http.StatusNoContent, gin.H{}, nil
}
func postMailServiceEndpoint(reqBody interface{}, endpoint string) error {
var body []byte
var err error
var rawResp *http.Response
var timeout = 10 * time.Second
var expiration int = 60
var accessToken string
if body, err = json.Marshal(reqBody); err != nil {
return err
}
// Setup HTTP client with timeout
client := &http.Client{Timeout: timeout}
accessToken, _ = utils.RetrieveMailServiceAccessToken(expiration)
req, _ := http.NewRequest("POST", endpoint, bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", accessToken))
if rawResp, err = client.Do(req); err != nil {
return err
}
defer rawResp.Body.Close()
if rawResp.StatusCode != http.StatusNoContent {
if body, err = ioutil.ReadAll(rawResp.Body); err != nil {
return err
}
errMsg := fmt.Sprintf("receive error status code(%d) from %s. error response: %s", rawResp.StatusCode, endpoint, string(body))
return errors.New(errMsg)
}
return nil
}