mirror of
https://github.com/warmbly/warmbly.git
synced 2026-08-20 00:01:21 +00:00
d5147659a7
removing the BurstMultiplier field changed column widths in several struct literals; let gofmt realign them. no semantic change.
159 lines
6.2 KiB
Go
159 lines
6.2 KiB
Go
package models
|
|
|
|
import (
|
|
"encoding/json"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// WebhookEventType is the canonical event-name identifier carried both in
|
|
// the subscription filter and the delivery payload. Keep these stable —
|
|
// renaming a value breaks every customer subscribed to it.
|
|
type WebhookEventType string
|
|
|
|
const (
|
|
// Email account lifecycle
|
|
WebhookEventEmailAccountConnected WebhookEventType = "email_account.connected"
|
|
WebhookEventEmailAccountRemoved WebhookEventType = "email_account.removed"
|
|
|
|
// Campaign send pipeline
|
|
WebhookEventCampaignEmailSent WebhookEventType = "campaign.email_sent"
|
|
WebhookEventCampaignEmailDelivered WebhookEventType = "campaign.email_delivered"
|
|
WebhookEventCampaignEmailOpened WebhookEventType = "campaign.email_opened"
|
|
WebhookEventCampaignEmailClicked WebhookEventType = "campaign.email_clicked"
|
|
WebhookEventCampaignEmailBounced WebhookEventType = "campaign.email_bounced"
|
|
WebhookEventCampaignReplyReceived WebhookEventType = "campaign.reply_received"
|
|
WebhookEventCampaignUnsubscribed WebhookEventType = "campaign.unsubscribed"
|
|
WebhookEventCampaignStarted WebhookEventType = "campaign.started"
|
|
WebhookEventCampaignPaused WebhookEventType = "campaign.paused"
|
|
WebhookEventCampaignCompleted WebhookEventType = "campaign.completed"
|
|
|
|
// Warmup
|
|
WebhookEventWarmupEmailSent WebhookEventType = "warmup.email_sent"
|
|
WebhookEventWarmupHealthChanged WebhookEventType = "warmup.health_changed"
|
|
WebhookEventWarmupPlacementInSpam WebhookEventType = "warmup.placement_in_spam"
|
|
WebhookEventWarmupQuarantined WebhookEventType = "warmup.quarantined"
|
|
WebhookEventWarmupBlocked WebhookEventType = "warmup.blocked"
|
|
|
|
// Deliverability
|
|
WebhookEventDeliverabilityBounce WebhookEventType = "deliverability.bounce"
|
|
WebhookEventDeliverabilityComplaint WebhookEventType = "deliverability.complaint"
|
|
)
|
|
|
|
// AllWebhookEventTypes lists every emitted event so the CRUD endpoint can
|
|
// validate `event_types` filters and the UI can render a picker.
|
|
var AllWebhookEventTypes = []WebhookEventType{
|
|
WebhookEventEmailAccountConnected,
|
|
WebhookEventEmailAccountRemoved,
|
|
WebhookEventCampaignEmailSent,
|
|
WebhookEventCampaignEmailDelivered,
|
|
WebhookEventCampaignEmailOpened,
|
|
WebhookEventCampaignEmailClicked,
|
|
WebhookEventCampaignEmailBounced,
|
|
WebhookEventCampaignReplyReceived,
|
|
WebhookEventCampaignUnsubscribed,
|
|
WebhookEventCampaignStarted,
|
|
WebhookEventCampaignPaused,
|
|
WebhookEventCampaignCompleted,
|
|
WebhookEventWarmupEmailSent,
|
|
WebhookEventWarmupHealthChanged,
|
|
WebhookEventWarmupPlacementInSpam,
|
|
WebhookEventWarmupQuarantined,
|
|
WebhookEventWarmupBlocked,
|
|
WebhookEventDeliverabilityBounce,
|
|
WebhookEventDeliverabilityComplaint,
|
|
}
|
|
|
|
func IsValidWebhookEventType(s string) bool {
|
|
for _, t := range AllWebhookEventTypes {
|
|
if string(t) == s {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// WebhookEndpoint is a customer's subscription to events.
|
|
type WebhookEndpoint struct {
|
|
ID uuid.UUID `json:"id"`
|
|
OrganizationID uuid.UUID `json:"organization_id"`
|
|
URL string `json:"url"`
|
|
Description string `json:"description"`
|
|
EventTypes []string `json:"event_types"`
|
|
Enabled bool `json:"enabled"`
|
|
LastSuccessAt *time.Time `json:"last_success_at,omitempty"`
|
|
LastFailureAt *time.Time `json:"last_failure_at,omitempty"`
|
|
LastFailureReason *string `json:"last_failure_reason,omitempty"`
|
|
ConsecutiveFailures int `json:"consecutive_failures"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
// WebhookEndpointWithSecret is returned once at creation time so the
|
|
// client can capture the secret. Subsequent reads do not include it.
|
|
type WebhookEndpointWithSecret struct {
|
|
WebhookEndpoint
|
|
Secret string `json:"secret"`
|
|
}
|
|
|
|
// Subscribes returns true if this endpoint should receive the given event.
|
|
// An endpoint with an empty event_types array receives all events.
|
|
func (e *WebhookEndpoint) Subscribes(event WebhookEventType) bool {
|
|
if !e.Enabled {
|
|
return false
|
|
}
|
|
if len(e.EventTypes) == 0 {
|
|
return true
|
|
}
|
|
for _, t := range e.EventTypes {
|
|
if t == string(event) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// WebhookDeliveryStatus tracks where a delivery attempt sits in its
|
|
// lifecycle. 'abandoned' = retries exhausted.
|
|
type WebhookDeliveryStatus string
|
|
|
|
const (
|
|
WebhookDeliveryPending WebhookDeliveryStatus = "pending"
|
|
WebhookDeliveryInFlight WebhookDeliveryStatus = "in_flight"
|
|
WebhookDeliveryDelivered WebhookDeliveryStatus = "delivered"
|
|
WebhookDeliveryFailed WebhookDeliveryStatus = "failed"
|
|
WebhookDeliveryAbandoned WebhookDeliveryStatus = "abandoned"
|
|
)
|
|
|
|
// WebhookDelivery is one attempt-history record. Multiple rows may exist
|
|
// per (event, endpoint) across retries — each row updates in place as
|
|
// attempts progress.
|
|
type WebhookDelivery struct {
|
|
ID uuid.UUID `json:"id"`
|
|
EndpointID uuid.UUID `json:"endpoint_id"`
|
|
OrganizationID uuid.UUID `json:"organization_id"`
|
|
EventType string `json:"event_type"`
|
|
EventID uuid.UUID `json:"event_id"`
|
|
Payload json.RawMessage `json:"payload"`
|
|
Status WebhookDeliveryStatus `json:"status"`
|
|
AttemptCount int `json:"attempt_count"`
|
|
MaxAttempts int `json:"max_attempts"`
|
|
NextAttemptAt time.Time `json:"next_attempt_at"`
|
|
LastAttemptAt *time.Time `json:"last_attempt_at,omitempty"`
|
|
ResponseStatus *int `json:"response_status,omitempty"`
|
|
ResponseBodyExcerpt *string `json:"response_body_excerpt,omitempty"`
|
|
ErrorReason *string `json:"error_reason,omitempty"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
// WebhookPayload is the JSON body the dispatcher POSTs to a subscriber.
|
|
type WebhookPayload struct {
|
|
ID uuid.UUID `json:"id"`
|
|
EventType WebhookEventType `json:"event_type"`
|
|
OrganizationID uuid.UUID `json:"organization_id"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
Data any `json:"data"`
|
|
}
|