Rebuilds the qbit on QQQ 4.1.0-RC.4 per the assessment (QRun-IO/qqq#1105, decisions D1-D8): - webhooks_endpoint, webhooks_endpoint_secret, webhooks_subscription, webhooks_delivery and webhooks_delivery_attempt (snake_case, webhooks_ prefix), permissions webhooks.view / manage / admin - Standard Webhooks signing with per-endpoint secrets (AES-GCM or env references) and overlapping rotation - SSRF-safe client: private, loopback, link-local and metadata ranges blocked, DNS pinned, no redirects, timeouts, size cap, admin-only internal allowlist (shared/ is an internal copy of QRun-IO/qqq#1109, replaced at RC.5) - event sources: table insert/update/delete (record change listener), process completed/failed, ESB topics (own events skipped, CloudEvent id dedup), custom events and a channel API for other qbits - delivery: unique attempt-row claims with leases (no double sends across replicas), retries with backoff, Retry-After, dead letters and Replay, serial mode, per-endpoint rate limits, health with probation and auto-disable, alerts through the Alerts SPI when present, retention - subscriptions: record filters, record security, read permission check, detail levels, include and exclude fields, payload customizer hook - app home dashboard, actions with the standard review/result screens, ESB / Quick Search / API integration hooks - JaCoCo gate, Checkstyle in verify, Maven wrapper, default 4.1 profile, Postgres DDL in docs/ddl The 0.x sources stay in the tree, excluded from the build, until their removal is approved. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
144 lines
5.1 KiB
SQL
144 lines
5.1 KiB
SQL
-- qbit-webhooks 1.0: the five webhooks_ tables (PostgreSQL).
|
|
--
|
|
-- Column names are the snake_case of the field names (WebhooksQBitConfig
|
|
-- snakeCaseColumnNames, the default). Hosts that configure securityFields add
|
|
-- those columns to webhooks_endpoint, webhooks_subscription and
|
|
-- webhooks_delivery. The unique keys matter: webhooks_delivery.dedup_key stops
|
|
-- an event being queued twice, and (delivery_id, attempt_number) on
|
|
-- webhooks_delivery_attempt is the claim that stops two replicas sending the
|
|
-- same attempt.
|
|
|
|
CREATE TABLE webhooks_endpoint
|
|
(
|
|
id SERIAL PRIMARY KEY,
|
|
create_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
modify_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
name VARCHAR(100) NOT NULL,
|
|
description TEXT,
|
|
url VARCHAR(500) NOT NULL,
|
|
active_status VARCHAR(20) DEFAULT 'ACTIVE',
|
|
health_status VARCHAR(20) DEFAULT 'HEALTHY',
|
|
serial_delivery BOOLEAN DEFAULT FALSE,
|
|
max_per_minute INTEGER,
|
|
timeout_seconds INTEGER,
|
|
allow_unsigned BOOLEAN DEFAULT FALSE,
|
|
allow_internal_targets BOOLEAN DEFAULT FALSE,
|
|
internal_allowlist TEXT,
|
|
consecutive_failures INTEGER DEFAULT 0,
|
|
probation_failures INTEGER DEFAULT 0,
|
|
last_success_date TIMESTAMP WITH TIME ZONE,
|
|
last_failure_date TIMESTAMP WITH TIME ZONE,
|
|
last_failure_message VARCHAR(500),
|
|
unhealthy_since_date TIMESTAMP WITH TIME ZONE,
|
|
auto_disabled_date TIMESTAMP WITH TIME ZONE,
|
|
CONSTRAINT webhooks_endpoint_name_uk UNIQUE (name)
|
|
);
|
|
|
|
|
|
CREATE TABLE webhooks_endpoint_secret
|
|
(
|
|
id SERIAL PRIMARY KEY,
|
|
create_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
modify_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
endpoint_id INTEGER NOT NULL REFERENCES webhooks_endpoint (id),
|
|
status VARCHAR(20) DEFAULT 'ACTIVE',
|
|
source VARCHAR(20) NOT NULL,
|
|
secret_ref VARCHAR(100),
|
|
encrypted_secret TEXT,
|
|
fingerprint VARCHAR(20),
|
|
expires_date TIMESTAMP WITH TIME ZONE,
|
|
retired_date TIMESTAMP WITH TIME ZONE,
|
|
note VARCHAR(250)
|
|
);
|
|
|
|
CREATE INDEX webhooks_endpoint_secret_endpoint_ix ON webhooks_endpoint_secret (endpoint_id, status);
|
|
|
|
|
|
CREATE TABLE webhooks_subscription
|
|
(
|
|
id SERIAL PRIMARY KEY,
|
|
create_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
modify_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
endpoint_id INTEGER NOT NULL REFERENCES webhooks_endpoint (id),
|
|
event_type_name VARCHAR(200) NOT NULL,
|
|
active_status VARCHAR(20) DEFAULT 'ACTIVE',
|
|
description VARCHAR(250),
|
|
filter_json TEXT,
|
|
detail_level VARCHAR(20) DEFAULT 'FULL',
|
|
include_fields VARCHAR(1000),
|
|
exclude_fields VARCHAR(1000),
|
|
api_name VARCHAR(100),
|
|
api_version VARCHAR(50)
|
|
);
|
|
|
|
CREATE INDEX webhooks_subscription_type_ix ON webhooks_subscription (event_type_name, active_status);
|
|
|
|
CREATE INDEX webhooks_subscription_endpoint_ix ON webhooks_subscription (endpoint_id);
|
|
|
|
|
|
CREATE TABLE webhooks_delivery
|
|
(
|
|
id SERIAL PRIMARY KEY,
|
|
create_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
modify_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
event_id VARCHAR(40) NOT NULL,
|
|
event_type_name VARCHAR(200) NOT NULL,
|
|
endpoint_id INTEGER NOT NULL,
|
|
subscription_id INTEGER,
|
|
source VARCHAR(20),
|
|
source_table VARCHAR(100),
|
|
source_record_id VARCHAR(100),
|
|
source_event_id VARCHAR(100),
|
|
dedup_key VARCHAR(250) NOT NULL,
|
|
status VARCHAR(20) NOT NULL,
|
|
attempt_count INTEGER DEFAULT 0,
|
|
cycle_attempt_count INTEGER DEFAULT 0,
|
|
replay_count INTEGER DEFAULT 0,
|
|
occurred_date TIMESTAMP WITH TIME ZONE,
|
|
next_attempt_date TIMESTAMP WITH TIME ZONE,
|
|
lease_expires_date TIMESTAMP WITH TIME ZONE,
|
|
last_http_status INTEGER,
|
|
last_error VARCHAR(500),
|
|
delivered_date TIMESTAMP WITH TIME ZONE,
|
|
dead_letter_date TIMESTAMP WITH TIME ZONE,
|
|
payload_bytes INTEGER,
|
|
payload TEXT,
|
|
CONSTRAINT webhooks_delivery_dedup_key_uk UNIQUE (dedup_key),
|
|
CONSTRAINT webhooks_delivery_event_id_uk UNIQUE (event_id)
|
|
);
|
|
|
|
CREATE INDEX webhooks_delivery_due_ix ON webhooks_delivery (endpoint_id, status, next_attempt_date);
|
|
|
|
CREATE INDEX webhooks_delivery_status_ix ON webhooks_delivery (status, modify_date);
|
|
|
|
CREATE INDEX webhooks_delivery_delivered_ix ON webhooks_delivery (delivered_date);
|
|
|
|
|
|
CREATE TABLE webhooks_delivery_attempt
|
|
(
|
|
id SERIAL PRIMARY KEY,
|
|
create_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
modify_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
|
delivery_id INTEGER NOT NULL,
|
|
endpoint_id INTEGER NOT NULL,
|
|
attempt_number INTEGER NOT NULL,
|
|
outcome VARCHAR(20) NOT NULL,
|
|
claimed_by VARCHAR(100),
|
|
lease_expires_date TIMESTAMP WITH TIME ZONE,
|
|
start_date TIMESTAMP WITH TIME ZONE,
|
|
end_date TIMESTAMP WITH TIME ZONE,
|
|
duration_millis INTEGER,
|
|
http_status INTEGER,
|
|
response_excerpt TEXT,
|
|
error_message VARCHAR(1000),
|
|
remote_address VARCHAR(64),
|
|
request_url VARCHAR(500),
|
|
signed_with VARCHAR(100),
|
|
CONSTRAINT webhooks_delivery_attempt_uk UNIQUE (delivery_id, attempt_number)
|
|
);
|
|
|
|
CREATE INDEX webhooks_delivery_attempt_endpoint_ix ON webhooks_delivery_attempt (endpoint_id, start_date);
|
|
|
|
CREATE INDEX webhooks_delivery_attempt_start_ix ON webhooks_delivery_attempt (start_date);
|
|
|