From 8987f0890a7d181f98f870c59310a081013ffeac Mon Sep 17 00:00:00 2001 From: kolaente Date: Thu, 8 Jan 2026 15:51:31 +0100 Subject: [PATCH] fix(mail): disable queue when mailer disabled (#2069) - Don't create the mail queue when the mailer is disabled to prevent `SendMail()` from blocking indefinitely - Add guard clause in `SendMail()` to return early when mailer is disabled or queue is nil - Add test to verify notifications don't block when mailer is disabled This implements the changes from #1080 with the review feedback addressed (using `package notifications` instead of `package notifications_test`). Closes #1080 --- pkg/mail/mail.go | 5 +- pkg/mail/send_mail.go | 4 ++ pkg/notifications/notify_disabled_test.go | 59 +++++++++++++++++++++++ 3 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 pkg/notifications/notify_disabled_test.go diff --git a/pkg/mail/mail.go b/pkg/mail/mail.go index 21715a6e6..2cc9e50f5 100644 --- a/pkg/mail/mail.go +++ b/pkg/mail/mail.go @@ -84,12 +84,13 @@ func getClient() (*mail.Client, error) { // StartMailDaemon starts the mail daemon func StartMailDaemon() { - Queue = make(chan *mail.Msg, config.MailerQueuelength.GetInt()) - if !config.MailerEnabled.GetBool() { + Queue = nil return } + Queue = make(chan *mail.Msg, config.MailerQueuelength.GetInt()) + if config.MailerHost.GetString() == "" { log.Warning("Mailer seems to be not configured! Please see the config docs for more details.") return diff --git a/pkg/mail/send_mail.go b/pkg/mail/send_mail.go index 57e02c1d6..c19bdd81f 100644 --- a/pkg/mail/send_mail.go +++ b/pkg/mail/send_mail.go @@ -128,6 +128,10 @@ func SendMail(opts *Opts) { return } + if !config.MailerEnabled.GetBool() || Queue == nil { + return + } + m := getMessage(opts) Queue <- m } diff --git a/pkg/notifications/notify_disabled_test.go b/pkg/notifications/notify_disabled_test.go new file mode 100644 index 000000000..55b950c6a --- /dev/null +++ b/pkg/notifications/notify_disabled_test.go @@ -0,0 +1,59 @@ +// Vikunja is a to-do list application to facilitate your life. +// Copyright 2018-present Vikunja and contributors. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package notifications + +import ( + "testing" + "time" + + "code.vikunja.io/api/pkg/config" + "code.vikunja.io/api/pkg/i18n" +) + +type disabledMailNotifiable struct{} + +func (d *disabledMailNotifiable) RouteForMail() (string, error) { return "test@example.com", nil } +func (d *disabledMailNotifiable) RouteForDB() int64 { return 1 } +func (d *disabledMailNotifiable) ShouldNotify() (bool, error) { return true, nil } +func (d *disabledMailNotifiable) Lang() string { return "en" } + +type disabledMailNotification struct{} + +func (n *disabledMailNotification) ToMail(string) *Mail { + return NewMail().Subject("Test").Line("Test") +} +func (n *disabledMailNotification) ToDB() any { return nil } +func (n *disabledMailNotification) Name() string { return "disabled.mail.notification" } + +func TestNotifyDoesNotBlockWhenMailerDisabled(t *testing.T) { + config.InitDefaultConfig() + config.MailerEnabled.Set(false) + i18n.Init() + + done := make(chan struct{}) + go func() { + _ = Notify(&disabledMailNotifiable{}, &disabledMailNotification{}) + close(done) + }() + + select { + case <-done: + // Success - Notify returned without blocking + case <-time.After(1 * time.Second): + t.Fatal("Notify blocked when mailer was disabled") + } +}