package model import ( "encoding/json" "time" "gorm.io/gorm" ) // WebhookSubscription represents an account-level webhook subscription for outgoing events. // Reference: Chatwoot webhook integration + P2B M8 spec type WebhookSubscription struct { ID uint `gorm:"primaryKey" json:"id"` AccountID uint `gorm:"not null;index" json:"account_id"` URL string `gorm:"size:2048;not null" json:"url"` Events json.RawMessage `gorm:"type:jsonb;not null" json:"events"` // JSON array of event types, e.g. ["conversation_created","message_created"] Secret string `gorm:"size:128;not null" json:"secret,omitempty"` // HMAC-SHA256 signing secret Active bool `gorm:"default:true" json:"active"` VerifiedAt *time.Time `json:"verified_at,omitempty"` LastDeliveryStatus string `gorm:"size:50" json:"last_delivery_status,omitempty"` // success/failed/pending LastDeliveryAt *time.Time `json:"last_delivery_at,omitempty"` CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"` DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at,omitempty"` Account Account `gorm:"foreignKey:AccountID" json:"account,omitempty"` } func (WebhookSubscription) TableName() string { return "webhook_subscriptions" } // WebhookDeliveryStatus constants for delivery tracking. const ( WebhookDeliveryStatusPending = "pending" WebhookDeliveryStatusSuccess = "success" WebhookDeliveryStatusFailed = "failed" WebhookDeliveryStatusRetrying = "retrying" ) // IsEventSubscribed checks whether the subscription includes a given event type. // The Events field is a JSON array of event type strings, e.g. ["conversation_created","message_created"]. func (s *WebhookSubscription) IsEventSubscribed(eventType string) bool { var events []string if err := json.Unmarshal(s.Events, &events); err != nil { return false } for _, e := range events { if e == eventType { return true } } return false } // WebhookDelivery represents a single webhook delivery attempt. // Reference: Chatwoot webhook delivery tracking + P2B M8 spec type WebhookDelivery struct { ID uint `gorm:"primaryKey" json:"id"` SubscriptionID uint `gorm:"not null;index" json:"subscription_id"` EventType string `gorm:"size:100;not null;index" json:"event_type"` Payload json.RawMessage `gorm:"type:jsonb" json:"payload"` ResponseCode int `json:"response_code,omitempty"` ResponseBody string `gorm:"size:4096" json:"response_body,omitempty"` Status string `gorm:"size:50;not null;index" json:"status"` // success/failed/retrying Attempts int `gorm:"default:0" json:"attempts"` NextAttemptAt *time.Time `json:"next_attempt_at,omitempty"` CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"` Subscription WebhookSubscription `gorm:"foreignKey:SubscriptionID" json:"subscription,omitempty"` } func (WebhookDelivery) TableName() string { return "webhook_deliveries" }