rdev/internal/db/migrations/007_webhooks.sql
jordan 72d16929ca feat: Implement hexagonal architecture with services, webhooks, queue, and telemetry
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>
2026-01-25 19:57:46 -07:00

70 lines
3.5 KiB
SQL

-- Create webhooks table for project webhook subscriptions
CREATE TABLE IF NOT EXISTS webhooks (
id TEXT PRIMARY KEY,
project_id TEXT NOT NULL,
url TEXT NOT NULL,
secret TEXT, -- HMAC-SHA256 signing secret (optional but recommended)
events TEXT NOT NULL, -- JSON array of event types to subscribe
enabled BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Index for efficient lookup by project
CREATE INDEX IF NOT EXISTS idx_webhooks_project_id
ON webhooks(project_id);
-- Index for finding enabled webhooks (most queries will filter by enabled)
CREATE INDEX IF NOT EXISTS idx_webhooks_enabled
ON webhooks(enabled)
WHERE enabled = true;
-- GIN index for efficient JSONB containment queries on events column
-- Required for: WHERE events::jsonb ? 'event_type'
CREATE INDEX IF NOT EXISTS idx_webhooks_events_gin
ON webhooks USING GIN ((events::jsonb));
-- Create webhook_deliveries table for delivery tracking
CREATE TABLE IF NOT EXISTS webhook_deliveries (
id TEXT PRIMARY KEY,
webhook_id TEXT NOT NULL REFERENCES webhooks(id) ON DELETE CASCADE,
event_type TEXT NOT NULL,
payload TEXT NOT NULL, -- JSON payload that was sent
response_status INT,
response_body TEXT,
delivered_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
success BOOLEAN NOT NULL DEFAULT false,
retry_count INT NOT NULL DEFAULT 0,
error_message TEXT -- Capture error details for failed deliveries
);
-- Index for listing deliveries by webhook
CREATE INDEX IF NOT EXISTS idx_webhook_deliveries_webhook_id
ON webhook_deliveries(webhook_id, delivered_at DESC);
-- Index for monitoring failed deliveries
CREATE INDEX IF NOT EXISTS idx_webhook_deliveries_success
ON webhook_deliveries(success, delivered_at DESC)
WHERE success = false;
-- Index for cleanup of old deliveries
CREATE INDEX IF NOT EXISTS idx_webhook_deliveries_delivered_at
ON webhook_deliveries(delivered_at);
COMMENT ON TABLE webhooks IS 'Webhook subscriptions for project events';
COMMENT ON COLUMN webhooks.project_id IS 'Project ID that this webhook is subscribed to';
COMMENT ON COLUMN webhooks.url IS 'URL to POST webhook payloads to';
COMMENT ON COLUMN webhooks.secret IS 'Secret for HMAC-SHA256 signing (X-Webhook-Signature header)';
COMMENT ON COLUMN webhooks.events IS 'JSON array of event types: command.started, command.completed, command.failed, pod.ready, pod.failed';
COMMENT ON COLUMN webhooks.enabled IS 'Whether this webhook is active';
COMMENT ON TABLE webhook_deliveries IS 'Webhook delivery history and retry tracking';
COMMENT ON COLUMN webhook_deliveries.webhook_id IS 'Reference to the webhook configuration';
COMMENT ON COLUMN webhook_deliveries.event_type IS 'Type of event that triggered this delivery';
COMMENT ON COLUMN webhook_deliveries.payload IS 'JSON payload that was sent to the webhook URL';
COMMENT ON COLUMN webhook_deliveries.response_status IS 'HTTP status code from the webhook endpoint';
COMMENT ON COLUMN webhook_deliveries.response_body IS 'Response body from the webhook endpoint (truncated)';
COMMENT ON COLUMN webhook_deliveries.success IS 'Whether the delivery was successful (2xx response)';
COMMENT ON COLUMN webhook_deliveries.retry_count IS 'Number of retry attempts for this delivery';
COMMENT ON COLUMN webhook_deliveries.error_message IS 'Error details if delivery failed';