Add queued email notifications for collaboration events

This commit is contained in:
2026-07-22 08:51:42 -04:00
parent b6d2ded141
commit 09f51d1ef4
20 changed files with 1507 additions and 102 deletions

View File

@@ -24,12 +24,13 @@ import (
)
type App struct {
cfg config.Config
logger *slog.Logger
db database.DB
docs *docs.Service
hub *realtime.Hub
server *http.Server
cfg config.Config
logger *slog.Logger
db database.DB
docs *docs.Service
hub *realtime.Hub
server *http.Server
emailQueue *email.Queue
}
func New(ctx context.Context, cfg config.Config, logger *slog.Logger) (*App, error) {
@@ -69,13 +70,33 @@ func New(ctx context.Context, cfg config.Config, logger *slog.Logger) (*App, err
collabRepo := collaboration.NewRepository(db.SQL())
var emailSender collaboration.EmailSender
if cfg.Email.SMTPHost != "" {
emailSender = email.NewSMTPSender(cfg.Email, logger)
authService.SetEmailSender(emailSender)
} else {
emailSender = email.NewNoOpSender(logger)
var emailQueue *email.Queue
emailRenderer := email.CollaborationRenderer{InstanceName: cfg.Email.InstanceName}
switch cfg.Email.ResolvedProvider() {
case "postmark":
pm := email.NewPostmarkSender(cfg.Email.PostmarkToken, cfg.Email.From, cfg.Email.PostmarkAPIURL, logger)
emailSender = pm
authService.SetEmailSender(pm)
case "smtp":
sm := email.NewSMTPSender(cfg.Email, logger)
emailSender = sm
authService.SetEmailSender(sm)
default:
noOp := email.NewNoOpSender(logger)
emailSender = noOp
authService.SetEmailSender(noOp)
}
collabService := collaboration.NewService(collabRepo, emailSender, hub, logger)
// The durable queue wraps whichever sender is configured so retries
// survive process restarts. It is nil-safe in the service: if absent,
// notifications stay in-app only.
emailQueue = email.NewQueue(db.SQL(), emailSender, logger)
collabService := collaboration.NewServiceWithOptions(collabRepo, hub, logger, collaboration.Options{
EmailSender: emailSender,
EmailQueue: emailQueue,
EmailRenderer: emailRenderer,
UserLookup: authRepo,
PublicOrigin: cfg.Auth.PublicOrigin,
})
service.OnChange(func(change docs.DocumentChange) {
hub.Broadcast(realtime.Event{Type: "document_version", Data: change})
@@ -110,18 +131,22 @@ func New(ctx context.Context, cfg config.Config, logger *slog.Logger) (*App, err
}
return &App{
cfg: cfg,
logger: logger,
db: db,
docs: service,
hub: hub,
server: server,
cfg: cfg,
logger: logger,
db: db,
docs: service,
hub: hub,
server: server,
emailQueue: emailQueue,
}, nil
}
func (a *App) Run(ctx context.Context) error {
go a.watchDocuments(ctx)
go a.syncPoll(ctx)
if a.emailQueue != nil {
go a.emailQueue.Run(ctx)
}
go func() {
<-ctx.Done()