notify

package
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Nov 1, 2026 License: GPL-3.0 Imports: 37 Imported by: 0

Documentation

Overview

Package notify provides durable, rule-filtered owner notifications. Channel secrets never appear in API responses, delivery logs, or transport errors.

Index

Constants

View Source
const Redacted = "[configured]"

Variables

View Source
var Kinds = []string{"server.started", "server.stopping", "server.crash", "provider.recovered", "indexer.recovered", "cache.recovered", "playback.start", "playback.stop", "playback.error", "playback.failover", "provider.outage", "indexer.outage", "indexer.budget", "cache.full", "cert.renewed", "update.available", "backup.failed", "user.joined", "title.available", "newsletter.weekly", "test"}

Functions

func PurgeGrantTx added in v0.3.0

func PurgeGrantTx(ctx context.Context, tx *sql.Tx, viewerID int64, grantHash string) error

func PurgeViewerTx added in v0.3.0

func PurgeViewerTx(ctx context.Context, tx *sql.Tx, viewerID int64) error

PurgeViewerTx and PurgeGrantTx participate in the same transaction that deactivates membership or revokes a device grant.

func RunMonitor added in v0.2.4

func RunMonitor(input io.Reader) error

RunMonitor is used only by the executable's notification-monitor entry point. A killed host, network outage, or supervisor killing the whole process group also stops this watcher. An external uptime monitor is required for those.

Types

type Channel

type Channel struct {
	SMTPMode      string            `json:"smtpMode,omitempty"`
	Subscription  *PushSubscription `json:"subscription,omitempty"`
	ID            string            `json:"id"`
	Name          string            `json:"name"`
	Kind          string            `json:"kind"`
	Enabled       bool              `json:"enabled"`
	Events        []string          `json:"events"`
	Endpoint      string            `json:"endpoint"`
	Token         string            `json:"token"`
	Target        string            `json:"target"`
	Username      string            `json:"username"`
	Password      string            `json:"password"`
	From          string            `json:"from"`
	To            string            `json:"to"`
	PerMinute     int               `json:"perMinute"`
	DigestMinutes int               `json:"digestMinutes"`
}

type Event

type Event struct {
	HTML    string    `json:"html,omitempty"`
	Kind    string    `json:"kind"`
	Title   string    `json:"title"`
	Message string    `json:"message"`
	At      time.Time `json:"at"`
}

type EventInfo added in v0.2.4

type EventInfo struct {
	Kind        string `json:"kind"`
	Description string `json:"description"`
	Automatic   bool   `json:"automatic"`
}

EventInfo distinguishes automatic producers from owner API integrations. Existing event identifiers remain accepted for compatibility.

type FavoriteEpisodeDetector added in v0.3.0

type FavoriteEpisodeDetector struct {
	Preferences *ViewerAlerts
	// Episodes must return only visible, currently favorited shows and episodes.
	Episodes func(context.Context, *core.User) ([]core.Title, error)
	Now      func() time.Time
}

FavoriteEpisodeDetector scans only currently authorized favorites. The caller runs it under the cluster leader lease. The database uniqueness constraint also protects against overlap during a leader handoff.

func (*FavoriteEpisodeDetector) Scan added in v0.3.0

type Heartbeat added in v0.2.4

type Heartbeat struct {
	Enabled  bool   `json:"enabled"`
	Endpoint string `json:"endpoint"`
}

type Options

type Options struct {
	// NodeID names this node on the queue rows it claims; a random id is
	// used when empty.
	NodeID     string
	Providers  func() []usenet.ProviderStats
	CacheUsage func() (int64, int64)
	Client     *http.Client
	Now        func() time.Time
	Logger     *slog.Logger
	// Sealer protects stored channel secrets and the push identity
	// (secrets.go); nil stores them as given.
	Sealer config.FieldSealer
}

type PushKeys added in v0.2.4

type PushKeys struct {
	Auth   string `json:"auth"`
	P256dh string `json:"p256dh"`
}

type PushSubscription added in v0.2.4

type PushSubscription struct {
	Endpoint string   `json:"endpoint"`
	Keys     PushKeys `json:"keys"`
}

type Service

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

func New

func New(db *sql.DB, opts Options) *Service

func (*Service) BackupFailed added in v0.2.48

func (s *Service) BackupFailed(ctx context.Context, err error) error

BackupFailed reports a scheduled backup that did not complete.

func (*Service) Channels

func (s *Service) Channels(ctx context.Context) ([]Channel, error)

func (*Service) Drain

func (s *Service) Drain(ctx context.Context) error

func (*Service) Emit

func (s *Service) Emit(ctx context.Context, e Event) error

func (*Service) EmitOnce added in v0.2.48

func (s *Service) EmitOnce(ctx context.Context, key string, e Event) (bool, error)

EmitOnce emits e unless an event was already emitted under key, and reports whether it emitted. The key and the queued deliveries commit in one transaction: a crash or a failed enqueue leaves neither, so a later attempt emits it.

func (*Service) Handler

func (s *Service) Handler() http.Handler

Handler is an owner-only API; mount with /notifications stripped.

func (*Service) Heartbeat added in v0.2.4

func (s *Service) Heartbeat(ctx context.Context) error

Heartbeat is called only after a successful local HTTP probe. The remote monitor must alert when these one-minute pings stop (allow a three-minute grace).

func (*Service) HeartbeatSettings added in v0.2.4

func (s *Service) HeartbeatSettings(ctx context.Context, redact bool) (Heartbeat, error)

func (*Service) MigrateSecrets added in v0.2.4

func (s *Service) MigrateSecrets(ctx context.Context) (int, error)

MigrateSecrets seals channel secrets and the push identity stored in plaintext and returns how many rows it rewrote. A row is replaced only if it is unchanged since read, so it is safe to rerun on any node; a row that does not open is left untouched and reported in the error.

func (*Service) ObserveIndexer

func (s *Service) ObserveIndexer(e newznab.Event)

ObserveIndexer alerts once per outage, retrying failed queue writes at most once a minute. Database waits never hold the shared health mutex.

func (*Service) ObservePlayback

func (s *Service) ObservePlayback(ev playback.Event)

ObservePlayback only enqueues; network delivery runs separately in Run.

func (*Service) Run

func (s *Service) Run(ctx context.Context)

Run is the single queue consumer. Failed sends are recorded without automatic retry, avoiding duplicate delivery when an upstream timeout is ambiguous.

func (*Service) SampleHealth

func (s *Service) SampleHealth(ctx context.Context) error

SampleHealth emits one alert on entering an unhealthy state, rather than one alert for every sample. Normal recovery allows a later outage to alert again.

func (*Service) Save

func (s *Service) Save(ctx context.Context, c Channel) error

func (*Service) SaveHeartbeat added in v0.2.4

func (s *Service) SaveHeartbeat(ctx context.Context, h Heartbeat) error

func (*Service) TitleAvailable

func (s *Service) TitleAvailable(ctx context.Context, title string) error

TitleAvailable lets the discovery/watchlist flow publish availability.

func (*Service) TitleAvailableOnce added in v0.2.48

func (s *Service) TitleAvailableOnce(ctx context.Context, key, title string) error

TitleAvailableOnce publishes availability once per key (an episode, say), however often a caller retries after a failure or crash.

func (*Service) UpdateAvailable added in v0.2.48

func (s *Service) UpdateAvailable(ctx context.Context, current, version string) error

UpdateAvailable announces a release once per version.

func (*Service) WatchProcess added in v0.2.4

func (s *Service) WatchProcess(executable string) (func(bool) error, error)

WatchProcess starts a small independent delivery process. The returned function must be called on normal exit, with clean=false for a fatal run error. A panic must be allowed to terminate the parent so that EOF reaches the watcher.

type ViewerAlerts added in v0.3.0

type ViewerAlerts struct {
	DB     *sql.DB
	Active func(context.Context, int64) (*core.User, error)
	Now    func() time.Time
}

ViewerAlerts keeps viewer notification settings separate from owner channels. Active must consult current membership, not a cached viewer supplied by the caller.

func (*ViewerAlerts) Enabled added in v0.3.0

func (s *ViewerAlerts) Enabled(ctx context.Context, viewer *core.User) (bool, error)

Enabled defaults to false for every viewer without an explicit preference.

func (*ViewerAlerts) SetEnabled added in v0.3.0

func (s *ViewerAlerts) SetEnabled(ctx context.Context, viewer *core.User, enabled bool) error

SetEnabled records the time of each off-to-on transition so detection can suppress episodes already aired before a viewer opted in.

type ViewerPush added in v0.3.0

type ViewerPush struct {
	Notifications *Service
	Preferences   *ViewerAlerts
	Allowed       func(context.Context, *core.User, core.RatingKey) (bool, error)
	Now           func() time.Time
	// Send is optional and permits offline tests; nil uses the web-push transport.
	Send func(context.Context, PushSubscription, Event) error
}

ViewerPush uses viewer-owned destinations, separate from owner channels. Allowed must recheck the favorite show and current episode/show policy.

func (*ViewerPush) Drain added in v0.3.0

func (p *ViewerPush) Drain(ctx context.Context) error

Drain claims recent alerts once per destination across nodes. Claims are intentionally at-most-once: a crash or send failure does not replay a push. Old alerts and alerts preceding subscription or opt-in are never sent.

func (*ViewerPush) PublicKey added in v0.3.0

func (p *ViewerPush) PublicKey(ctx context.Context, viewer *core.User) (string, error)

func (*ViewerPush) PurgeViewer added in v0.3.0

func (p *ViewerPush) PurgeViewer(ctx context.Context, viewerID int64) error

PurgeViewer removes all destinations after membership deactivation or revocation.

func (*ViewerPush) Register added in v0.3.0

func (p *ViewerPush) Register(ctx context.Context, viewer *core.User, subscription PushSubscription, grantHash string) error

Register associates a browser endpoint with its current viewer only.

func (*ViewerPush) Registered added in v0.3.0

func (p *ViewerPush) Registered(ctx context.Context, viewer *core.User, endpoint string) (bool, error)

Registered reports whether this exact browser endpoint belongs to the viewer.

func (*ViewerPush) Unregister added in v0.3.0

func (p *ViewerPush) Unregister(ctx context.Context, viewer *core.User, endpoint string) error

Jump to

Keyboard shortcuts

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