refactor: extract smtp module

This commit is contained in:
Fu Diwei
2026-01-22 21:21:16 +08:00
parent 35c1c2e97a
commit 53812bf227
5 changed files with 149 additions and 56 deletions
+96
View File
@@ -0,0 +1,96 @@
package smtp
import (
"context"
"errors"
"fmt"
"time"
"github.com/wneessen/go-mail"
xtls "github.com/certimate-go/certimate/pkg/utils/tls"
)
type Client struct {
cli *mail.Client
}
func NewClient(config *Config) (*Client, error) {
if config == nil {
return nil, fmt.Errorf("the configuration of SMTP client is nil")
}
client, err := createSmtpClient(config)
if err != nil {
return nil, err
}
return &Client{cli: client}, nil
}
func (c *Client) Close() error {
return c.cli.Close()
}
func (c *Client) Send(ctx context.Context, msg *Message) error {
if err := c.cli.DialAndSendWithContext(ctx, msg); err != nil {
errShouldBeIgnored := false
// REF: https://github.com/wneessen/go-mail/issues/463
var sendErr *mail.SendError
if errors.As(err, &sendErr) {
if sendErr.Reason == mail.ErrSMTPReset {
errShouldBeIgnored = true
}
}
if !errShouldBeIgnored {
return fmt.Errorf("smtp: %w", err)
}
}
return nil
}
func createSmtpClient(config *Config) (*mail.Client, error) {
clientOptions := []mail.Option{
mail.WithSMTPAuth(mail.SMTPAuthAutoDiscover),
mail.WithUsername(config.Username),
mail.WithPassword(config.Password),
mail.WithTimeout(time.Second * 30),
}
if config.Port == 0 {
if config.UseSsl {
clientOptions = append(clientOptions, mail.WithPort(mail.DefaultPortSSL))
} else {
clientOptions = append(clientOptions, mail.WithPort(mail.DefaultPort))
}
} else {
clientOptions = append(clientOptions, mail.WithPort(config.Port))
}
if config.UseSsl {
tlsConfig := xtls.NewCompatibleConfig()
if config.SkipTlsVerify {
tlsConfig.InsecureSkipVerify = true
} else {
tlsConfig.ServerName = config.Host
}
clientOptions = append(clientOptions, mail.WithSSL())
clientOptions = append(clientOptions, mail.WithTLSConfig(tlsConfig))
clientOptions = append(clientOptions, mail.WithTLSPolicy(mail.TLSMandatory))
} else {
clientOptions = append(clientOptions, mail.WithTLSPolicy(mail.TLSOpportunistic))
}
client, err := mail.NewClient(config.Host, clientOptions...)
if err != nil {
return nil, fmt.Errorf("smtp: %w", err)
}
client.ErrorHandlerRegistry.RegisterHandler("smtp.qq.com", "QUIT", &wQQMailQuitErrorHandler{})
return client, nil
}
+20
View File
@@ -0,0 +1,20 @@
package smtp
const (
defaultPort int = 25
)
type Config struct {
Host string
Port int
Username string
Password string
UseSsl bool
SkipTlsVerify bool
}
func NewDefaultConfig() *Config {
return &Config{
Port: defaultPort,
}
}
@@ -1,4 +1,4 @@
package email
package smtp
import (
"bytes"
+18
View File
@@ -0,0 +1,18 @@
package smtp
import (
"github.com/wneessen/go-mail"
)
type Message = mail.Msg
func NewMessage() *Message {
return mail.NewMsg()
}
type MIMEType = mail.ContentType
const (
MIMETypeTextHTML MIMEType = mail.TypeTextHTML
MIMETypeTextPlain MIMEType = mail.TypeTextPlain
)
+14 -55
View File
@@ -5,12 +5,9 @@ import (
"errors"
"fmt"
"log/slog"
"time"
"github.com/wneessen/go-mail"
"github.com/certimate-go/certimate/internal/tools/smtp"
"github.com/certimate-go/certimate/pkg/core/notifier"
xtls "github.com/certimate-go/certimate/pkg/utils/tls"
)
type NotifierConfig struct {
@@ -62,49 +59,23 @@ func (n *Notifier) SetLogger(logger *slog.Logger) {
}
func (n *Notifier) Notify(ctx context.Context, subject string, message string) (*notifier.NotifyResult, error) {
clientOptions := []mail.Option{
mail.WithSMTPAuth(mail.SMTPAuthAutoDiscover),
mail.WithUsername(n.config.Username),
mail.WithPassword(n.config.Password),
mail.WithTimeout(time.Second * 30),
}
if n.config.SmtpPort == 0 {
if n.config.SmtpTls {
clientOptions = append(clientOptions, mail.WithPort(mail.DefaultPortSSL))
} else {
clientOptions = append(clientOptions, mail.WithPort(mail.DefaultPort))
}
} else {
clientOptions = append(clientOptions, mail.WithPort(int(n.config.SmtpPort)))
}
if n.config.SmtpTls {
tlsConfig := xtls.NewCompatibleConfig()
if n.config.AllowInsecureConnections {
tlsConfig.InsecureSkipVerify = true
} else {
tlsConfig.ServerName = n.config.SmtpHost
}
clientOptions = append(clientOptions, mail.WithSSL())
clientOptions = append(clientOptions, mail.WithTLSConfig(tlsConfig))
clientOptions = append(clientOptions, mail.WithTLSPolicy(mail.TLSMandatory))
} else {
clientOptions = append(clientOptions, mail.WithTLSPolicy(mail.TLSOpportunistic))
}
client, err := mail.NewClient(n.config.SmtpHost, clientOptions...)
clientCfg := smtp.NewDefaultConfig()
clientCfg.Host = n.config.SmtpHost
clientCfg.Port = int(n.config.SmtpPort)
clientCfg.Username = n.config.Username
clientCfg.Password = n.config.Password
clientCfg.UseSsl = n.config.SmtpTls
clientCfg.SkipTlsVerify = n.config.AllowInsecureConnections
client, err := smtp.NewClient(clientCfg)
if err != nil {
return nil, fmt.Errorf("failed to create smtp client: %w", err)
return nil, fmt.Errorf("failed to create SMTP client: %w", err)
}
client.ErrorHandlerRegistry.RegisterHandler("smtp.qq.com", "QUIT", &wQQMailQuitErrorHandler{})
defer client.Close()
msg := mail.NewMsg()
msg := smtp.NewMessage()
msg.Subject(subject)
msg.SetBodyString(mail.TypeTextPlain, message)
msg.SetBodyString(smtp.MIMETypeTextPlain, message)
if n.config.SenderName == "" {
msg.From(n.config.SenderAddress)
} else {
@@ -112,20 +83,8 @@ func (n *Notifier) Notify(ctx context.Context, subject string, message string) (
}
msg.To(n.config.ReceiverAddress)
if err := client.DialAndSend(msg); err != nil {
errShouldBeIgnored := false
// REF: https://github.com/wneessen/go-mail/issues/463
var sendErr *mail.SendError
if errors.As(err, &sendErr) {
if sendErr.Reason == mail.ErrSMTPReset {
errShouldBeIgnored = true
}
}
if !errShouldBeIgnored {
return nil, fmt.Errorf("failed to send mail: %w", err)
}
if err := client.Send(ctx, msg); err != nil {
return nil, fmt.Errorf("failed to send mail: %w", err)
}
return &notifier.NotifyResult{}, nil