send.go 6.0 KB

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