Integrate new SMTP pool lib
This commit is contained in:
parent
e58b2fa669
commit
9d3ca357f6
7 changed files with 68 additions and 56 deletions
|
@ -91,8 +91,7 @@ max_idle = 10
|
|||
username = "xxxxx"
|
||||
password = ""
|
||||
|
||||
# Optional. Inform listmonk on which email format to use
|
||||
# {html|plain|both} default: both
|
||||
# Format to send e-mails in: html|plain|both.
|
||||
email_format = "both"
|
||||
|
||||
# Optional. Some SMTP servers require a FQDN in the hostname.
|
||||
|
@ -100,12 +99,19 @@ max_idle = 10
|
|||
# hostname should be used.
|
||||
hello_hostname = ""
|
||||
|
||||
# Maximum time (milliseconds) to wait per e-mail push.
|
||||
send_timeout = 5000
|
||||
|
||||
# Maximum concurrent connections to the SMTP server.
|
||||
max_conns = 10
|
||||
|
||||
# Time to wait for new activity on a connection before closing
|
||||
# it and removing it from the pool.
|
||||
idle_timeout = "15s"
|
||||
|
||||
# Message send / wait timeout.
|
||||
wait_timeout = "5s"
|
||||
|
||||
# The number of times a message should be retried if sending fails.
|
||||
max_msg_retries = 2
|
||||
|
||||
[smtp.postal]
|
||||
enabled = false
|
||||
host = "my.smtp.server2"
|
||||
|
@ -116,16 +122,27 @@ max_idle = 10
|
|||
username = "xxxxx"
|
||||
password = ""
|
||||
|
||||
# Optional. Inform listmonk on which email format to use
|
||||
# {html|plain|both} default: both
|
||||
# Format to send e-mails in: html|plain|both.
|
||||
email_format = "both"
|
||||
|
||||
# Maximum time (milliseconds) to wait per e-mail push.
|
||||
send_timeout = 5000
|
||||
# Optional. Some SMTP servers require a FQDN in the hostname.
|
||||
# By default, HELLOs go with "localhost". Set this if a custom
|
||||
# hostname should be used.
|
||||
hello_hostname = ""
|
||||
|
||||
# Maximum concurrent connections to the SMTP server.
|
||||
max_conns = 10
|
||||
|
||||
# Time to wait for new activity on a connection before closing
|
||||
# it and removing it from the pool.
|
||||
idle_timeout = "15s"
|
||||
|
||||
# Message send / wait timeout.
|
||||
wait_timeout = "5s"
|
||||
|
||||
# The number of times a message should be retried if sending fails.
|
||||
max_msg_retries = 2
|
||||
|
||||
|
||||
[upload]
|
||||
# File storage backend. "filesystem" or "s3".
|
||||
|
|
6
go.mod
6
go.mod
|
@ -1,13 +1,14 @@
|
|||
module github.com/knadh/listmonk
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/gofrs/uuid v3.2.0+incompatible
|
||||
github.com/jaytaylor/html2text v0.0.0-20200220170450-61d9dc4d7195
|
||||
github.com/jmoiron/sqlx v1.2.0
|
||||
github.com/jordan-wright/email v0.0.0-20200307200233-de844847de93
|
||||
github.com/knadh/goyesql/v2 v2.1.1
|
||||
github.com/knadh/koanf v0.8.1
|
||||
github.com/knadh/smtppool v0.2.0
|
||||
github.com/knadh/stuffbin v1.1.0
|
||||
github.com/labstack/echo v3.3.10+incompatible
|
||||
github.com/labstack/gommon v0.3.0 // indirect
|
||||
|
@ -22,6 +23,3 @@ require (
|
|||
jaytaylor.com/html2text v0.0.0-20200220170450-61d9dc4d7195
|
||||
)
|
||||
|
||||
replace github.com/jordan-wright/email => github.com/knadh/email v0.0.0-20200206100304-6d2c7064c2e8
|
||||
|
||||
go 1.13
|
||||
|
|
4
go.sum
4
go.sum
|
@ -29,6 +29,10 @@ github.com/knadh/goyesql/v2 v2.1.1 h1:Orp5ldaxPM4ozKHfu1m7p6iolJFXDGOpF3/jyOgO6l
|
|||
github.com/knadh/goyesql/v2 v2.1.1/go.mod h1:pMzCA130/ZhEIoMmSmbEFXor3A2dxl5L+JllAc/l64s=
|
||||
github.com/knadh/koanf v0.8.1 h1:4VLACWqrkWRQIup3ooq6lOnaSbOJSNO+YVXnJn/NPZ8=
|
||||
github.com/knadh/koanf v0.8.1/go.mod h1:kVvmDbXnBtW49Czi4c1M+nnOWF0YSNZ8BaKvE/bCO1w=
|
||||
github.com/knadh/smtppool v0.1.1 h1:pSi1Gc5TXOaN/Z/YiqfZbk/vd9dqzXzAfQiss0QSGQU=
|
||||
github.com/knadh/smtppool v0.1.1/go.mod h1:3DJHouXAgPDBz0kC50HukOsdapYSwIEfJGwuip46oCA=
|
||||
github.com/knadh/smtppool v0.2.0 h1:+llTWRljNIVg05MMu9TiefELTNwblexjsd1ALAPXZUs=
|
||||
github.com/knadh/smtppool v0.2.0/go.mod h1:3DJHouXAgPDBz0kC50HukOsdapYSwIEfJGwuip46oCA=
|
||||
github.com/knadh/stuffbin v1.0.0 h1:NQon6PTpLXies4bRFhS3VpLCf6y+jn6YVXU3i2wPQ+M=
|
||||
github.com/knadh/stuffbin v1.0.0/go.mod h1:yVCFaWaKPubSNibBsTAJ939q2ABHudJQxRWZWV5yh+4=
|
||||
github.com/knadh/stuffbin v1.1.0 h1:f5S5BHzZALjuJEgTIOMC9NidEnBJM7Ze6Lu1GHR/lwU=
|
||||
|
|
15
init.go
15
init.go
|
@ -7,11 +7,11 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/knadh/goyesql/v2"
|
||||
goyesqlx "github.com/knadh/goyesql/v2/sqlx"
|
||||
"github.com/knadh/koanf"
|
||||
"github.com/knadh/koanf/maps"
|
||||
"github.com/knadh/listmonk/internal/manager"
|
||||
"github.com/knadh/listmonk/internal/media"
|
||||
|
@ -214,28 +214,27 @@ func initImporter(q *Queries, db *sqlx.DB, app *App) *subimporter.Importer {
|
|||
})
|
||||
}
|
||||
|
||||
// initMessengers initializes various messaging backends.
|
||||
// initMessengers initializes various messenger backends.
|
||||
func initMessengers(m *manager.Manager) messenger.Messenger {
|
||||
// Load SMTP configurations for the default e-mail Messenger.
|
||||
var (
|
||||
mapKeys = ko.MapKeys("smtp")
|
||||
srv = make([]messenger.Server, 0, len(mapKeys))
|
||||
)
|
||||
|
||||
// Load the default SMTP messengers.
|
||||
for _, name := range mapKeys {
|
||||
if !ko.Bool(fmt.Sprintf("smtp.%s.enabled", name)) {
|
||||
lo.Printf("skipped SMTP: %s", name)
|
||||
continue
|
||||
}
|
||||
|
||||
var s messenger.Server
|
||||
if err := ko.Unmarshal("smtp."+name, &s); err != nil {
|
||||
// Read the SMTP config.
|
||||
s := messenger.Server{Name: name}
|
||||
if err := ko.UnmarshalWithConf("smtp."+name, &s, koanf.UnmarshalConf{Tag: "json"}); err != nil {
|
||||
lo.Fatalf("error loading SMTP: %v", err)
|
||||
}
|
||||
s.Name = name
|
||||
s.SendTimeout *= time.Millisecond
|
||||
srv = append(srv, s)
|
||||
|
||||
srv = append(srv, s)
|
||||
lo.Printf("loaded SMTP: %s (%s@%s)", s.Name, s.Username, s.Host)
|
||||
}
|
||||
|
||||
|
|
|
@ -5,10 +5,9 @@ import (
|
|||
"fmt"
|
||||
"math/rand"
|
||||
"net/smtp"
|
||||
"time"
|
||||
|
||||
"github.com/jaytaylor/html2text"
|
||||
"github.com/jordan-wright/email"
|
||||
"github.com/knadh/smtppool"
|
||||
)
|
||||
|
||||
const emName = "email"
|
||||
|
@ -21,21 +20,21 @@ type loginAuth struct {
|
|||
|
||||
// Server represents an SMTP server's credentials.
|
||||
type Server struct {
|
||||
Name string
|
||||
Host string `koanf:"host"`
|
||||
Port int `koanf:"port"`
|
||||
AuthProtocol string `koanf:"auth_protocol"`
|
||||
Username string `koanf:"username"`
|
||||
Password string `koanf:"password"`
|
||||
EmailFormat string `koanf:"email_format"`
|
||||
HelloHostname string `koanf:"hello_hostname"`
|
||||
SendTimeout time.Duration `koanf:"send_timeout"`
|
||||
MaxConns int `koanf:"max_conns"`
|
||||
Name string
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
AuthProtocol string `json:"auth_protocol"`
|
||||
EmailFormat string `json:"email_format"`
|
||||
|
||||
mailer *email.Pool
|
||||
// Rest of the options are embedded directly from the smtppool lib.
|
||||
// The JSON tag is for config unmarshal to work.
|
||||
smtppool.Opt `json:",squash"`
|
||||
|
||||
pool *smtppool.Pool
|
||||
}
|
||||
|
||||
type emailer struct {
|
||||
// Emailer is the SMTP e-mail messenger.
|
||||
type Emailer struct {
|
||||
servers map[string]*Server
|
||||
serverNames []string
|
||||
numServers int
|
||||
|
@ -43,8 +42,8 @@ type emailer struct {
|
|||
|
||||
// NewEmailer creates and returns an e-mail Messenger backend.
|
||||
// It takes multiple SMTP configurations.
|
||||
func NewEmailer(srv ...Server) (Messenger, error) {
|
||||
e := &emailer{
|
||||
func NewEmailer(srv ...Server) (*Emailer, error) {
|
||||
e := &Emailer{
|
||||
servers: make(map[string]*Server),
|
||||
}
|
||||
|
||||
|
@ -62,18 +61,14 @@ func NewEmailer(srv ...Server) (Messenger, error) {
|
|||
default:
|
||||
return nil, fmt.Errorf("unknown SMTP auth type '%s'", s.AuthProtocol)
|
||||
}
|
||||
s.Opt.Auth = auth
|
||||
|
||||
pool, err := email.NewPool(fmt.Sprintf("%s:%d", s.Host, s.Port), s.MaxConns, auth)
|
||||
pool, err := smtppool.New(s.Opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Optional SMTP HELLO hostname.
|
||||
if server.HelloHostname != "" {
|
||||
pool.SetHelloHostname(server.HelloHostname)
|
||||
}
|
||||
|
||||
s.mailer = pool
|
||||
s.pool = pool
|
||||
e.servers[s.Name] = &s
|
||||
e.serverNames = append(e.serverNames, s.Name)
|
||||
}
|
||||
|
@ -83,12 +78,12 @@ func NewEmailer(srv ...Server) (Messenger, error) {
|
|||
}
|
||||
|
||||
// Name returns the Server's name.
|
||||
func (e *emailer) Name() string {
|
||||
func (e *Emailer) Name() string {
|
||||
return emName
|
||||
}
|
||||
|
||||
// Push pushes a message to the server.
|
||||
func (e *emailer) Push(fromAddr string, toAddr []string, subject string, m []byte, atts []*Attachment) error {
|
||||
func (e *Emailer) Push(fromAddr string, toAddr []string, subject string, m []byte, atts []Attachment) error {
|
||||
var key string
|
||||
|
||||
// If there are more than one SMTP servers, send to a random
|
||||
|
@ -100,11 +95,11 @@ func (e *emailer) Push(fromAddr string, toAddr []string, subject string, m []byt
|
|||
}
|
||||
|
||||
// Are there attachments?
|
||||
var files []*email.Attachment
|
||||
var files []smtppool.Attachment
|
||||
if atts != nil {
|
||||
files = make([]*email.Attachment, 0, len(atts))
|
||||
files = make([]smtppool.Attachment, 0, len(atts))
|
||||
for _, f := range atts {
|
||||
a := &email.Attachment{
|
||||
a := smtppool.Attachment{
|
||||
Filename: f.Name,
|
||||
Header: f.Header,
|
||||
Content: make([]byte, len(f.Content)),
|
||||
|
@ -120,7 +115,7 @@ func (e *emailer) Push(fromAddr string, toAddr []string, subject string, m []byt
|
|||
}
|
||||
|
||||
srv := e.servers[key]
|
||||
em := &email.Email{
|
||||
em := smtppool.Email{
|
||||
From: fromAddr,
|
||||
To: toAddr,
|
||||
Subject: subject,
|
||||
|
@ -137,11 +132,11 @@ func (e *emailer) Push(fromAddr string, toAddr []string, subject string, m []byt
|
|||
em.Text = []byte(mtext)
|
||||
}
|
||||
|
||||
return srv.mailer.Send(em, srv.SendTimeout)
|
||||
return srv.pool.Send(em)
|
||||
}
|
||||
|
||||
// Flush flushes the message queue to the server.
|
||||
func (e *emailer) Flush() error {
|
||||
func (e *Emailer) Flush() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,7 @@ import "net/textproto"
|
|||
// for instance, e-mail, SMS etc.
|
||||
type Messenger interface {
|
||||
Name() string
|
||||
|
||||
Push(fromAddr string, toAddr []string, subject string, message []byte, atts []*Attachment) error
|
||||
Push(fromAddr string, toAddr []string, subject string, message []byte, atts []Attachment) error
|
||||
Flush() error
|
||||
}
|
||||
|
||||
|
|
|
@ -360,8 +360,8 @@ func handleSelfExportSubscriberData(c echo.Context) error {
|
|||
[]string{data.Email},
|
||||
"Your profile data",
|
||||
msg.Bytes(),
|
||||
[]*messenger.Attachment{
|
||||
&messenger.Attachment{
|
||||
[]messenger.Attachment{
|
||||
{
|
||||
Name: fname,
|
||||
Content: b,
|
||||
Header: messenger.MakeAttachmentHeader(fname, "base64"),
|
||||
|
|
Loading…
Reference in a new issue