From 6de82db7e4d36a544b5d25c28dec7e00869ccd5a Mon Sep 17 00:00:00 2001 From: kolaente Date: Tue, 24 Feb 2026 20:07:12 +0100 Subject: [PATCH] fix: decouple webhook dispatch from email/mailer config The reminder and overdue crons now always run and dispatch webhook events. Email notifications are only sent when both ServiceEnableEmailReminders and MailerEnabled are true. Webhook dispatch errors are logged but no longer abort the cron run. --- pkg/models/listeners.go | 6 ++-- pkg/models/task_overdue_reminder.go | 50 +++++++++++++---------------- pkg/models/task_reminder.go | 32 ++++++++---------- 3 files changed, 37 insertions(+), 51 deletions(-) diff --git a/pkg/models/listeners.go b/pkg/models/listeners.go index 5cbe8bb79..58578091f 100644 --- a/pkg/models/listeners.go +++ b/pkg/models/listeners.go @@ -93,10 +93,8 @@ func RegisterListeners() { RegisterEventForWebhook(&ProjectDeletedEvent{}) RegisterEventForWebhook(&ProjectSharedWithUserEvent{}) RegisterEventForWebhook(&ProjectSharedWithTeamEvent{}) - if config.ServiceEnableEmailReminders.GetBool() { - RegisterEventForWebhook(&TaskReminderFiredEvent{}) - RegisterEventForWebhook(&TaskOverdueEvent{}) - } + RegisterEventForWebhook(&TaskReminderFiredEvent{}) + RegisterEventForWebhook(&TaskOverdueEvent{}) } } diff --git a/pkg/models/task_overdue_reminder.go b/pkg/models/task_overdue_reminder.go index b5bb59bf4..31b669e1f 100644 --- a/pkg/models/task_overdue_reminder.go +++ b/pkg/models/task_overdue_reminder.go @@ -111,15 +111,6 @@ type userWithTasks struct { // RegisterOverdueReminderCron registers a function which checks once a day for tasks that are overdue and not done. func RegisterOverdueReminderCron() { - if !config.ServiceEnableEmailReminders.GetBool() { - return - } - - if !config.MailerEnabled.GetBool() { - log.Info("Mailer is disabled, not sending overdue per mail") - return - } - err := cron.Schedule("* * * * *", func() { s := db.NewSession() defer s.Close() @@ -146,6 +137,28 @@ func RegisterOverdueReminderCron() { return } + // Dispatch webhook events, deduplicated by task ID across all users + dispatchedTasks := make(map[int64]bool) + for _, ut := range uts { + for _, t := range ut.tasks { + if dispatchedTasks[t.ID] { + continue + } + dispatchedTasks[t.ID] = true + err = events.Dispatch(&TaskOverdueEvent{ + Task: t, + Project: projects[t.ProjectID], + }) + if err != nil { + log.Errorf("[Undone Overdue Tasks Reminder] Could not dispatch overdue event for task %d: %s", t.ID, err) + } + } + } + + if !config.ServiceEnableEmailReminders.GetBool() || !config.MailerEnabled.GetBool() { + return + } + for _, ut := range uts { var n notifications.Notification = &UndoneTasksOverdueNotification{ User: ut.user, @@ -173,25 +186,6 @@ func RegisterOverdueReminderCron() { log.Debugf("[Undone Overdue Tasks Reminder] Sent reminder email for %d tasks to user %d", len(ut.tasks), ut.user.ID) } - - // Dispatch webhook events, deduplicated by task ID across all users - dispatchedTasks := make(map[int64]bool) - for _, ut := range uts { - for _, t := range ut.tasks { - if dispatchedTasks[t.ID] { - continue - } - dispatchedTasks[t.ID] = true - err = events.Dispatch(&TaskOverdueEvent{ - Task: t, - Project: projects[t.ProjectID], - }) - if err != nil { - log.Errorf("[Undone Overdue Tasks Reminder] Could not dispatch overdue event for task %d: %s", t.ID, err) - return - } - } - } }) if err != nil { log.Fatalf("Could not register undone overdue tasks reminder cron: %s", err) diff --git a/pkg/models/task_reminder.go b/pkg/models/task_reminder.go index 1b8c40de5..8e5c37b41 100644 --- a/pkg/models/task_reminder.go +++ b/pkg/models/task_reminder.go @@ -341,15 +341,6 @@ func getTasksWithRemindersDueAndTheirUsers(s *xorm.Session, now time.Time) (remi // RegisterReminderCron registers a cron function which runs every minute to check if any reminders are due the // next minute to send emails. func RegisterReminderCron() { - if !config.ServiceEnableEmailReminders.GetBool() { - return - } - - if !config.MailerEnabled.GetBool() { - log.Info("Mailer is disabled, not sending reminders per mail") - return - } - tz := config.GetTimeZone() log.Debugf("[Task Reminder Cron] Timezone is %s", tz) @@ -371,16 +362,6 @@ func RegisterReminderCron() { log.Debugf("[Task Reminder Cron] Sending %d reminders", len(reminders)) - for _, n := range reminders { - err = notifications.Notify(n.User, n) - if err != nil { - log.Errorf("[Task Reminder Cron] Could not notify user %d: %s", n.User.ID, err) - return - } - - log.Debugf("[Task Reminder Cron] Sent reminder email for task %d to user %d", n.Task.ID, n.User.ID) - } - // Dispatch webhook events, deduplicated by task ID dispatchedTasks := make(map[int64]bool) for _, n := range reminders { @@ -394,8 +375,21 @@ func RegisterReminderCron() { }) if err != nil { log.Errorf("[Task Reminder Cron] Could not dispatch reminder event for task %d: %s", n.Task.ID, err) + } + } + + if !config.ServiceEnableEmailReminders.GetBool() || !config.MailerEnabled.GetBool() { + return + } + + for _, n := range reminders { + err = notifications.Notify(n.User, n) + if err != nil { + log.Errorf("[Task Reminder Cron] Could not notify user %d: %s", n.User.ID, err) return } + + log.Debugf("[Task Reminder Cron] Sent reminder email for task %d to user %d", n.Task.ID, n.User.ID) } }) if err != nil {