notification

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Apr 23, 2026 License: AGPL-3.0 Imports: 25 Imported by: 0

Documentation

Overview

Package notification handles dispatching push notifications to subscribed devices via Web Push.

Index

Constants

View Source
const (
	DeliveryModeServerTrusted = "server_trusted"
	DeliveryModeE2E           = "e2e"
	MaxNotificationTitleLen   = 64
	MaxNotificationBodyLen    = 256
)

Variables

View Source
var (
	// ErrAttachmentProcessingFailed is returned when an attachment cannot be fetched, encrypted, or stored.
	ErrAttachmentProcessingFailed = errors.New("attachment processing failed")
)

Functions

This section is empty.

Types

type AttachmentInput

type AttachmentInput struct {
	Data     io.Reader // raw file bytes (multipart mode)
	URL      string    // external URL to download (JSON mode)
	MimeType string    // detected or declared MIME type
	Filename string    // sanitized original filename (empty = unknown)
}

AttachmentInput carries the raw attachment source for a send request. Either Data or URL is set, never both.

type AttachmentStorer

type AttachmentStorer interface {
	Store(ctx context.Context, topicID, mimeType string, originalSize int, data []byte) (token string, err error)
}

AttachmentStorer is a cross-domain interface for storing encrypted attachments.

type DeviceProvider

type DeviceProvider interface {
	GetSubscribedDevices(ctx context.Context, userID, topicName string) ([]PushSub, error)
	MarkSubscriptionGone(ctx context.Context, deviceID string) error
}

DeviceProvider defines the interface for device operations needed by notification.

type DeviceResult

type DeviceResult struct {
	DeviceID         string
	StatusCode       int
	Err              error
	SubscriptionGone bool // true if device was deleted due to 410/404
}

DeviceResult holds the result of sending to a single device.

type E2EEnvelope

type E2EEnvelope struct {
	BeeBuzz E2EEnvelopeToken `json:"beebuzz"`
}

E2EEnvelope points a paired device to an opaque encrypted blob stored on the server.

type E2EEnvelopeToken

type E2EEnvelopeToken struct {
	ID     string `json:"id"`
	Token  string `json:"token"`
	SentAt string `json:"sent_at"`
}

E2EEnvelopeToken carries the attachment token for an encrypted payload blob.

type EventTracker

type EventTracker interface {
	// NotificationCreated is called once per Send operation after the push loop completes.
	NotificationCreated(ctx context.Context, userID, topic, source, deliveryMode string, attachmentBytes int64)
	// DeviceDelivered is called for each successful push delivery.
	DeviceDelivered(ctx context.Context, userID, deviceID string)
	// DeviceFailed is called for each failed push delivery.
	DeviceFailed(ctx context.Context, userID string, result DeviceResult)
}

EventTracker reports notification send outcomes for analytics. Implementations bridge to the event/analytics layer via adapters.

type Handler

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

Handler handles notification HTTP requests.

func NewHandler

func NewHandler(svc Sender, pushAuth PushAuthorizer, keyProvider KeyProvider, logger *slog.Logger) *Handler

NewHandler creates a new notification handler.

func (*Handler) Keys

func (h *Handler) Keys(w http.ResponseWriter, r *http.Request)

Keys handles GET /v1/push/keys. Returns the age public keys for all paired devices of the authenticated user.

func (*Handler) Send

func (h *Handler) Send(w http.ResponseWriter, r *http.Request)

Send handles POST /v1/push and POST /v1/push/{topic} requests. Authentication is performed inline via Bearer API token. The request body may be JSON or multipart/form-data (for file attachments). Requires the ExtractBearerToken middleware to run first.

func (*Handler) VAPIDPublicKey

func (h *Handler) VAPIDPublicKey(w http.ResponseWriter, r *http.Request)

VAPIDPublicKey handles requests for the VAPID public key.

type KeyProvider

type KeyProvider interface {
	GetDeviceKeys(ctx context.Context, userID string) ([]device.DeviceKeyDescriptor, error)
}

KeyProvider returns age public keys for a user's devices.

type KeysResponse

type KeysResponse struct {
	Data []device.DeviceKeyDescriptor `json:"data"`
}

KeysResponse is the response for GET /v1/push/keys.

type NotificationAttachment

type NotificationAttachment struct {
	Token    string `json:"token,omitempty"`
	MIME     string `json:"mime,omitempty"`
	Filename string `json:"filename,omitempty"`
}

NotificationAttachment represents an attachment in a notification.

type NotificationPayload

type NotificationPayload struct {
	ID         string                  `json:"id"`
	Title      string                  `json:"title"`
	Body       string                  `json:"body"`
	TopicID    string                  `json:"topic_id,omitempty"`
	Topic      string                  `json:"topic,omitempty"`
	Priority   string                  `json:"priority,omitempty"`
	SentAt     string                  `json:"sent_at"`
	Attachment *NotificationAttachment `json:"attachment,omitempty"`
}

NotificationPayload represents the JSON payload sent to devices.

type PushAuthorizer

type PushAuthorizer interface {
	ValidateAPITokenForTopic(ctx context.Context, token, topicName string) (userID, topicID string, err error)
	ValidateAPIToken(ctx context.Context, token string) (userID string, err error)
}

PushAuthorizer validates API tokens for push operations.

type PushSub

type PushSub struct {
	DeviceID     string
	Endpoint     string
	P256dh       string
	Auth         string
	AgeRecipient string
}

PushSub represents a push subscription used by the notification domain.

type SendInput

type SendInput struct {
	TopicName    string
	Title        string
	Body         string
	Priority     string
	Source       string           // opaque source tag for analytics (e.g., "api", "webhook")
	DeliveryMode string           // delivery mode (e.g., "server_trusted", "e2e")
	Attachment   *AttachmentInput // nil when no attachment
	OpaqueBlob   []byte           // raw E2E-encrypted payload (octet-stream mode)
}

SendInput carries all parameters for a push notification send operation.

type SendReport

type SendReport struct {
	DeviceResults []DeviceResult
	TotalSent     int
	TotalFailed   int
}

SendReport holds detailed results from a send operation.

type SendRequest

type SendRequest struct {
	Title         string `json:"title"`
	Body          string `json:"body"`
	Priority      string `json:"priority,omitempty"`
	AttachmentURL string `json:"attachment_url,omitempty"`
}

SendRequest represents a JSON notification send request body.

func (*SendRequest) Validate

func (r *SendRequest) Validate() []error

Validate validates the send request fields.

type SendResponse

type SendResponse struct {
	Status      SendStatus                   `json:"status"`
	SentCount   int                          `json:"sent_count"`
	TotalCount  int                          `json:"total_count"`
	FailedCount int                          `json:"failed_count"`
	DeviceKeys  []device.DeviceKeyDescriptor `json:"device_keys,omitempty"` // current paired device keys for CLI auto-sync
}

SendResponse represents a notification send response.

type SendStatus

type SendStatus string

SendStatus represents the aggregate outcome of a push send request.

const (
	SendStatusDelivered SendStatus = "delivered"
	SendStatusPartial   SendStatus = "partial"
	SendStatusFailed    SendStatus = "failed"
)

type Sender

type Sender interface {
	Send(ctx context.Context, userID, topicID string, input SendInput, log *slog.Logger) (*SendReport, error)
	VAPIDPublicKey() string
}

Sender is the interface used by Handler to dispatch push notifications. It is satisfied by *Service and by any tracking wrapper.

type Service

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

Service provides notification business logic.

func NewService

func NewService(device DeviceProvider, attachment AttachmentStorer, tracker EventTracker, vapidKeys *VAPIDKeys, subject string, log *slog.Logger) *Service

NewService creates a new notification service. tracker may be nil to disable event tracking.

func (*Service) Send

func (s *Service) Send(ctx context.Context, userID, topicID string, input SendInput, log *slog.Logger) (*SendReport, error)

Send sends a push notification to all subscribed devices for a topic. userID and topicID must already be validated by the caller (e.g., via token authorization). Returns a SendReport with per-device results.

func (*Service) VAPIDPublicKey

func (s *Service) VAPIDPublicKey() string

VAPIDPublicKey returns the VAPID public key.

type VAPIDKeys

type VAPIDKeys struct {
	PublicKey  string
	PrivateKey string
}

VAPIDKeys holds VAPID keys for Web Push API.

type VAPIDPublicKeyResponse

type VAPIDPublicKeyResponse struct {
	Key string `json:"key"`
}

VAPIDPublicKeyResponse represents the VAPID public key response.

Jump to

Keyboard shortcuts

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