深化smtp文件的支持
This commit is contained in:
+154
-37
@@ -3,6 +3,8 @@ package aws
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"code.yun.ink/pkg/mailx/interfaces"
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
@@ -11,11 +13,15 @@ import (
|
||||
"github.com/aws/aws-sdk-go/service/ses"
|
||||
)
|
||||
|
||||
// 不支持变更发信人(必须配置好)
|
||||
const (
|
||||
MaxRetries = 3
|
||||
DefaultRegion = "us-east-1"
|
||||
MaxRecipients = 50 // AWS SES limit
|
||||
)
|
||||
|
||||
type Aws struct {
|
||||
interfaces.DefaultEmail
|
||||
// params *interfaces.EmailConfigDataAws
|
||||
sesClient *ses.SES
|
||||
}
|
||||
|
||||
func NewAws() *Aws {
|
||||
@@ -26,65 +32,176 @@ func NewAws() *Aws {
|
||||
}
|
||||
|
||||
func (l *Aws) SetOption(ctx context.Context, opt ...interfaces.Option) (interfaces.EmailInterface, error) {
|
||||
|
||||
for _, o := range opt {
|
||||
o(&l.Options)
|
||||
}
|
||||
|
||||
l.Options.Logger.Infof(ctx, "Aws:%+v", l.Options.Aws)
|
||||
if l.Options.Aws == nil {
|
||||
return nil, errors.New("not aws")
|
||||
return nil, fmt.Errorf("AWS configuration is required")
|
||||
}
|
||||
|
||||
// 验证配置
|
||||
if err := l.validateConfig(); err != nil {
|
||||
return nil, fmt.Errorf("invalid AWS config: %w", err)
|
||||
}
|
||||
|
||||
if l.Options.Aws.Region == "" {
|
||||
l.Options.Aws.Region = "ap-northeast-1"
|
||||
l.Options.Aws.Region = DefaultRegion
|
||||
}
|
||||
|
||||
// 初始化SES客户端
|
||||
if err := l.initSESClient(); err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize SES client: %w", err)
|
||||
}
|
||||
|
||||
// 安全日志输出
|
||||
l.Options.Logger.Infof(ctx, "AWS SES configured - Region:%s Sender:%s",
|
||||
l.Options.Aws.Region, l.Options.Aws.Sender)
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (l *Aws) Send(ctx context.Context, params interfaces.Message) error {
|
||||
if l.Options.Aws == nil {
|
||||
return errors.New("not init")
|
||||
if l.sesClient == nil {
|
||||
return fmt.Errorf("AWS SES client not initialized")
|
||||
}
|
||||
// 配置AWS认证信息
|
||||
|
||||
// 验证消息
|
||||
if err := l.validateMessage(params); err != nil {
|
||||
return fmt.Errorf("invalid message: %w", err)
|
||||
}
|
||||
|
||||
// 重试机制
|
||||
var lastErr error
|
||||
for i := 0; i < MaxRetries; i++ {
|
||||
if i > 0 {
|
||||
l.Options.Logger.Infof(ctx, "Retrying AWS SES email send, attempt %d/%d", i+1, MaxRetries)
|
||||
time.Sleep(time.Duration(i) * time.Second)
|
||||
}
|
||||
|
||||
lastErr = l.sendEmail(ctx, params)
|
||||
if lastErr == nil {
|
||||
l.Options.Logger.Infof(ctx, "AWS SES email sent successfully to %v", params.To)
|
||||
return nil
|
||||
}
|
||||
|
||||
l.Options.Logger.Errorf(ctx, "AWS SES send attempt %d failed: %v", i+1, lastErr)
|
||||
}
|
||||
|
||||
return fmt.Errorf("failed to send AWS SES email after %d attempts: %w", MaxRetries, lastErr)
|
||||
}
|
||||
|
||||
// 验证配置
|
||||
func (l *Aws) validateConfig() error {
|
||||
if l.Options.Aws.AccessId == "" {
|
||||
return errors.New("AccessId is required")
|
||||
}
|
||||
if l.Options.Aws.AccessSecret == "" {
|
||||
return errors.New("AccessSecret is required")
|
||||
}
|
||||
if l.Options.Aws.Sender == "" {
|
||||
return errors.New("Sender is required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 验证消息
|
||||
func (l *Aws) validateMessage(params interfaces.Message) error {
|
||||
if len(params.To) == 0 {
|
||||
return errors.New("at least one recipient is required")
|
||||
}
|
||||
if len(params.To) > MaxRecipients {
|
||||
return fmt.Errorf("too many recipients: %d (max: %d)", len(params.To), MaxRecipients)
|
||||
}
|
||||
if params.Subject == "" {
|
||||
return errors.New("subject is required")
|
||||
}
|
||||
if params.Body == "" {
|
||||
return errors.New("body is required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 初始化SES客户端
|
||||
func (l *Aws) initSESClient() error {
|
||||
config := aws.Config{
|
||||
Region: aws.String(l.Options.Aws.Region), // 设置你的AWS区域
|
||||
Region: aws.String(l.Options.Aws.Region),
|
||||
Credentials: credentials.NewStaticCredentials(l.Options.Aws.AccessId, l.Options.Aws.AccessSecret, ""),
|
||||
}
|
||||
|
||||
// 创建AWS会话
|
||||
sess := session.Must(session.NewSession(&config))
|
||||
|
||||
// 创建SES客户端
|
||||
svc := ses.New(sess)
|
||||
|
||||
toAddress := []*string{}
|
||||
for _, val := range params.To {
|
||||
toAddress = append(toAddress, aws.String(val))
|
||||
sess, err := session.NewSession(&config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create AWS session: %w", err)
|
||||
}
|
||||
|
||||
// 使用SES服务发送邮件
|
||||
_, err := svc.SendEmail(&ses.SendEmailInput{
|
||||
Destination: &ses.Destination{
|
||||
ToAddresses: toAddress,
|
||||
l.sesClient = ses.New(sess)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 发送邮件核心逻辑
|
||||
func (l *Aws) sendEmail(ctx context.Context, params interfaces.Message) error {
|
||||
// 构建收件人列表
|
||||
destination := &ses.Destination{}
|
||||
|
||||
if len(params.To) > 0 {
|
||||
toAddresses := make([]*string, len(params.To))
|
||||
for i, addr := range params.To {
|
||||
toAddresses[i] = aws.String(addr)
|
||||
}
|
||||
destination.ToAddresses = toAddresses
|
||||
}
|
||||
|
||||
if len(params.Cc) > 0 {
|
||||
ccAddresses := make([]*string, len(params.Cc))
|
||||
for i, addr := range params.Cc {
|
||||
ccAddresses[i] = aws.String(addr)
|
||||
}
|
||||
destination.CcAddresses = ccAddresses
|
||||
}
|
||||
|
||||
if len(params.Bcc) > 0 {
|
||||
bccAddresses := make([]*string, len(params.Bcc))
|
||||
for i, addr := range params.Bcc {
|
||||
bccAddresses[i] = aws.String(addr)
|
||||
}
|
||||
destination.BccAddresses = bccAddresses
|
||||
}
|
||||
|
||||
// 构建邮件内容
|
||||
message := &ses.Message{
|
||||
Subject: &ses.Content{
|
||||
Data: aws.String(params.Subject),
|
||||
Charset: aws.String("UTF-8"),
|
||||
},
|
||||
Message: &ses.Message{
|
||||
Body: &ses.Body{
|
||||
Html: &ses.Content{
|
||||
Data: aws.String(params.Body),
|
||||
Charset: aws.String("UTF-8"),
|
||||
},
|
||||
},
|
||||
Subject: &ses.Content{
|
||||
Data: aws.String(params.Subject),
|
||||
Body: &ses.Body{
|
||||
Html: &ses.Content{
|
||||
Data: aws.String(params.Body),
|
||||
Charset: aws.String("UTF-8"),
|
||||
},
|
||||
},
|
||||
Source: aws.String(l.Options.Aws.Sender),
|
||||
})
|
||||
}
|
||||
|
||||
// svc.SendRawEmail()
|
||||
// 发送邮件
|
||||
input := &ses.SendEmailInput{
|
||||
Destination: destination,
|
||||
Message: message,
|
||||
Source: aws.String(l.Options.Aws.Sender),
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
// 设置回复地址
|
||||
if params.ReplyTo != "" {
|
||||
input.ReplyToAddresses = []*string{aws.String(params.ReplyTo)}
|
||||
}
|
||||
|
||||
resp, err := l.sesClient.SendEmailWithContext(ctx, input)
|
||||
if err != nil {
|
||||
return fmt.Errorf("AWS SES API call failed: %w", err)
|
||||
}
|
||||
|
||||
// 记录响应信息
|
||||
if resp != nil && resp.MessageId != nil {
|
||||
l.Options.Logger.Infof(ctx, "AWS SES email sent - MessageId:%s", aws.StringValue(resp.MessageId))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"code.yun.ink/pkg/mailx/interfaces"
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/ses"
|
||||
)
|
||||
|
||||
// 不支持变更发信人(必须配置好)
|
||||
|
||||
type AwsOld struct {
|
||||
interfaces.DefaultEmail
|
||||
// params *interfaces.EmailConfigDataAws
|
||||
}
|
||||
|
||||
func NewAwsOld() *AwsOld {
|
||||
aws := &AwsOld{}
|
||||
aws.Options = interfaces.DefaultOptions()
|
||||
aws.EmailType = interfaces.EmailTypeAws
|
||||
return aws
|
||||
}
|
||||
|
||||
func (l *AwsOld) SetOption(ctx context.Context, opt ...interfaces.Option) (interfaces.EmailInterface, error) {
|
||||
|
||||
for _, o := range opt {
|
||||
o(&l.Options)
|
||||
}
|
||||
|
||||
l.Options.Logger.Infof(ctx, "Aws:%+v", l.Options.Aws)
|
||||
if l.Options.Aws == nil {
|
||||
return nil, errors.New("not aws")
|
||||
}
|
||||
|
||||
if l.Options.Aws.Region == "" {
|
||||
l.Options.Aws.Region = "ap-northeast-1"
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (l *AwsOld) Send(ctx context.Context, params interfaces.Message) error {
|
||||
if l.Options.Aws == nil {
|
||||
return errors.New("not init")
|
||||
}
|
||||
// 配置AWS认证信息
|
||||
config := aws.Config{
|
||||
Region: aws.String(l.Options.Aws.Region), // 设置你的AWS区域
|
||||
Credentials: credentials.NewStaticCredentials(l.Options.Aws.AccessId, l.Options.Aws.AccessSecret, ""),
|
||||
}
|
||||
|
||||
// 创建AWS会话
|
||||
sess := session.Must(session.NewSession(&config))
|
||||
|
||||
// 创建SES客户端
|
||||
svc := ses.New(sess)
|
||||
|
||||
toAddress := []*string{}
|
||||
for _, val := range params.To {
|
||||
toAddress = append(toAddress, aws.String(val))
|
||||
}
|
||||
|
||||
// 使用SES服务发送邮件
|
||||
_, err := svc.SendEmail(&ses.SendEmailInput{
|
||||
Destination: &ses.Destination{
|
||||
ToAddresses: toAddress,
|
||||
},
|
||||
Message: &ses.Message{
|
||||
Body: &ses.Body{
|
||||
Html: &ses.Content{
|
||||
Data: aws.String(params.Body),
|
||||
Charset: aws.String("UTF-8"),
|
||||
},
|
||||
},
|
||||
Subject: &ses.Content{
|
||||
Data: aws.String(params.Subject),
|
||||
Charset: aws.String("UTF-8"),
|
||||
},
|
||||
},
|
||||
Source: aws.String(l.Options.Aws.Sender),
|
||||
})
|
||||
|
||||
// svc.SendRawEmail()
|
||||
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user