105 lines
3.5 KiB
Go
105 lines
3.5 KiB
Go
package email
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"net/mail"
|
||
|
||
"github.com/ssdomei232/goodBaby/internal/meta"
|
||
"github.com/ssdomei232/goodBaby/internal/retry"
|
||
)
|
||
|
||
// EmailAccountConfigValidator 邮箱账号配置验证器
|
||
type EmailAccountConfigValidator struct{}
|
||
|
||
func (v *EmailAccountConfigValidator) GetType() string {
|
||
return AccountType
|
||
}
|
||
|
||
func (v *EmailAccountConfigValidator) Validate(config string) error {
|
||
cfg, err := ParseAccountConfig(config)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
if cfg.SMTPServer == "" {
|
||
return fmt.Errorf("邮箱账号配置中 SMTP 服务器不能为空")
|
||
}
|
||
if cfg.Port <= 0 || cfg.Port > 65535 {
|
||
return fmt.Errorf("邮箱账号配置中 SMTP 端口不合法")
|
||
}
|
||
if cfg.Username == "" {
|
||
return fmt.Errorf("邮箱账号配置中用户名不能为空")
|
||
}
|
||
if cfg.Password == "" {
|
||
return fmt.Errorf("邮箱账号配置中密码不能为空")
|
||
}
|
||
switch cfg.SecurityOrDefault() {
|
||
case SecuritySSL, SecuritySTARTTLS, SecurityNone:
|
||
default:
|
||
return fmt.Errorf("不支持的加密方式: %s", cfg.Security)
|
||
}
|
||
if cfg.From != "" {
|
||
if _, err := mail.ParseAddress(cfg.From); err != nil {
|
||
return fmt.Errorf("发件人地址不合法: %s", cfg.From)
|
||
}
|
||
}
|
||
if cfg.TestDestination != "" {
|
||
if _, err := mail.ParseAddress(cfg.TestDestination); err != nil {
|
||
return fmt.Errorf("测试收件地址不合法: %s", cfg.TestDestination)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// Test 连接 SMTP 服务器并完成认证;填写了测试收件地址时会真的发一封测试邮件
|
||
func (v *EmailAccountConfigValidator) Test(config string) error {
|
||
cfg, err := ParseAccountConfig(config)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
client, err := newSMTPClient(cfg)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
ctx, cancel := context.WithTimeout(context.Background(), retry.TestTimeout)
|
||
defer cancel()
|
||
|
||
if cfg.TestDestination == "" {
|
||
if err := client.DialWithContext(ctx); err != nil {
|
||
return fmt.Errorf("连接 SMTP 服务器失败: %w", err)
|
||
}
|
||
return client.Close()
|
||
}
|
||
|
||
message, err := buildMessage(cfg, cfg.TestDestination, "goodBaby 测试邮件", "这是一封来自 goodBaby 的测试邮件,收到即表示邮箱账号配置正确。")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if err := client.DialAndSendWithContext(ctx, message); err != nil {
|
||
return fmt.Errorf("发送测试邮件失败: %w", err)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (v *EmailAccountConfigValidator) Meta() meta.AccountMeta {
|
||
return meta.AccountMeta{
|
||
Type: AccountType,
|
||
Label: "邮箱 (SMTP)",
|
||
Description: "配置一个用于发信的 SMTP 账号。",
|
||
Docs: "docs/email-config.md",
|
||
Fields: []meta.Field{
|
||
{Key: "smtp_server", Label: "SMTP 服务器", Type: meta.FieldString, Required: true, Placeholder: "smtp.example.com"},
|
||
{Key: "port", Label: "端口", Type: meta.FieldNumber, Required: true, Default: 465, Help: "SSL 一般为 465,STARTTLS 一般为 587"},
|
||
{Key: "security", Label: "加密方式", Type: meta.FieldString, Required: false, Default: SecuritySSL, Help: "可选 ssl / starttls / none,留空按 ssl 处理"},
|
||
{Key: "username", Label: "用户名", Type: meta.FieldString, Required: true, Placeholder: "you@example.com"},
|
||
{Key: "password", Label: "密码 / 授权码", Type: meta.FieldPassword, Required: true, Secret: true},
|
||
{Key: "from", Label: "发件人地址", Type: meta.FieldString, Help: "留空则使用用户名作为发件人"},
|
||
{Key: "test_destination", Label: "测试收件地址", Type: meta.FieldString, Help: "填写后点击“测试”会真的发送一封测试邮件"},
|
||
},
|
||
}
|
||
}
|