notifydelivery

package
v1.118.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 1, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package notifydelivery assembles the email-notification substrate into one startable handle: the settings, preference, and queue stores, the trigger-side enqueuer, the renderer and SMTP sender, and the send worker with its LISTEN/NOTIFY wakeup adapter.

It is the only place that names every layer of the substrate at once. Each layer is a package of its own (pkg/notification for the domain, pkg/notification/smtp for the mail-server settings, internal/notification/* for persistence, rendering, transport and the worker) and knows only the layers below it; the composition happens here.

The package must not import pkg/platform. The composition root (the HTTP server, which owns the portal and admin surfaces the feature serves) passes in the *sql.DB, DSN, encryptor, and branding values it already holds, and brackets Start/Stop around its serve loop.

Index

Constants

HistoryRetention is how long a resolved row survives before the worker's purge removes it -- the window History can report on. Both surfaces show it so a reader knows the listing is recent history, not an archive.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	// DB is the shared platform database pool. Required.
	DB *sql.DB
	// DSN is the raw database DSN for the LISTEN connection. Empty disables
	// LISTEN/NOTIFY; the worker degrades to poll-only.
	DSN string
	// Encryptor protects the SMTP password at rest. Required (may be a
	// nil-wrapping passthrough when encryption is disabled).
	Encryptor smtp.StringEncryptor
	// Branding is the deployment identity emails render with.
	Branding notifyrender.Branding
	// DigestHourUTC is the UTC hour daily digests are scheduled for.
	DigestHourUTC int
	// UnsubscribeURL builds the no-login unsubscribe link for a recipient
	// address (#1001). nil omits the footer link from notification emails.
	UnsubscribeURL func(email string) string
}

Config carries everything the substrate needs. No platform types.

type Handle

type Handle struct {
	// contains filtered or unexported fields
}

Handle owns the running substrate.

func New

func New(cfg Config) (*Handle, error)

New composes the substrate. Returns nil when cfg.DB is nil (no database: the feature is unavailable and every accessor degrades gracefully).

func (*Handle) Enqueuer

func (h *Handle) Enqueuer() *notification.Enqueuer

Enqueuer returns the trigger-side entry point. Nil-safe: a nil handle returns a nil enqueuer, whose Notify drops everything.

func (*Handle) History added in v1.118.0

func (h *Handle) History() notification.HistoryStore

History returns the delivery-history reader the admin monitoring surface and each user's own notification screen read from, or nil when the feature is unavailable. It is the same PostgreSQL store the worker writes through, narrowed to its read contract.

func (*Handle) PortalNotifier

func (h *Handle) PortalNotifier(stores PortalStores, baseURL string) *PortalNotifier

PortalNotifier builds the portal-facing notifier over this handle's enqueuer. Returns nil when the handle is nil (feature unavailable); the caller must then leave the portal's Notifier dependency unset.

func (*Handle) Prefs

func (h *Handle) Prefs() notification.PrefsStore

Prefs returns the preference store, or nil when the feature is unavailable.

func (h *Handle) SendGuestLink(ctx context.Context, to, link string) error

SendGuestLink delivers a one-time view link email directly through the sender (#1001). The send is transactional: the recipient requested it from a share landing page, so it bypasses the queue (no digest deferral) and the preference gate (an opted-out recipient still gets the link they asked for). It uses the same stored SMTP settings and deliverability gate as every other send.

func (*Handle) SendTest

func (h *Handle) SendTest(ctx context.Context, to string) error

SendTest delivers a test email to the given recipient using the currently stored SMTP settings, so an admin can verify the configuration end to end.

The admin API answers a failed test with fixed text that does not vary with the failure mode (#1072), so this log is the only place the underlying error survives. Every failure below is therefore recorded, with the host and port that produced it.

func (*Handle) Settings

func (h *Handle) Settings() smtp.SettingsStore

Settings returns the settings store, or nil when the feature is unavailable.

func (*Handle) Start

func (h *Handle) Start(ctx context.Context)

Start launches the send worker and, when configured, the LISTEN adapter. A LISTEN failure (e.g. missing privilege) degrades to poll-only delivery rather than failing startup. Nil-safe.

func (*Handle) Stop

func (h *Handle) Stop()

Stop shuts down the listener, worker, and enqueuer limiter, waiting for in-flight sends. Nil-safe and idempotent.

type PortalNotifier

type PortalNotifier struct {
	// contains filtered or unexported fields
}

PortalNotifier implements portal.Notifier: it turns portal share and thread events into queued email notifications. All failures are logged, never surfaced -- a share or comment must succeed regardless of notification state.

func NewPortalNotifier

func NewPortalNotifier(enq *notification.Enqueuer, stores PortalStores, baseURL string) *PortalNotifier

NewPortalNotifier builds a notifier over an enqueuer directly; tests and the handle-based constructor below share it.

func (*PortalNotifier) NotifyShare

func (n *PortalNotifier) NotifyShare(ctx context.Context, share *portal.Share, ev portal.ShareEvent)

NotifyShare queues a "shared with you" email for a direct share. Token-only shares (no recipient email) queue nothing: a share targeted by user ID alone carries no email address anywhere in the system.

func (*PortalNotifier) NotifyThreadEvent

func (n *PortalNotifier) NotifyThreadEvent(ctx context.Context, thread *portal.Thread, actorEmail, body string, mentioned []string)

NotifyThreadEvent queues the emails one thread event produces: a mention email for each person the body named, and a "new comment/feedback" email for everyone else attached to the target -- its owner, the thread author, and the people it is shared with.

Mentions are queued first, per recipient: the author chose those addresses, so each one costs a token of their rate limit. The general fan-out then goes out as one batch, because the size of a target's share list is a property of the item rather than something the author picked.

Only a mention that was actually queued removes someone from the general fan-out. A recipient whose mention was dropped -- they muted the mention category, or the enqueue failed -- has been told nothing, so they still get the comment notification they would have had without being named.

type PortalStores

type PortalStores struct {
	Assets         portal.AssetStore
	Collections    portal.CollectionStore
	Prompts        portal.PromptStore
	KnowledgePages knowledgepage.Store
	// Grantees lists the people holding an explicit grant on a thread target.
	// Optional: nil narrows thread notifications to the target owner and the
	// thread author.
	Grantees ThreadGrantees
}

PortalStores bundles the read access the notifier needs to resolve a thread's target (title, deep link, owner email) and the people attached to it.

type ThreadGrantees added in v1.114.0

type ThreadGrantees interface {
	Grantees(ctx context.Context, targetType, targetID string) ([]string, error)
}

ThreadGrantees lists the addresses holding an explicit grant on a thread target: its owner and the recipients of active shares. Satisfied by pkg/portal/mention.Audience.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL