mirror of
https://github.com/multipleof4/ntfy.git
synced 2026-10-08 21:05:21 +00:00
Rename
This commit is contained in:
+12
-12
@@ -57,8 +57,8 @@ type Server struct {
|
||||
unixListener net.Listener
|
||||
smtpServer *smtp.Server
|
||||
smtpServerBackend *smtpBackend
|
||||
smtpSender mailer
|
||||
mailSender emailVerifier
|
||||
notificationMailer messageMailer
|
||||
accountMailer magicLinkMailer
|
||||
topics map[string]*topic
|
||||
visitors map[string]*visitor // ip:<ip> or user:<user>
|
||||
firebaseClient *firebaseClient
|
||||
@@ -184,17 +184,17 @@ const (
|
||||
// New instantiates a new Server. It creates the cache and adds a Firebase
|
||||
// subscriber (if configured).
|
||||
func New(conf *Config) (*Server, error) {
|
||||
var mailer mailer
|
||||
var emailSender emailVerifier // Stays untyped-nil when SMTP is unconfigured, so ensureEmailsEnabled gates correctly
|
||||
var notificationMailer messageMailer
|
||||
var accountEmailer magicLinkMailer // Stays untyped-nil when SMTP is unconfigured, so ensureEmailsEnabled gates correctly
|
||||
if conf.SMTPSenderAddr != "" {
|
||||
mailSender := mail.NewSender(&mail.Config{
|
||||
sender := mail.NewSender(&mail.Config{
|
||||
SMTPAddr: conf.SMTPSenderAddr,
|
||||
SMTPUser: conf.SMTPSenderUser,
|
||||
SMTPPass: conf.SMTPSenderPass,
|
||||
From: conf.SMTPSenderFrom,
|
||||
})
|
||||
mailer = &smtpSender{config: conf, sender: mailSender}
|
||||
emailSender = mailSender
|
||||
notificationMailer = ¬ificationSender{config: conf, sender: sender}
|
||||
accountEmailer = sender
|
||||
}
|
||||
var stripe stripeAPI
|
||||
if payments.Available && conf.StripeSecretKey != "" {
|
||||
@@ -299,8 +299,8 @@ func New(conf *Config) (*Server, error) {
|
||||
webPush: wp,
|
||||
attachment: attachmentStore,
|
||||
firebaseClient: firebaseClient,
|
||||
smtpSender: mailer,
|
||||
mailSender: emailSender,
|
||||
notificationMailer: notificationMailer,
|
||||
accountMailer: accountEmailer,
|
||||
topics: topics,
|
||||
userManager: userManager,
|
||||
messages: messages,
|
||||
@@ -974,7 +974,7 @@ func (s *Server) handlePublishInternal(r *http.Request, v *visitor) (*model.Mess
|
||||
if s.firebaseClient != nil && firebase {
|
||||
go s.sendToFirebase(v, m)
|
||||
}
|
||||
if s.smtpSender != nil && email != "" {
|
||||
if s.notificationMailer != nil && email != "" {
|
||||
go s.sendEmail(v, m, email)
|
||||
}
|
||||
if s.config.TwilioAccount != "" && call != "" {
|
||||
@@ -1136,7 +1136,7 @@ func (s *Server) sendToFirebase(v *visitor, m *model.Message) {
|
||||
|
||||
func (s *Server) sendEmail(v *visitor, m *model.Message, email string) {
|
||||
logvm(v, m).Tag(tagEmail).Field("email", email).Info("Sending email to %s", email)
|
||||
if err := s.smtpSender.Send(v, m, email); err != nil {
|
||||
if err := s.notificationMailer.Send(v, m, email); err != nil {
|
||||
logvm(v, m).Tag(tagEmail).Field("email", email).Err(err).Warn("Unable to send email to %s: %v", email, err.Error())
|
||||
minc(metricEmailsPublishedFailure)
|
||||
return
|
||||
@@ -1236,7 +1236,7 @@ func (s *Server) parsePublishParams(r *http.Request, m *model.Message) (cache bo
|
||||
if email != "" && !emailAddressRegex.MatchString(email) && !toBool(email) {
|
||||
return false, false, "", "", "", false, "", errHTTPBadRequestEmailAddressInvalid
|
||||
}
|
||||
if s.smtpSender == nil && email != "" {
|
||||
if s.notificationMailer == nil && email != "" {
|
||||
return false, false, "", "", "", false, "", errHTTPBadRequestEmailDisabled
|
||||
}
|
||||
call = readParam(r, "x-call", "call")
|
||||
|
||||
@@ -54,7 +54,7 @@ func (s *Server) handleAccountCreate(w http.ResponseWriter, r *http.Request, v *
|
||||
// If an email was provided and email sending is configured, start verification (best-effort).
|
||||
// The address becomes the primary email on verify (the new account has no primary yet); a
|
||||
// failure to send must not fail signup, so we only log it.
|
||||
if newAccount.Email != "" && s.mailSender != nil {
|
||||
if newAccount.Email != "" && s.accountMailer != nil {
|
||||
if u, err := s.userManager.User(newAccount.Username); err != nil {
|
||||
logvr(v, r).Tag(tagAccount).Err(err).Warn("Failed to load new user for email verification")
|
||||
} else if err := s.enqueueEmailVerification(u.ID, newAccount.Email); err != nil {
|
||||
@@ -175,7 +175,7 @@ func (s *Server) handleAccountGet(w http.ResponseWriter, r *http.Request, v *vis
|
||||
response.PhoneNumbers = phoneNumbers
|
||||
}
|
||||
}
|
||||
if s.mailSender != nil {
|
||||
if s.accountMailer != nil {
|
||||
emails, err := s.userManager.Emails(u.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -788,7 +788,7 @@ func (s *Server) enqueueEmailVerification(userID, email string) error {
|
||||
return err
|
||||
}
|
||||
link := s.config.BaseURL + webAppEmailVerifyPathPrefix + token
|
||||
return s.mailSender.SendEmailVerification(email, link)
|
||||
return s.accountMailer.SendEmailVerification(email, link)
|
||||
}
|
||||
|
||||
// handleAccountPasswordResetRequest starts a password reset (POST /v1/account/password/reset/request,
|
||||
@@ -814,7 +814,7 @@ func (s *Server) handleAccountPasswordResetRequest(w http.ResponseWriter, r *htt
|
||||
} else {
|
||||
link := s.config.BaseURL + webAppPasswordResetPathPrefix + token
|
||||
logvr(v, r).Tag(tagAccount).Field("user_id", userID).Info("Sending password reset link")
|
||||
if err := s.mailSender.SendPasswordReset(email, link); err != nil {
|
||||
if err := s.accountMailer.SendPasswordReset(email, link); err != nil {
|
||||
logvr(v, r).Tag(tagAccount).Err(err).Warn("Failed to send password reset email")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"heckel.io/ntfy/v2/util"
|
||||
)
|
||||
|
||||
// captureMailer is a fake emailVerifier that records the magic links it is asked to send, so
|
||||
// captureMailer is a fake magicLinkMailer that records the magic links it is asked to send, so
|
||||
// tests can "click" them without a real SMTP server.
|
||||
type captureMailer struct {
|
||||
verifyLinks map[string]string // email -> verification link
|
||||
@@ -41,7 +41,7 @@ func newEmailTestServer(t *testing.T, databaseURL string) (*Server, *captureMail
|
||||
conf.BaseURL = "https://ntfy.example.com"
|
||||
s := newTestServer(t, conf)
|
||||
mailer := newCaptureMailer()
|
||||
s.mailSender = mailer
|
||||
s.accountMailer = mailer
|
||||
require.Nil(t, s.userManager.AddUser("ben", "ben", user.RoleUser, false))
|
||||
auth := map[string]string{"Authorization": util.BasicAuth("ben", "ben")}
|
||||
return s, mailer, auth
|
||||
@@ -283,7 +283,7 @@ func TestAccount_Signup_WithEmail_SendsVerification(t *testing.T) {
|
||||
conf.BaseURL = "https://ntfy.example.com"
|
||||
s := newTestServer(t, conf)
|
||||
mailer := newCaptureMailer()
|
||||
s.mailSender = mailer
|
||||
s.accountMailer = mailer
|
||||
defer s.closeDatabases()
|
||||
|
||||
// Sign up with an optional email -> account created and a verification link sent
|
||||
@@ -310,7 +310,7 @@ func TestAccount_Signup_WithoutEmail_NoSend(t *testing.T) {
|
||||
conf.BaseURL = "https://ntfy.example.com"
|
||||
s := newTestServer(t, conf)
|
||||
mailer := newCaptureMailer()
|
||||
s.mailSender = mailer
|
||||
s.accountMailer = mailer
|
||||
defer s.closeDatabases()
|
||||
|
||||
// No email -> account created, nothing sent
|
||||
@@ -335,7 +335,7 @@ func TestAccount_Email_ProvisionedNoPrimary(t *testing.T) {
|
||||
conf.AuthUsers = []*user.User{{Name: "prov", Hash: hash, Role: user.RoleUser}}
|
||||
s := newTestServer(t, conf)
|
||||
mailer := newCaptureMailer()
|
||||
s.mailSender = mailer
|
||||
s.accountMailer = mailer
|
||||
defer s.closeDatabases()
|
||||
auth := map[string]string{"Authorization": util.BasicAuth("prov", "provpass")}
|
||||
|
||||
@@ -364,7 +364,7 @@ func TestAccount_PasswordReset_ProvisionedUserNoSend(t *testing.T) {
|
||||
}
|
||||
s := newTestServer(t, conf)
|
||||
mailer := newCaptureMailer()
|
||||
s.mailSender = mailer
|
||||
s.accountMailer = mailer
|
||||
defer s.closeDatabases()
|
||||
|
||||
// Give the provisioned user a verified primary email anyway
|
||||
|
||||
@@ -151,7 +151,7 @@ func TestAccount_Get_Anonymous(t *testing.T) {
|
||||
conf.VisitorAttachmentTotalSizeLimit = 5123
|
||||
conf.AttachmentFileSizeLimit = 512
|
||||
s := newTestServer(t, conf)
|
||||
s.smtpSender = &testMailer{}
|
||||
s.notificationMailer = &testMailer{}
|
||||
defer s.closeDatabases()
|
||||
|
||||
rr := request(t, s, "GET", "/v1/account", "", nil)
|
||||
|
||||
@@ -54,8 +54,8 @@ func (s *Server) execManager() {
|
||||
receivedMailTotal, receivedMailSuccess, receivedMailFailure = s.smtpServerBackend.Counts()
|
||||
}
|
||||
var sentMailTotal, sentMailSuccess, sentMailFailure int64
|
||||
if s.smtpSender != nil {
|
||||
sentMailTotal, sentMailSuccess, sentMailFailure = s.smtpSender.Counts()
|
||||
if s.notificationMailer != nil {
|
||||
sentMailTotal, sentMailSuccess, sentMailFailure = s.notificationMailer.Counts()
|
||||
}
|
||||
|
||||
// Users
|
||||
|
||||
@@ -105,7 +105,7 @@ func (s *Server) ensureCallsEnabled(next handleFunc) handleFunc {
|
||||
|
||||
func (s *Server) ensureEmailsEnabled(next handleFunc) handleFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||
if s.mailSender == nil || s.userManager == nil {
|
||||
if s.accountMailer == nil || s.userManager == nil {
|
||||
return errHTTPNotFound
|
||||
}
|
||||
return next(w, r, v)
|
||||
|
||||
@@ -252,7 +252,7 @@ func (s *Server) handleAccountBillingSubscriptionCreateSuccess(w http.ResponseWr
|
||||
// collision (or any other skip), the generic "no recovery email set" warning on the account page
|
||||
// nudges the user to add one. This is best-effort: failures are logged, never surfaced.
|
||||
func (s *Server) maybeEnqueueBillingEmailVerification(r *http.Request, v *visitor, userID, billingEmail string) {
|
||||
if s.mailSender == nil || s.config.BaseURL == "" || billingEmail == "" || !emailAddressRegex.MatchString(billingEmail) {
|
||||
if s.accountMailer == nil || s.config.BaseURL == "" || billingEmail == "" || !emailAddressRegex.MatchString(billingEmail) {
|
||||
return
|
||||
}
|
||||
emails, err := s.userManager.Emails(userID)
|
||||
|
||||
@@ -45,7 +45,7 @@ func newCheckoutEmailTestServer(t *testing.T, databaseURL string) (*Server, *cap
|
||||
c.SMTPSenderFrom = "noreply@example.com"
|
||||
s := newTestServer(t, c)
|
||||
mailer := newCaptureMailer()
|
||||
s.mailSender = mailer
|
||||
s.accountMailer = mailer
|
||||
require.Nil(t, s.userManager.AddTier(&user.Tier{
|
||||
ID: "ti_123", Code: "starter", StripeMonthlyPriceID: "price_1234", MessageLimit: 100, MessageExpiryDuration: time.Hour,
|
||||
}))
|
||||
|
||||
+13
-13
@@ -740,7 +740,7 @@ func TestServer_PublishMessageInHeaderWithNewlines(t *testing.T) {
|
||||
func TestServer_PublishInvalidTopic(t *testing.T) {
|
||||
forEachBackend(t, func(t *testing.T, databaseURL string) {
|
||||
s := newTestServer(t, newTestConfig(t, databaseURL))
|
||||
s.smtpSender = &testMailer{}
|
||||
s.notificationMailer = &testMailer{}
|
||||
response := request(t, s, "PUT", "/docs", "fail", nil)
|
||||
require.Equal(t, 40010, toHTTPError(t, response.Body.String()).Code)
|
||||
})
|
||||
@@ -1231,7 +1231,7 @@ func TestServer_StatsResetter_MessageLimiter_EmailsLimiter(t *testing.T) {
|
||||
|
||||
c := newTestConfigWithAuthFile(t, databaseURL)
|
||||
s := newTestServer(t, c)
|
||||
s.smtpSender = &testMailer{}
|
||||
s.notificationMailer = &testMailer{}
|
||||
|
||||
// Publish some messages, and check stats
|
||||
for i := 0; i < 3; i++ {
|
||||
@@ -1461,7 +1461,7 @@ func TestServer_PublishTooManyRequests_ShortReplenish(t *testing.T) {
|
||||
func TestServer_PublishTooManyEmails_Defaults(t *testing.T) {
|
||||
forEachBackend(t, func(t *testing.T, databaseURL string) {
|
||||
s := newTestServer(t, newTestConfig(t, databaseURL))
|
||||
s.smtpSender = &testMailer{}
|
||||
s.notificationMailer = &testMailer{}
|
||||
for i := 0; i < 16; i++ {
|
||||
response := request(t, s, "PUT", "/mytopic", fmt.Sprintf("message %d", i), map[string]string{
|
||||
"E-Mail": "test@example.com",
|
||||
@@ -1481,7 +1481,7 @@ func TestServer_PublishTooManyEmails_Replenish(t *testing.T) {
|
||||
c := newTestConfig(t, databaseURL)
|
||||
c.VisitorEmailLimitReplenish = 500 * time.Millisecond
|
||||
s := newTestServer(t, c)
|
||||
s.smtpSender = &testMailer{}
|
||||
s.notificationMailer = &testMailer{}
|
||||
for i := 0; i < 16; i++ {
|
||||
response := request(t, s, "PUT", "/mytopic", fmt.Sprintf("message %d", i), map[string]string{
|
||||
"E-Mail": "test@example.com",
|
||||
@@ -1509,7 +1509,7 @@ func TestServer_PublishTooManyEmails_Replenish(t *testing.T) {
|
||||
func TestServer_PublishDelayedEmail_Fail(t *testing.T) {
|
||||
forEachBackend(t, func(t *testing.T, databaseURL string) {
|
||||
s := newTestServer(t, newTestConfig(t, databaseURL))
|
||||
s.smtpSender = &testMailer{}
|
||||
s.notificationMailer = &testMailer{}
|
||||
response := request(t, s, "PUT", "/mytopic", "fail", map[string]string{
|
||||
"E-Mail": "test@example.com",
|
||||
"Delay": "20 min",
|
||||
@@ -1546,7 +1546,7 @@ func TestServer_PublishEmailNoMailer_Fail(t *testing.T) {
|
||||
func TestServer_PublishEmailAddressInvalid(t *testing.T) {
|
||||
forEachBackend(t, func(t *testing.T, databaseURL string) {
|
||||
s := newTestServer(t, newTestConfig(t, databaseURL))
|
||||
s.smtpSender = &testMailer{}
|
||||
s.notificationMailer = &testMailer{}
|
||||
addresses := []string{
|
||||
"test@example.com, other@example.com",
|
||||
"invalidaddress",
|
||||
@@ -1572,7 +1572,7 @@ func TestServer_PublishEmailVerify_VerifiedAddress(t *testing.T) {
|
||||
conf := newTestConfigWithAuthFile(t, databaseURL)
|
||||
conf.SMTPSenderVerify = true
|
||||
s := newTestServer(t, conf)
|
||||
s.smtpSender = &testMailer{}
|
||||
s.notificationMailer = &testMailer{}
|
||||
defer s.closeDatabases()
|
||||
|
||||
require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser, false))
|
||||
@@ -1602,7 +1602,7 @@ func TestServer_PublishEmailVerify_BoolValue(t *testing.T) {
|
||||
conf := newTestConfigWithAuthFile(t, databaseURL)
|
||||
conf.SMTPSenderVerify = true
|
||||
s := newTestServer(t, conf)
|
||||
s.smtpSender = &testMailer{}
|
||||
s.notificationMailer = &testMailer{}
|
||||
defer s.closeDatabases()
|
||||
|
||||
require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser, false))
|
||||
@@ -1631,7 +1631,7 @@ func TestServer_PublishEmailVerify_BoolValue(t *testing.T) {
|
||||
func TestServer_PublishEmailVerify_BoolValue_NoVerify(t *testing.T) {
|
||||
forEachBackend(t, func(t *testing.T, databaseURL string) {
|
||||
s := newTestServer(t, newTestConfig(t, databaseURL))
|
||||
s.smtpSender = &testMailer{}
|
||||
s.notificationMailer = &testMailer{}
|
||||
|
||||
// "yes" without smtp-sender-verify should fail with invalid address
|
||||
response := request(t, s, "PUT", "/mytopic", "hi", map[string]string{
|
||||
@@ -1647,7 +1647,7 @@ func TestServer_PublishEmailVerify_Anonymous(t *testing.T) {
|
||||
conf := newTestConfigWithAuthFile(t, databaseURL)
|
||||
conf.SMTPSenderVerify = true
|
||||
s := newTestServer(t, conf)
|
||||
s.smtpSender = &testMailer{}
|
||||
s.notificationMailer = &testMailer{}
|
||||
defer s.closeDatabases()
|
||||
|
||||
// Anonymous user should be rejected
|
||||
@@ -1664,7 +1664,7 @@ func TestServer_PublishEmailVerify_NoVerifiedEmails(t *testing.T) {
|
||||
conf := newTestConfigWithAuthFile(t, databaseURL)
|
||||
conf.SMTPSenderVerify = true
|
||||
s := newTestServer(t, conf)
|
||||
s.smtpSender = &testMailer{}
|
||||
s.notificationMailer = &testMailer{}
|
||||
defer s.closeDatabases()
|
||||
|
||||
require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser, false))
|
||||
@@ -1682,7 +1682,7 @@ func TestServer_PublishEmailVerify_NoVerifiedEmails(t *testing.T) {
|
||||
func TestServer_PublishEmailVerify_Disabled_Backwards_Compatible(t *testing.T) {
|
||||
forEachBackend(t, func(t *testing.T, databaseURL string) {
|
||||
s := newTestServer(t, newTestConfig(t, databaseURL))
|
||||
s.smtpSender = &testMailer{}
|
||||
s.notificationMailer = &testMailer{}
|
||||
|
||||
// Without smtp-sender-verify, any email address should work (backwards compatible)
|
||||
response := request(t, s, "PUT", "/mytopic", "hi", map[string]string{
|
||||
@@ -2139,7 +2139,7 @@ func TestServer_PublishAsJSON_WithEmail(t *testing.T) {
|
||||
t.Parallel()
|
||||
mailer := &testMailer{}
|
||||
s := newTestServer(t, newTestConfig(t, databaseURL))
|
||||
s.smtpSender = mailer
|
||||
s.notificationMailer = mailer
|
||||
body := `{"topic":"mytopic","message":"A message","email":"phil@example.com"}`
|
||||
response := request(t, s, "PUT", "/", body, nil)
|
||||
require.Equal(t, 200, response.Code)
|
||||
|
||||
+11
-7
@@ -15,19 +15,23 @@ import (
|
||||
"heckel.io/ntfy/v2/util"
|
||||
)
|
||||
|
||||
type mailer interface {
|
||||
// messageMailer sends notification emails (the email-on-publish feature). It formats a ntfy
|
||||
// message into an email. Implemented by *notificationSender; tests inject testMailer.
|
||||
type messageMailer interface {
|
||||
Send(v *visitor, m *model.Message, to string) error
|
||||
Counts() (total int64, success int64, failure int64)
|
||||
}
|
||||
|
||||
// emailVerifier sends the magic-link emails for email verification and password reset.
|
||||
// magicLinkMailer sends the magic-link emails for email verification and password reset.
|
||||
// *mail.Sender implements it; tests inject a fake to capture the generated links.
|
||||
type emailVerifier interface {
|
||||
type magicLinkMailer interface {
|
||||
SendEmailVerification(to, link string) error
|
||||
SendPasswordReset(to, link string) error
|
||||
}
|
||||
|
||||
type smtpSender struct {
|
||||
// notificationSender adapts a *mail.Sender for notification emails: it formats a model.Message
|
||||
// into an email and tracks success/failure counts.
|
||||
type notificationSender struct {
|
||||
config *Config
|
||||
sender *mail.Sender
|
||||
success int64
|
||||
@@ -35,7 +39,7 @@ type smtpSender struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (s *smtpSender) Send(v *visitor, m *model.Message, to string) error {
|
||||
func (s *notificationSender) Send(v *visitor, m *model.Message, to string) error {
|
||||
return s.withCount(v, m, func() error {
|
||||
message, err := formatMail(s.config.BaseURL, v.ip.String(), s.sender.From(), to, m)
|
||||
if err != nil {
|
||||
@@ -56,13 +60,13 @@ func (s *smtpSender) Send(v *visitor, m *model.Message, to string) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (s *smtpSender) Counts() (total int64, success int64, failure int64) {
|
||||
func (s *notificationSender) Counts() (total int64, success int64, failure int64) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.success + s.failure, s.success, s.failure
|
||||
}
|
||||
|
||||
func (s *smtpSender) withCount(v *visitor, m *model.Message, fn func() error) error {
|
||||
func (s *notificationSender) withCount(v *visitor, m *model.Message, fn func() error) error {
|
||||
err := fn()
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
Reference in New Issue
Block a user