webhook

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Oct 13, 2026 License: MPL-2.0 Imports: 21 Imported by: 0

Documentation

Overview

Package webhook implements project- and organization-scoped webhook activation and delivery.

Index

Constants

View Source
const (
	OperationVersionCreated             = "version.created"
	OperationVersionCompleted           = "version.completed"
	OperationVersionRevoked             = "version.revoked"
	OperationVersionRevocationScheduled = "version.revocation_scheduled"
	OperationVersionRestored            = "version.restored"
	OperationVersionDeleted             = "version.deleted"
	OperationChannelCreated             = "channel.created"
	OperationChannelDeleted             = "channel.deleted"
	OperationChannelAssigned            = "channel.assigned"
	OperationBucketCreated              = "bucket.created"
	OperationBucketDeleted              = "bucket.deleted"
	OperationVerification               = "webhook.verification"
	OperationPluginUpdateAvailable      = "plugin.update_available"

	StatePending = "pending"
	StateActive  = "active"

	DeliveryPending   = "pending"
	DeliveryRetrying  = "retrying"
	DeliveryDelivered = "delivered"
	DeliveryFailed    = "failed"
	DeliveryRefused   = "refused"

	MaxAttempts = 5
)
View Source
const (
	SignatureHeader = "X-Dufflebag-Signature"
	EventHeader     = "X-Dufflebag-Event"
	DeliveryHeader  = "X-Dufflebag-Delivery"
)

Variables

View Source
var (
	ErrNotFound        = errors.New("webhook not found")
	ErrInvalid         = errors.New("invalid webhook")
	ErrSealUnavailable = errors.New("webhook secret sealing unavailable")
)
View Source
var ErrPrivateAddress = errors.New("webhook target resolves to a private or local address")
View Source
var OrganizationOperations = []string{OperationPluginUpdateAvailable}

OrganizationOperations are the events an organization webhook can receive.

Functions

func NewHTTPClient

func NewHTTPClient(allowPrivate bool, resolver Resolver, configuredDialer Dialer) *http.Client

func RefusedAddress

func RefusedAddress(address netip.Addr) bool

func SetHeaders

func SetHeaders(request *http.Request, secret, operation, eventID string, body []byte)

func Signature

func Signature(secret string, body []byte) string

func Subscribed

func Subscribed(events []string, operation string) bool

Types

type Actor

type Actor struct {
	PrincipalID string `json:"principal_id"`
	Name        string `json:"name"`
}

type Create

type Create struct {
	Name        string
	URL         string
	Description string
	Secret      string
	Events      []string
}

type Delivery

type Delivery struct {
	OrganizationID   string
	ProjectID        string
	ID               string
	WebhookID        string
	EventID          string
	Operation        string
	Status           string
	AttemptCount     int
	FirstAttemptedAt *time.Time
	LastAttemptedAt  *time.Time
	NextAttemptAt    *time.Time
	ResponseCode     *int
	Detail           *string
	CreatedAt        time.Time
}

type Dialer

type Dialer interface {
	DialContext(context.Context, string, string) (net.Conn, error)
}

type DispatchRepository

type DispatchRepository interface {
	ListWebhookProjects(context.Context) ([]Project, error)
	GetNextWebhookOutboxEvent(context.Context, Project, time.Time) (*OutboxEvent, error)
	ListWebhookEventDeliveries(context.Context, Project, string) ([]Delivery, error)
	GetWebhook(context.Context, string, string, string) (*Record, error)
	RecordWebhookDeliveryAttempt(context.Context, Delivery) error
	ScheduleWebhookOutboxEvent(context.Context, Project, string, time.Time) error
	DeleteWebhookOutboxEvent(context.Context, Project, string) error
}

type Dispatcher

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

func NewDispatcher

func NewDispatcher(repository DispatchRepository, sealer *credseal.Sealer, client *http.Client, interval, retryBase time.Duration, logger *slog.Logger) (*Dispatcher, error)

func (*Dispatcher) DispatchAll

func (d *Dispatcher) DispatchAll(ctx context.Context) error

func (*Dispatcher) DispatchProject

func (d *Dispatcher) DispatchProject(ctx context.Context, project Project) error

func (*Dispatcher) Run

func (d *Dispatcher) Run(ctx context.Context)

func (*Dispatcher) Started

func (d *Dispatcher) Started() <-chan struct{}

type Envelope

type Envelope struct {
	EventID        string          `json:"event_id"`
	OccurredAt     time.Time       `json:"occurred_at"`
	OrganizationID string          `json:"organization_id"`
	ProjectID      string          `json:"project_id,omitempty"`
	Operation      string          `json:"operation"`
	Target         Target          `json:"target"`
	Actor          Actor           `json:"actor"`
	Payload        json.RawMessage `json:"payload"`
}

type OutboxEvent

type OutboxEvent struct {
	Envelope
	AvailableAt time.Time
}

type Project

type Project struct {
	OrganizationID string
	ProjectID      string
}

Project is a dispatch unit. An empty ProjectID is the organization's own webhooks.

type Record

type Record struct {
	OrganizationID        string
	ProjectID             string
	ID                    string
	Name                  string
	URL                   string
	Description           string
	SealedSecret          []byte
	Events                []string
	State                 string
	LastVerificationAt    *time.Time
	LastVerificationError *string
	CreatedAt             time.Time
	UpdatedAt             time.Time
}

type Repository

type Repository interface {
	CreateWebhook(context.Context, Record) (*Record, error)
	GetWebhook(context.Context, string, string, string) (*Record, error)
	ListWebhooks(context.Context, string, string) ([]Record, error)
	UpdateWebhook(context.Context, Record) (*Record, error)
	DeleteWebhook(context.Context, string, string, string) error
	RecordWebhookVerification(context.Context, Record, string, string, *int, *string, time.Time) (*Record, error)
	ListWebhookDeliveries(context.Context, string, string, string) ([]Delivery, error)
}

type Resolver

type Resolver interface {
	LookupNetIP(context.Context, string, string) ([]netip.Addr, error)
}

type Service

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

func NewService

func NewService(repository Repository, sealer *credseal.Sealer, client *http.Client) *Service

func (*Service) Create

func (s *Service) Create(ctx context.Context, organizationID, projectID string, write Create) (*Record, error)

func (*Service) Delete

func (s *Service) Delete(ctx context.Context, organizationID, projectID, webhookID string) error

func (*Service) Deliveries

func (s *Service) Deliveries(ctx context.Context, organizationID, projectID, webhookID string) ([]Delivery, error)

func (*Service) Get

func (s *Service) Get(ctx context.Context, organizationID, projectID, webhookID string) (*Record, error)

func (*Service) List

func (s *Service) List(ctx context.Context, organizationID, projectID string) ([]Record, error)

func (*Service) Update

func (s *Service) Update(ctx context.Context, organizationID, projectID, webhookID string, write Update) (*Record, error)

func (*Service) Verify

func (s *Service) Verify(ctx context.Context, organizationID, projectID, webhookID string) (*Record, error)

type Target

type Target struct {
	Type        string `json:"type"`
	Bucket      string `json:"bucket,omitempty"`
	Fingerprint string `json:"fingerprint,omitempty"`
	Name        string `json:"name,omitempty"`
}

type Update

type Update struct {
	Name        *string
	URL         *string
	Description *string
	Secret      *string
	Events      *[]string
}

Jump to

Keyboard shortcuts

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