github.com/cloudreve/Cloudreve/v3@v3.0.0-20240224133659-3edb00a6484c/pkg/email/smtp.go (about)

     1  package email
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/google/uuid"
     6  	"time"
     7  
     8  	"github.com/cloudreve/Cloudreve/v3/pkg/util"
     9  	"github.com/go-mail/mail"
    10  )
    11  
    12  // SMTP SMTP协议发送邮件
    13  type SMTP struct {
    14  	Config SMTPConfig
    15  	ch     chan *mail.Message
    16  	chOpen bool
    17  }
    18  
    19  // SMTPConfig SMTP发送配置
    20  type SMTPConfig struct {
    21  	Name       string // 发送者名
    22  	Address    string // 发送者地址
    23  	ReplyTo    string // 回复地址
    24  	Host       string // 服务器主机名
    25  	Port       int    // 服务器端口
    26  	User       string // 用户名
    27  	Password   string // 密码
    28  	Encryption bool   // 是否启用加密
    29  	Keepalive  int    // SMTP 连接保留时长
    30  }
    31  
    32  // NewSMTPClient 新建SMTP发送队列
    33  func NewSMTPClient(config SMTPConfig) *SMTP {
    34  	client := &SMTP{
    35  		Config: config,
    36  		ch:     make(chan *mail.Message, 30),
    37  		chOpen: false,
    38  	}
    39  
    40  	client.Init()
    41  
    42  	return client
    43  }
    44  
    45  // Send 发送邮件
    46  func (client *SMTP) Send(to, title, body string) error {
    47  	if !client.chOpen {
    48  		return ErrChanNotOpen
    49  	}
    50  	m := mail.NewMessage()
    51  	m.SetAddressHeader("From", client.Config.Address, client.Config.Name)
    52  	m.SetAddressHeader("Reply-To", client.Config.ReplyTo, client.Config.Name)
    53  	m.SetHeader("To", to)
    54  	m.SetHeader("Subject", title)
    55  	m.SetHeader("Message-ID", fmt.Sprintf("<%s@%s>", uuid.NewString(), "cloudreve"))
    56  	m.SetBody("text/html", body)
    57  	client.ch <- m
    58  	return nil
    59  }
    60  
    61  // Close 关闭发送队列
    62  func (client *SMTP) Close() {
    63  	if client.ch != nil {
    64  		close(client.ch)
    65  	}
    66  }
    67  
    68  // Init 初始化发送队列
    69  func (client *SMTP) Init() {
    70  	go func() {
    71  		defer func() {
    72  			if err := recover(); err != nil {
    73  				client.chOpen = false
    74  				util.Log().Error("Exception while sending email: %s, queue will be reset in 10 seconds.", err)
    75  				time.Sleep(time.Duration(10) * time.Second)
    76  				client.Init()
    77  			}
    78  		}()
    79  
    80  		d := mail.NewDialer(client.Config.Host, client.Config.Port, client.Config.User, client.Config.Password)
    81  		d.Timeout = time.Duration(client.Config.Keepalive+5) * time.Second
    82  		client.chOpen = true
    83  		// 是否启用 SSL
    84  		d.SSL = false
    85  		if client.Config.Encryption {
    86  			d.SSL = true
    87  		}
    88  		d.StartTLSPolicy = mail.OpportunisticStartTLS
    89  
    90  		var s mail.SendCloser
    91  		var err error
    92  		open := false
    93  		for {
    94  			select {
    95  			case m, ok := <-client.ch:
    96  				if !ok {
    97  					util.Log().Debug("Email queue closing...")
    98  					client.chOpen = false
    99  					return
   100  				}
   101  				if !open {
   102  					if s, err = d.Dial(); err != nil {
   103  						panic(err)
   104  					}
   105  					open = true
   106  				}
   107  				if err := mail.Send(s, m); err != nil {
   108  					util.Log().Warning("Failed to send email: %s", err)
   109  				} else {
   110  					util.Log().Debug("Email sent.")
   111  				}
   112  			// 长时间没有新邮件,则关闭SMTP连接
   113  			case <-time.After(time.Duration(client.Config.Keepalive) * time.Second):
   114  				if open {
   115  					if err := s.Close(); err != nil {
   116  						util.Log().Warning("Failed to close SMTP connection: %s", err)
   117  					}
   118  					open = false
   119  				}
   120  			}
   121  		}
   122  	}()
   123  }