mirror of
https://github.com/warmbly/warmbly.git
synced 2026-09-06 08:01:24 +00:00
63 lines
3.5 KiB
SQL
63 lines
3.5 KiB
SQL
-- Full webhook platform: per-OAuth-app webhook-domain allowlist, app-scoped
|
|
-- endpoints, endpoint ownership verification, and auto-disable bookkeeping.
|
|
|
|
-- An OAuth app may only register webhook endpoints on domains it declares here.
|
|
-- Subdomain-inclusive matching is applied in app code (a leading-dot entry
|
|
-- ".acme.com" matches any subdomain; a bare "acme.com" matches that host only).
|
|
-- Empty list = the app cannot register webhooks.
|
|
ALTER TABLE oauth_applications
|
|
ADD COLUMN allowed_webhook_domains text[] NOT NULL DEFAULT '{}';
|
|
|
|
-- Endpoint provenance + ownership-verification + auto-disable state.
|
|
ALTER TABLE webhook_endpoints
|
|
-- App-scoped endpoint: when set, the endpoint is owned by an OAuth app and
|
|
-- its URL host is re-checked against the app's allowed_webhook_domains on
|
|
-- every create/update/delivery. NULL = a normal org-level endpoint.
|
|
ADD COLUMN oauth_application_id uuid REFERENCES oauth_applications (id) ON DELETE CASCADE,
|
|
-- The member who created the endpoint (UX/audit; NULL for app-created).
|
|
ADD COLUMN created_by uuid REFERENCES users (id) ON DELETE SET NULL,
|
|
-- Ownership verification: an endpoint only receives the real event stream
|
|
-- once verified. Until then it gets a single signed challenge request, so a
|
|
-- webhook pointed at a non-consenting third party can never be flooded.
|
|
ADD COLUMN verified_at timestamptz,
|
|
-- True once the receiver echoed the challenge back (proves URL control, not
|
|
-- just reachability). Reachability (a 2xx to the challenge) sets verified_at;
|
|
-- echo additionally sets this. App-scoped endpoints REQUIRE echo to verify.
|
|
ADD COLUMN ownership_confirmed boolean NOT NULL DEFAULT false,
|
|
-- Current challenge value the receiver must echo (rotated on each (re)verify).
|
|
ADD COLUMN verification_token text NOT NULL DEFAULT '',
|
|
-- When the current continuous-failure streak began (NULL when healthy). Used
|
|
-- for hysteretic auto-disable: we only disable after sustained failure.
|
|
ADD COLUMN first_failure_at timestamptz,
|
|
-- Set when the platform auto-disabled the endpoint after sustained failure.
|
|
ADD COLUMN auto_disabled_at timestamptz,
|
|
-- Human-readable reason shown in the dashboard when auto-disabled.
|
|
ADD COLUMN disabled_reason text;
|
|
|
|
-- Grandfather every pre-existing endpoint as verified: they were already
|
|
-- receiving the event stream before ownership verification existed, so do not
|
|
-- silently cut them off. New endpoints created after this migration go through
|
|
-- the challenge flow (verified_at stays NULL until they pass).
|
|
UPDATE webhook_endpoints
|
|
SET verified_at = created_at, ownership_confirmed = true
|
|
WHERE verified_at IS NULL;
|
|
|
|
CREATE INDEX idx_webhook_endpoints_oauth_app ON webhook_endpoints (oauth_application_id)
|
|
WHERE oauth_application_id IS NOT NULL;
|
|
|
|
-- Reaper support: claim stuck in_flight rows by age.
|
|
CREATE INDEX idx_webhook_deliveries_inflight ON webhook_deliveries (updated_at)
|
|
WHERE status = 'in_flight';
|
|
|
|
-- Visible throttle drops: when the per-(org,event_type) dispatch throttle trips,
|
|
-- we record a flood-safe daily rollup (one upsert per minute-window-trip) so the
|
|
-- dashboard can show "events were rate-limited" instead of dropping silently.
|
|
CREATE TABLE webhook_event_drops (
|
|
organization_id uuid NOT NULL REFERENCES organizations (id) ON DELETE CASCADE,
|
|
event_type text NOT NULL,
|
|
day date NOT NULL,
|
|
dropped_windows integer NOT NULL DEFAULT 0,
|
|
last_dropped_at timestamptz NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (organization_id, event_type, day)
|
|
);
|