webhook

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: 19 Imported by: 0

Documentation

Overview

Package webhook manages webhooks that dispatch push notifications on incoming HTTP payloads.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrWebhookNotFound is returned when a webhook does not exist or does not belong to the user.
	ErrWebhookNotFound = errors.New("webhook not found")
	// ErrWebhookInactive is returned when a webhook token resolves to a revoked webhook.
	ErrWebhookInactive = errors.New("webhook is inactive")
	// ErrPayloadExtraction is returned when gjson cannot find the configured path in the payload.
	ErrPayloadExtraction = errors.New("failed to extract fields from payload")
	// ErrAtLeastOneTopic is returned when a webhook request contains no topics.
	ErrAtLeastOneTopic = errors.New("at least one topic is required")
	// ErrInvalidTopicSelection is returned when one or more topics are invalid for the user.
	ErrInvalidTopicSelection = errors.New("invalid topic selection")
	// ErrWebhookDeliveryFailed is returned when every dispatch attempt for a webhook fails.
	ErrWebhookDeliveryFailed = errors.New("webhook delivery failed")

	// ErrInspectSessionNotFound is returned when an inspect session does not exist or has expired.
	ErrInspectSessionNotFound = errors.New("inspect session not found")
	// ErrInspectNotWaiting is returned when trying to capture a payload on a non-waiting session.
	ErrInspectNotWaiting = errors.New("inspect session is not in waiting state")
	// ErrInspectNotCaptured is returned when trying to finalize without a captured payload.
	ErrInspectNotCaptured = errors.New("no payload has been captured")
	// ErrInspectSessionExpired is returned when an inspect session has expired.
	ErrInspectSessionExpired = errors.New("inspect session expired")
)

Functions

This section is empty.

Types

type CreateInspectSessionRequest

type CreateInspectSessionRequest struct {
	Name        string   `json:"name"`
	Description string   `json:"description"`
	Topics      []string `json:"topics"`
	Priority    string   `json:"priority"`
}

CreateInspectSessionRequest is the request body for POST /webhooks/inspect.

func (*CreateInspectSessionRequest) Validate

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

Validate validates the create inspect session request fields.

type CreateWebhookRequest

type CreateWebhookRequest struct {
	Name        string      `json:"name"`
	Description string      `json:"description"`
	PayloadType PayloadType `json:"payload_type"`
	TitlePath   string      `json:"title_path"`
	BodyPath    string      `json:"body_path"`
	Priority    string      `json:"priority"`
	Topics      []string    `json:"topics"`
}

CreateWebhookRequest is the request body for POST /webhooks.

func (*CreateWebhookRequest) Validate

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

Validate validates the create webhook request fields.

type CreatedWebhookResponse

type CreatedWebhookResponse struct {
	ID    string `json:"id"`
	Token string `json:"token"`
	Name  string `json:"name"`
}

CreatedWebhookResponse is returned on POST /webhooks (one-time token reveal).

type DispatchReport

type DispatchReport struct {
	TotalSent int
}

DispatchReport contains webhook-local delivery reporting needed for logging.

type Dispatcher

type Dispatcher interface {
	Dispatch(ctx context.Context, userID, topicID, topicName, title, body, priority string, log *slog.Logger) (*DispatchReport, error)
}

Dispatcher dispatches push notifications for a given user/topic.

type FinalizeInspectRequest

type FinalizeInspectRequest struct {
	TitlePath string `json:"title_path"`
	BodyPath  string `json:"body_path"`
}

FinalizeInspectRequest is the request body for POST /webhooks/inspect/finalize.

func (*FinalizeInspectRequest) Validate

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

Validate validates the finalize inspect request fields.

type Handler

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

Handler handles webhook HTTP requests.

func NewHandler

func NewHandler(svc *Service, hookURL string, logger *slog.Logger) *Handler

NewHandler creates a new webhook handler.

func (*Handler) CreateInspectSession

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

CreateInspectSession handles POST /webhooks/inspect.

func (*Handler) CreateWebhook

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

CreateWebhook handles POST /webhooks.

func (*Handler) DeleteWebhook

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

DeleteWebhook handles DELETE /webhooks/{webhookID}.

func (*Handler) FinalizeInspect

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

FinalizeInspect handles POST /webhooks/inspect/finalize.

func (*Handler) GetInspectSession

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

GetInspectSession handles GET /webhooks/inspect.

func (*Handler) ListWebhooks

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

ListWebhooks handles GET /webhooks.

func (*Handler) Receive

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

Receive handles incoming webhook payloads.

func (*Handler) RegenerateToken

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

RegenerateToken handles POST /webhooks/{webhookID}/token.

func (*Handler) UpdateWebhook

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

UpdateWebhook handles PATCH /webhooks/{webhookID}.

type InspectSession

type InspectSession struct {
	UserID      string
	TokenHash   string
	Token       string
	Name        string
	Description string
	Priority    string
	Topics      []string
	Status      InspectStatus
	Payload     json.RawMessage
	ExpiresAt   time.Time
	CapturedAt  *time.Time
}

type InspectSessionResponse

type InspectSessionResponse struct {
	Token     string        `json:"token"`
	URL       string        `json:"url"`
	Status    InspectStatus `json:"status"`
	ExpiresAt time.Time     `json:"expires_at"`
}

InspectSessionResponse is the HTTP response for inspect session operations.

type InspectSessionStatusResponse

type InspectSessionStatusResponse struct {
	Status     InspectStatus   `json:"status"`
	Payload    json.RawMessage `json:"payload,omitempty"`
	CapturedAt *time.Time      `json:"captured_at,omitempty"`
	ExpiresAt  time.Time       `json:"expires_at"`
}

InspectSessionStatusResponse is the HTTP response for GET /webhooks/inspect.

type InspectStatus

type InspectStatus string
const (
	InspectStatusWaiting   InspectStatus = "waiting"
	InspectStatusCaptured  InspectStatus = "captured"
	InspectStatusCompleted InspectStatus = "completed"
	InspectStatusExpired   InspectStatus = "expired"
)

type InspectStore

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

func NewInspectStore

func NewInspectStore() *InspectStore

NewInspectStore returns a new in-memory inspect session store.

func (*InspectStore) Capture

func (s *InspectStore) Capture(tokenHash string, payload json.RawMessage) error

Capture records the payload for the inspect session identified by token hash.

func (*InspectStore) Create

func (s *InspectStore) Create(userID, name, description, priority string, topics []string) (*InspectSession, error)

Create generates a new inspect session for the given user, replacing any previous one.

func (*InspectStore) Delete

func (s *InspectStore) Delete(userID string)

Delete removes the inspect session for the given user.

func (*InspectStore) GetByTokenHash

func (s *InspectStore) GetByTokenHash(tokenHash string) *InspectSession

GetByTokenHash returns the inspect session matching the given token hash, or nil if not found or expired.

func (*InspectStore) GetByUserID

func (s *InspectStore) GetByUserID(userID string) *InspectSession

GetByUserID returns the inspect session for the given user, or nil if not found or expired.

type PayloadType

type PayloadType string

PayloadType identifies how a webhook payload should be parsed.

const (
	PayloadTypeBeebuzz PayloadType = "beebuzz"
	PayloadTypeCustom  PayloadType = "custom"
)

type ReceiveResponse

type ReceiveResponse struct {
	Status      ReceiveStatus `json:"status"`
	SentCount   int           `json:"sent_count"`
	TotalCount  int           `json:"total_count"`
	FailedCount int           `json:"failed_count"`
}

ReceiveResponse is returned on successful webhook delivery.

type ReceiveStatus

type ReceiveStatus string

ReceiveStatus represents the outcome of a webhook delivery attempt.

const (
	ReceiveStatusDelivered ReceiveStatus = "delivered"
	ReceiveStatusPartial   ReceiveStatus = "partial"
	ReceiveStatusFailed    ReceiveStatus = "failed"
)

type RegenerateTokenResponse

type RegenerateTokenResponse struct {
	Token string `json:"token"`
}

RegenerateTokenResponse is returned when a webhook token is regenerated.

type Repository

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

Repository provides data access for the webhook domain.

func NewRepository

func NewRepository(db *sqlx.DB) *Repository

NewRepository creates a new webhook repository.

func (*Repository) AddTopic

func (r *Repository) AddTopic(ctx context.Context, webhookID, topicID string) error

AddTopic associates a webhook with a topic.

func (*Repository) Create

func (r *Repository) Create(ctx context.Context, userID, name, description string, payloadType PayloadType, tokenHash, titlePath, bodyPath, priority string) (string, error)

Create inserts a new webhook. tokenHash must be the SHA-256 hash of the raw token.

func (*Repository) CreateWithTopics

func (r *Repository) CreateWithTopics(ctx context.Context, userID, name, description string, payloadType PayloadType, tokenHash, titlePath, bodyPath, priority string, topicIDs []string) (string, error)

CreateWithTopics atomically creates a webhook and its topic associations.

func (*Repository) DeleteTopic

func (r *Repository) DeleteTopic(ctx context.Context, webhookID, topicID string) error

DeleteTopic removes a topic association from a webhook.

func (*Repository) GetByID

func (r *Repository) GetByID(ctx context.Context, userID, webhookID string) (*Webhook, error)

GetByID retrieves a webhook by ID and user. Returns nil, nil if not found.

func (*Repository) GetByTokenHash

func (r *Repository) GetByTokenHash(ctx context.Context, tokenHash string) (*Webhook, error)

GetByTokenHash looks up an active webhook by its token hash. Returns nil, nil if not found.

func (*Repository) GetByUser

func (r *Repository) GetByUser(ctx context.Context, userID string) ([]Webhook, error)

GetByUser retrieves all active webhooks for a user.

func (*Repository) GetTopicIDs

func (r *Repository) GetTopicIDs(ctx context.Context, webhookID string) ([]string, error)

GetTopicIDs retrieves topic IDs for a webhook.

func (*Repository) GetTopics

func (r *Repository) GetTopics(ctx context.Context, webhookID string) ([]string, error)

GetTopics retrieves topic names for a webhook.

func (*Repository) GetTopicsWithIDs

func (r *Repository) GetTopicsWithIDs(ctx context.Context, webhookID string) ([]WebhookTopic, error)

GetTopicsWithIDs retrieves topic IDs and names for a webhook.

func (*Repository) Revoke

func (r *Repository) Revoke(ctx context.Context, userID, webhookID string) error

Revoke marks a webhook as inactive.

func (*Repository) TouchLastUsedAt

func (r *Repository) TouchLastUsedAt(ctx context.Context, webhookID string) error

TouchLastUsedAt sets last_used_at to the current time for the given webhook.

func (*Repository) Update

func (r *Repository) Update(ctx context.Context, userID, webhookID, name, description string, payloadType PayloadType, titlePath, bodyPath, priority string) error

Update updates mutable fields of a webhook.

func (*Repository) UpdateTokenHash

func (r *Repository) UpdateTokenHash(ctx context.Context, userID, webhookID, newTokenHash string) error

UpdateTokenHash replaces the token hash for a webhook owned by userID.

func (*Repository) UpdateWithTopics

func (r *Repository) UpdateWithTopics(ctx context.Context, userID, webhookID, name, description string, payloadType PayloadType, titlePath, bodyPath, priority string, topicIDs []string) error

UpdateWithTopics atomically updates a webhook and replaces topic associations.

type Service

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

Service provides webhook business logic.

func NewService

func NewService(repo *Repository, inspectStore *InspectStore, dispatcher Dispatcher, topicValidator TopicValidator, log *slog.Logger) *Service

NewService creates a new webhook service.

func (*Service) CaptureInspectPayload

func (s *Service) CaptureInspectPayload(tokenStr string, body []byte) (bool, error)

CaptureInspectPayload attempts to capture a payload for an active inspect session.

func (*Service) CreateInspectSession

func (s *Service) CreateInspectSession(ctx context.Context, userID, name, description, priority string, topics []string, hookURL string) (*InspectSessionResponse, error)

CreateInspectSession creates a new inspect session and returns the raw token and webhook URL.

func (*Service) CreateWebhook

func (s *Service) CreateWebhook(ctx context.Context, userID, name, description string, payloadType PayloadType, titlePath, bodyPath, priority string, topics []string) (string, string, error)

CreateWebhook creates a new webhook and returns the raw token (one-time reveal) and webhook ID.

func (*Service) FinalizeInspect

func (s *Service) FinalizeInspect(ctx context.Context, userID, titlePath, bodyPath string) (string, string, error)

FinalizeInspect creates the actual webhook from a completed inspect session.

func (*Service) GetInspectSession

func (s *Service) GetInspectSession(ctx context.Context, userID string) *InspectSessionStatusResponse

GetInspectSession returns the current inspect session status for the user.

func (*Service) ListWebhooks

func (s *Service) ListWebhooks(ctx context.Context, userID string) ([]WebhookResponse, error)

ListWebhooks lists all active webhooks for the user.

func (*Service) Receive

func (s *Service) Receive(ctx context.Context, tokenStr string, body []byte, log *slog.Logger) (*ReceiveResponse, error)

Receive validates the token, extracts title/body from the payload, and dispatches notifications.

func (*Service) RegenerateToken

func (s *Service) RegenerateToken(ctx context.Context, userID, webhookID string) (string, error)

RegenerateToken generates a new token for the given webhook and returns the raw token.

func (*Service) RevokeWebhook

func (s *Service) RevokeWebhook(ctx context.Context, userID, webhookID string) error

RevokeWebhook revokes a webhook, returning ErrWebhookNotFound if it does not exist.

func (*Service) UpdateWebhook

func (s *Service) UpdateWebhook(ctx context.Context, userID, webhookID, name, description string, payloadType PayloadType, titlePath, bodyPath, priority string, topicIDs []string) error

UpdateWebhook updates a webhook's settings and topic associations.

type TopicValidator

type TopicValidator interface {
	ValidateTopicIDs(ctx context.Context, userID string, topicIDs []string) error
}

TopicValidator verifies that topic IDs belong to the given user.

type UpdateWebhookRequest

type UpdateWebhookRequest struct {
	Name        string      `json:"name"`
	Description string      `json:"description"`
	PayloadType PayloadType `json:"payload_type"`
	TitlePath   string      `json:"title_path"`
	BodyPath    string      `json:"body_path"`
	Priority    string      `json:"priority"`
	Topics      []string    `json:"topics"`
}

UpdateWebhookRequest is the request body for PATCH /webhooks/{webhookID}.

func (*UpdateWebhookRequest) Validate

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

Validate validates the update webhook request fields.

type Webhook

type Webhook struct {
	ID          string      `db:"id"`
	UserID      string      `db:"user_id"`
	TokenHash   string      `db:"token_hash"`
	Name        string      `db:"name"`
	Description *string     `db:"description"`
	PayloadType PayloadType `db:"payload_type"`
	TitlePath   string      `db:"title_path"`
	BodyPath    string      `db:"body_path"`
	Priority    string      `db:"priority"`
	IsActive    bool        `db:"is_active"`
	RevokedAt   *int64      `db:"revoked_at"`
	LastUsedAt  *int64      `db:"last_used_at"`
	CreatedAt   int64       `db:"created_at"`
}

Webhook is the DB struct — db tags only.

type WebhookResponse

type WebhookResponse struct {
	ID          string      `json:"id"`
	Name        string      `json:"name"`
	Description *string     `json:"description,omitempty"`
	PayloadType PayloadType `json:"payload_type"`
	TitlePath   string      `json:"title_path,omitempty"`
	BodyPath    string      `json:"body_path,omitempty"`
	Priority    string      `json:"priority"`
	IsActive    bool        `json:"is_active"`
	CreatedAt   time.Time   `json:"created_at"`
	LastUsedAt  *time.Time  `json:"last_used_at,omitempty"`
	TopicIDs    []string    `json:"topic_ids,omitempty"`
}

WebhookResponse is the HTTP response struct — json tags only.

type WebhookTopic

type WebhookTopic struct {
	ID   string `db:"id"`
	Name string `db:"name"`
}

WebhookTopic holds topic ID and name for dispatch — db tags only.

type WebhooksListResponse

type WebhooksListResponse struct {
	Data []WebhookResponse `json:"data"`
}

WebhooksListResponse wraps a collection.

Jump to

Keyboard shortcuts

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