Major refactoring to hexagonal (ports & adapters) architecture: - Add service layer (apikey_service, project_service) for business logic - Add webhook system with dispatcher and delivery tracking - Add command queue with priority-based processing - Add rate limiting with sliding window algorithm - Add audit logging for command execution - Add OpenTelemetry integration (traces, metrics, spans) - Add circuit breaker for fault tolerance - Add cached repository wrapper for performance - Add comprehensive validation package - Add Kubernetes client integration for pod management - Add database migrations (allowed_ips, audit_log, rate_limiting, queue, webhooks) - Add network policy and PodDisruptionBudget for k8s - Remove legacy executor and projects/registry packages - Untrack secrets.yaml (now managed via envault) - Add coverage.out to .gitignore - Add e2e test infrastructure with docker-compose - Add comprehensive documentation (API, architecture, operations, plans) - Add golangci-lint config and pre-commit hook Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
161 lines
5.2 KiB
Go
161 lines
5.2 KiB
Go
package domain
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
)
|
|
|
|
// WebhookID is a strongly-typed identifier for webhooks.
|
|
type WebhookID string
|
|
|
|
// String returns the webhook ID as a string.
|
|
func (id WebhookID) String() string {
|
|
return string(id)
|
|
}
|
|
|
|
// WebhookEventType represents the type of event that triggers a webhook.
|
|
type WebhookEventType string
|
|
|
|
// Available webhook event types.
|
|
const (
|
|
WebhookEventCommandStarted WebhookEventType = "command.started"
|
|
WebhookEventCommandCompleted WebhookEventType = "command.completed"
|
|
WebhookEventCommandFailed WebhookEventType = "command.failed"
|
|
WebhookEventPodReady WebhookEventType = "pod.ready"
|
|
WebhookEventPodFailed WebhookEventType = "pod.failed"
|
|
)
|
|
|
|
// AllWebhookEventTypes lists all valid webhook event types.
|
|
var AllWebhookEventTypes = []WebhookEventType{
|
|
WebhookEventCommandStarted,
|
|
WebhookEventCommandCompleted,
|
|
WebhookEventCommandFailed,
|
|
WebhookEventPodReady,
|
|
WebhookEventPodFailed,
|
|
}
|
|
|
|
// IsValid checks if a webhook event type is valid.
|
|
func (t WebhookEventType) IsValid() bool {
|
|
for _, valid := range AllWebhookEventTypes {
|
|
if t == valid {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// String returns the event type as a string.
|
|
func (t WebhookEventType) String() string {
|
|
return string(t)
|
|
}
|
|
|
|
// Webhook represents a webhook subscription for a project.
|
|
type Webhook struct {
|
|
ID WebhookID `json:"id"`
|
|
ProjectID string `json:"project_id"`
|
|
URL string `json:"url"`
|
|
Secret string `json:"-"` // Never expose secret in JSON responses
|
|
Events []WebhookEventType `json:"events"`
|
|
Enabled bool `json:"enabled"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
// HasSecret returns true if the webhook has a signing secret configured.
|
|
func (w *Webhook) HasSecret() bool {
|
|
return w.Secret != ""
|
|
}
|
|
|
|
// SubscribesToEvent checks if the webhook subscribes to the given event type.
|
|
func (w *Webhook) SubscribesToEvent(eventType WebhookEventType) bool {
|
|
for _, e := range w.Events {
|
|
if e == eventType {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// WebhookDeliveryID is a strongly-typed identifier for webhook deliveries.
|
|
type WebhookDeliveryID string
|
|
|
|
// String returns the delivery ID as a string.
|
|
func (id WebhookDeliveryID) String() string {
|
|
return string(id)
|
|
}
|
|
|
|
// WebhookDelivery represents a single webhook delivery attempt.
|
|
type WebhookDelivery struct {
|
|
ID WebhookDeliveryID `json:"id"`
|
|
WebhookID WebhookID `json:"webhook_id"`
|
|
EventType WebhookEventType `json:"event_type"`
|
|
Payload string `json:"payload"` // JSON payload that was sent
|
|
ResponseStatus int `json:"response_status,omitempty"`
|
|
ResponseBody string `json:"response_body,omitempty"`
|
|
DeliveredAt time.Time `json:"delivered_at"`
|
|
Success bool `json:"success"`
|
|
RetryCount int `json:"retry_count"`
|
|
ErrorMessage string `json:"error_message,omitempty"`
|
|
}
|
|
|
|
// WebhookEvent represents an event to be dispatched to webhooks.
|
|
type WebhookEvent struct {
|
|
Type WebhookEventType `json:"type"`
|
|
Timestamp time.Time `json:"timestamp"`
|
|
ProjectID string `json:"project_id"`
|
|
Data any `json:"data"`
|
|
}
|
|
|
|
// WebhookPayload is the structure sent to webhook endpoints.
|
|
type WebhookPayload struct {
|
|
ID string `json:"id"` // Unique delivery ID
|
|
Event WebhookEventType `json:"event"` // Event type
|
|
Timestamp time.Time `json:"timestamp"` // When the event occurred
|
|
ProjectID string `json:"project_id"` // Project this event relates to
|
|
Data any `json:"data"` // Event-specific data
|
|
}
|
|
|
|
// CommandEventData is the data structure for command-related webhook events.
|
|
type CommandEventData struct {
|
|
CommandID string `json:"command_id"`
|
|
CommandType CommandType `json:"command_type"`
|
|
ProjectID string `json:"project_id"`
|
|
StartedAt time.Time `json:"started_at,omitempty"`
|
|
CompletedAt time.Time `json:"completed_at,omitempty"`
|
|
ExitCode int `json:"exit_code,omitempty"`
|
|
DurationMs int64 `json:"duration_ms,omitempty"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
// PodEventData is the data structure for pod-related webhook events.
|
|
type PodEventData struct {
|
|
PodName string `json:"pod_name"`
|
|
ProjectID string `json:"project_id"`
|
|
Status string `json:"status"`
|
|
Reason string `json:"reason,omitempty"`
|
|
Message string `json:"message,omitempty"`
|
|
Timestamp time.Time `json:"timestamp"`
|
|
}
|
|
|
|
// WebhookFilters contains filter options for listing webhook deliveries.
|
|
type WebhookDeliveryFilters struct {
|
|
EventType *WebhookEventType // Filter by event type
|
|
Success *bool // Filter by success status
|
|
Limit int // Max results (default 100)
|
|
Offset int // For pagination
|
|
}
|
|
|
|
// DefaultWebhookDeliveryFilters returns sensible defaults.
|
|
func DefaultWebhookDeliveryFilters() *WebhookDeliveryFilters {
|
|
return &WebhookDeliveryFilters{
|
|
Limit: 100,
|
|
Offset: 0,
|
|
}
|
|
}
|
|
|
|
// Webhook-related errors.
|
|
var (
|
|
ErrWebhookNotFound = errors.New("webhook not found")
|
|
ErrInvalidWebhook = errors.New("invalid webhook configuration")
|
|
)
|