send.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. package email
  2. import (
  3. "encoding/base64"
  4. "encoding/json"
  5. log "github.com/sirupsen/logrus"
  6. "io"
  7. "net/http"
  8. "pmail/config"
  9. "pmail/db"
  10. "pmail/dto/parsemail"
  11. "pmail/dto/response"
  12. "pmail/hooks"
  13. "pmail/hooks/framework"
  14. "pmail/i18n"
  15. "pmail/utils/async"
  16. "pmail/utils/context"
  17. "pmail/utils/send"
  18. "strings"
  19. "time"
  20. )
  21. type sendRequest struct {
  22. ReplyTo []user `json:"reply_to"`
  23. From user `json:"from"`
  24. To []user `json:"to"`
  25. Bcc []user `json:"bcc"`
  26. Cc []user `json:"cc"`
  27. Subject string `json:"subject"`
  28. Text string `json:"text"` // Plaintext message (optional)
  29. HTML string `json:"html"` // Html message (optional)
  30. Sender user `json:"sender"` // override From as SMTP envelope sender (optional)
  31. ReadReceipt []string `json:"read_receipt"`
  32. Attachments []attachment `json:"attrs"`
  33. }
  34. type user struct {
  35. Name string `json:"name"`
  36. Email string `json:"email"`
  37. }
  38. type attachment struct {
  39. Name string `json:"name"`
  40. Data string `json:"data"`
  41. }
  42. func Send(ctx *context.Context, w http.ResponseWriter, req *http.Request) {
  43. reqBytes, err := io.ReadAll(req.Body)
  44. if err != nil {
  45. log.WithContext(ctx).Errorf("%+v", err)
  46. response.NewErrorResponse(response.ParamsError, "params error", err.Error()).FPrint(w)
  47. return
  48. }
  49. log.WithContext(ctx).Infof("发送邮件")
  50. var reqData sendRequest
  51. err = json.Unmarshal(reqBytes, &reqData)
  52. if err != nil {
  53. log.WithContext(ctx).Errorf("%+v", err)
  54. response.NewErrorResponse(response.ParamsError, "params error", err.Error()).FPrint(w)
  55. return
  56. }
  57. if reqData.From.Email == "" && reqData.From.Name != "" {
  58. reqData.From.Email = reqData.From.Name + "@" + config.Instance.Domain
  59. }
  60. if reqData.From.Email == "" {
  61. response.NewErrorResponse(response.ParamsError, "发件人必填", "发件人必填").FPrint(w)
  62. return
  63. }
  64. if reqData.Subject == "" {
  65. response.NewErrorResponse(response.ParamsError, "邮件标题必填", "邮件标题必填").FPrint(w)
  66. return
  67. }
  68. if len(reqData.To) <= 0 {
  69. response.NewErrorResponse(response.ParamsError, "收件人必填", "收件人必填").FPrint(w)
  70. return
  71. }
  72. e := &parsemail.Email{}
  73. for _, to := range reqData.To {
  74. e.To = append(e.To, &parsemail.User{
  75. Name: to.Name,
  76. EmailAddress: to.Email,
  77. })
  78. }
  79. for _, bcc := range reqData.Bcc {
  80. e.Bcc = append(e.Bcc, &parsemail.User{
  81. Name: bcc.Name,
  82. EmailAddress: bcc.Email,
  83. })
  84. }
  85. for _, cc := range reqData.Cc {
  86. e.Cc = append(e.Cc, &parsemail.User{
  87. Name: cc.Name,
  88. EmailAddress: cc.Email,
  89. })
  90. }
  91. e.From = &parsemail.User{
  92. Name: reqData.From.Name,
  93. EmailAddress: reqData.From.Email,
  94. }
  95. e.Text = []byte(reqData.Text)
  96. e.HTML = []byte(reqData.HTML)
  97. e.Subject = reqData.Subject
  98. for _, att := range reqData.Attachments {
  99. att.Data = strings.TrimPrefix(att.Data, "data:")
  100. infos := strings.Split(att.Data, ";")
  101. contentType := infos[0]
  102. content := strings.TrimPrefix(infos[1], "base64,")
  103. decoded, err := base64.StdEncoding.DecodeString(content)
  104. if err != nil {
  105. log.WithContext(ctx).Errorf("附件解码错误!%v", err)
  106. response.NewErrorResponse(response.ParamsError, i18n.GetText(ctx.Lang, "att_err"), err.Error()).FPrint(w)
  107. return
  108. }
  109. e.Attachments = append(e.Attachments, &parsemail.Attachment{
  110. Filename: att.Name,
  111. ContentType: contentType,
  112. Content: decoded,
  113. })
  114. }
  115. as := async.New(ctx)
  116. for _, hook := range hooks.HookList {
  117. if hook == nil {
  118. continue
  119. }
  120. as.WaitProcess(func(hk any) {
  121. hk.(framework.EmailHook).SendBefore(ctx, e)
  122. }, hook)
  123. }
  124. as.Wait()
  125. // 邮件落库
  126. sql := "INSERT INTO email (type,subject, reply_to, from_name, from_address, `to`, bcc, cc, text, html, sender, attachments,spf_check, dkim_check, create_time,send_user_id,error) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
  127. sqlRes, sqlerr := db.Instance.Exec(db.WithContext(ctx, sql),
  128. 1,
  129. e.Subject,
  130. json2string(e.ReplyTo),
  131. e.From.Name,
  132. e.From.EmailAddress,
  133. json2string(e.To),
  134. json2string(e.Bcc),
  135. json2string(e.Cc),
  136. e.Text,
  137. e.HTML,
  138. json2string(e.Sender),
  139. json2string(e.Attachments),
  140. 1,
  141. 1,
  142. time.Now(),
  143. ctx.UserID,
  144. "",
  145. )
  146. emailId, _ := sqlRes.LastInsertId()
  147. if sqlerr != nil || emailId <= 0 {
  148. log.Println("mysql insert error:", err.Error())
  149. response.NewErrorResponse(response.ServerError, i18n.GetText(ctx.Lang, "send_fail"), err.Error()).FPrint(w)
  150. return
  151. }
  152. e.MessageId = emailId
  153. async.New(ctx).Process(func(p any) {
  154. errMsg := ""
  155. err, sendErr := send.Send(ctx, e)
  156. as2 := async.New(ctx)
  157. for _, hook := range hooks.HookList {
  158. if hook == nil {
  159. continue
  160. }
  161. as2.WaitProcess(func(hk any) {
  162. hk.(framework.EmailHook).SendAfter(ctx, e, sendErr)
  163. }, hook)
  164. }
  165. as2.Wait()
  166. if err != nil {
  167. errMsg = err.Error()
  168. _, err := db.Instance.Exec(db.WithContext(ctx, "update email set status =2 ,error=? where id = ? "), errMsg, emailId)
  169. if err != nil {
  170. log.WithContext(ctx).Errorf("sql Error :%+v", err)
  171. }
  172. } else {
  173. _, err := db.Instance.Exec(db.WithContext(ctx, "update email set status =1 where id = ? "), emailId)
  174. if err != nil {
  175. log.WithContext(ctx).Errorf("sql Error :%+v", err)
  176. }
  177. }
  178. }, nil)
  179. response.NewSuccessResponse(i18n.GetText(ctx.Lang, "succ")).FPrint(w)
  180. }
  181. func json2string(d any) string {
  182. by, _ := json.Marshal(d)
  183. return string(by)
  184. }