Documentation
¶
Overview ¶
Package notify delivers panel events to the outside world.
The panel already recorded everything worth knowing in its event log, but a log only helps someone who is already looking. A node going offline at 3am, a subscriber hitting their quota, a server payment coming due — those are worth pushing rather than waiting to be read.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func TelegramText ¶
TelegramText renders an event as the message a human reads on their phone.
func ValidateWebhookURL ¶
ValidateWebhookURL rejects destinations a panel should never be talked into calling. The URL is operator-supplied, but an operator is not the only person who can end up owning an admin session, and a panel that will POST to any address is a request forgery primitive pointed at its own private network.
Types ¶
type Dispatcher ¶
type Dispatcher struct {
// contains filtered or unexported fields
}
Dispatcher fans events out to every channel subscribed to them.
Delivery is deliberately decoupled from whatever produced the event: an admin clicking "disable user" must not wait on someone's webhook endpoint, and a dead endpoint must not turn into a failed API request. Events go onto a buffered queue and a worker drains it.
func New ¶
func New(store Store, log *slog.Logger) *Dispatcher
New starts a dispatcher. Call Close to drain and stop it.
func (*Dispatcher) Close ¶
func (d *Dispatcher) Close()
func (*Dispatcher) Publish ¶
func (d *Dispatcher) Publish(e domain.Event)
Publish queues an event. It never blocks: a panel that stalls because a notification queue is full is a worse outcome than a dropped notification, so a full queue drops and says so.
func (*Dispatcher) Test ¶
func (d *Dispatcher) Test(ctx context.Context, c domain.NotificationChannel, e domain.Event) error
Test delivers one event to one channel and reports the outcome, bypassing both the queue and the channel's event subscription. An operator setting a webhook up needs the answer now, not the next time something happens to break — and they explicitly asked for this delivery, so the subscription filter would only get in the way.
It records the attempt like any other, so a failed test is visible in the delivery log alongside real traffic.
type Store ¶
type Store interface {
EnabledChannels(ctx context.Context) ([]domain.NotificationChannel, error)
RecordDelivery(ctx context.Context, d domain.NotificationDelivery) error
}
Store is the slice of persistence the dispatcher needs.
type WebhookPayload ¶
type WebhookPayload struct {
Event domain.EventKind `json:"event"`
Actor string `json:"actor,omitempty"`
Subject string `json:"subject,omitempty"`
Message string `json:"message,omitempty"`
Meta json.RawMessage `json:"meta,omitempty"`
Timestamp time.Time `json:"timestamp"`
Panel string `json:"panel"`
}
WebhookPayload is the JSON body posted to a webhook endpoint. It is a stable shape on purpose: receivers switch on `event` and read `subject`, and adding fields must not move the ones they already read.