Documentation
¶
Overview ¶
Package mail 提供 SMTP 发信能力。
对外只暴露 Sender 接口与一个后台队列:业务模块把邮件丢进队列就返回, 绝不在请求路径上等 SMTP——对方服务器慢一秒,用户的评论就要多等一秒。
Index ¶
- Constants
- Variables
- type Message
- type Module
- type Sender
- type Service
- func (s *Service) Done() <-chan struct{}
- func (s *Service) Enabled(ctx context.Context) bool
- func (s *Service) Enqueue(ctx context.Context, msg *Message)
- func (s *Service) Run(ctx context.Context)
- func (s *Service) Send(ctx context.Context, msg *Message) error
- func (s *Service) Settings(ctx context.Context) Settings
- func (s *Service) Shutdown(ctx context.Context)
- func (s *Service) Undelivered() int
- type Settings
Constants ¶
const ( // EncryptionNone 明文连接,仅适合同机或内网的中继。 EncryptionNone = "none" // EncryptionStartTLS 先明文连接再升级为 TLS,对应 587 端口。 EncryptionStartTLS = "starttls" // EncryptionTLS 直接建立 TLS 连接,对应 465 端口。 EncryptionTLS = "tls" )
传输加密方式。
const EnvSMTPPassword = "LUMO_SMTP_PASSWORD"
EnvSMTPPassword 是 SMTP 口令的兜底环境变量。
口令的正规去处是后台「邮件发送」里的口令字段(加密入库、接口不回传), 环境变量留给不便改后台的部署:容器编排里注入密钥、或者口令由运维统管。 两处都有时以后台为准——站长刚在界面上填的那一个才是他此刻的意图。
const GroupMail = "mail"
GroupMail 是发信设置分组。
const Name = "mail"
Name 是模块名,也是 App.Provide 的键。
Variables ¶
var ErrDisabled = errors.New("未启用邮件发送")
ErrDisabled 表示未启用发信。
var ErrMissingPassword = fmt.Errorf( "已配置 SMTP 用户名但没有口令:请在「设置 → 邮件发送」里填写,或设置环境变量 %s", EnvSMTPPassword)
ErrMissingPassword 表示配置了用户名却没有任何口令可用。
Functions ¶
This section is empty.
Types ¶
type Message ¶
type Message struct {
// To 是收件人地址,至少一个。
To []string
// Subject 是主题。
Subject string
// Text 是纯文本正文,必填:不是所有客户端都渲染 HTML。
Text string
// HTML 是可选的 HTML 正文,作为替代部分随信发出。
HTML string
}
Message 是一封待发送的邮件。
type Module ¶
type Module struct {
// contains filtered or unexported fields
}
Module 是邮件模块。本模块无数据库表,故不实现 Migrator。
func (*Module) Close ¶
Close 实现 app.Closer:有序停止邮件队列。
app.Close 在 HTTP 排空之后执行(cmd/lumo/serve.go 的 defer),到这里不会 再有业务请求入队。Service.Shutdown 在 ctx 期限内排空剩余邮件,随后取消 消费协程;仍未投递的数量写入日志,便于评估停机窗口内丢失的邮件。
func (*Module) Settings ¶
func (m *Module) Settings() []app.SettingGroup
Settings 实现 app.SettingsProvider。
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service 是发信队列:Enqueue 立即返回,后台单协程按序发送并重试。
func NewService ¶
NewService 构造发信服务;settings 可为 nil(此时始终视为未启用)。
func (*Service) Enqueue ¶
Enqueue 把邮件放入队列,立即返回。
未启用发信时静默丢弃:站点没配 SMTP 是常态,不该让每次评论都在日志里刷错误。 停机开始后同样拒绝入队,但会记一条调试日志,便于排查停机窗口内的丢信。
func (*Service) Run ¶
Run 运行后台发送协程,直到 Shutdown 停止服务。
ctx 只用于单封邮件的发送超时,不作为退出信号:进程收到 SIGTERM 后 HTTP 还要排空,在途请求仍可能入队,消费者若随之退出,这些邮件就无人消费。 退出统一走 Shutdown(由 app.Closer 在 HTTP 排空之后调用), 由它保证「先排空、后停止」的顺序。
func (*Service) Shutdown ¶
Shutdown 有序停止发信队列:拒绝新入队 → 在 ctx 期限内排空 → 停止消费协程。
必须在 HTTP 服务器排空之后调用(app.Closer 的时机),此时不会再有业务请求 入队。ctx 由 app 的 ShutdownTimeout 约束,等待有界,不会拖住进程退出。 未投递数量在停止时快照,由 Undelivered 读取;调用方负责记录。
func (*Service) Undelivered ¶
Undelivered 返回停机时未能投递的邮件数;Shutdown 完成后调用才有意义。
type Settings ¶
type Settings struct {
Enabled bool `json:"enabled"`
Host string `json:"host"`
Port int `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
Encryption string `json:"encryption"`
FromAddress string `json:"fromAddress"`
FromName string `json:"fromName"`
}
Settings 是 mail 分组的有效值。
Password 是解密后的明文,只在这条链路里流转:设置服务 → 发信器。 出接口(GET /api/v1/console/settings/mail)时它恒为空串,见 settings.Group.Mask。
func (*Settings) Credential ¶
Credential 返回本次发信该用的口令:后台填过就用后台的,否则回退环境变量。