Documentation
¶
Overview ¶
Package notification handles dispatching push notifications to subscribed devices via Web Push.
Index ¶
- Constants
- Variables
- type AttachmentInput
- type AttachmentStorer
- type DeviceProvider
- type DeviceResult
- type E2EEnvelope
- type E2EEnvelopeToken
- type EventTracker
- type Handler
- type KeyProvider
- type KeysResponse
- type NotificationAttachment
- type NotificationPayload
- type PushAuthorizer
- type PushSub
- type SendInput
- type SendReport
- type SendRequest
- type SendResponse
- type SendStatus
- type Sender
- type Service
- type VAPIDKeys
- type VAPIDPublicKeyResponse
Constants ¶
const ( DeliveryModeServerTrusted = "server_trusted" DeliveryModeE2E = "e2e" MaxNotificationTitleLen = 64 MaxNotificationBodyLen = 256 )
Variables ¶
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 ¶
VAPIDPublicKey returns the VAPID public key.
type VAPIDPublicKeyResponse ¶
type VAPIDPublicKeyResponse struct {
Key string `json:"key"`
}
VAPIDPublicKeyResponse represents the VAPID public key response.