forge
ExploreLog in

Tahinli/forge

public
0 watching
★ 0
2.1 KiB
1-- Repo webhooks and their deliveries. Applied by store/migrate.rs.
2-- secret_sealed holds the secret::seal output (UTF-8 "v1:..." text stored as a BLOB, like
3-- users.totp_secret_sealed). The plaintext secret never reaches the database.
4-- events is a JSON array of WebhookEvent names. active is 0 or 1.
5-- Timestamps are RFC 3339 UTC stamps from time.rs, so text comparison is time order.
6
7CREATE TABLE webhooks (
8 id TEXT PRIMARY KEY,
9 repo_id TEXT NOT NULL REFERENCES repos(id),
10 url TEXT NOT NULL,
11 secret_sealed BLOB NOT NULL,
12 events TEXT NOT NULL,
13 active INTEGER NOT NULL DEFAULT 1,
14 created_by TEXT NULL REFERENCES users(id) ON DELETE SET NULL,
15 created_at TEXT NOT NULL,
16 updated_at TEXT NOT NULL
17);
18CREATE INDEX webhooks_repo_id ON webhooks(repo_id);
19
20-- state: pending (queued, not yet attempted), ok (2xx received), failed (an attempt failed and a
21-- retry is scheduled at next_attempt_at), gave_up (no further attempts).
22-- Claimable rows are state IN ('pending', 'failed') with next_attempt_at <= now.
23-- guid is the X-Forge-Delivery value. The request columns are immutable once written.
24CREATE TABLE webhook_deliveries (
25 id TEXT PRIMARY KEY,
26 hook_id TEXT NOT NULL REFERENCES webhooks(id),
27 guid TEXT NOT NULL UNIQUE,
28 event TEXT NOT NULL,
29 action TEXT NOT NULL DEFAULT '',
30 request_headers TEXT NOT NULL,
31 request_body TEXT NOT NULL,
32 state TEXT NOT NULL CHECK (state IN ('pending', 'ok', 'failed', 'gave_up')),
33 attempt INTEGER NOT NULL DEFAULT 0,
34 next_attempt_at TEXT NULL,
35 response_status INTEGER NULL,
36 response_headers TEXT NULL,
37 response_body TEXT NULL, -- first 64 KiB of the body
38 error TEXT NULL,
39 duration_ms INTEGER NULL,
40 created_at TEXT NOT NULL,
41 delivered_at TEXT NULL
42);
43CREATE INDEX webhook_deliveries_state_next ON webhook_deliveries(state, next_attempt_at);
44CREATE INDEX webhook_deliveries_hook_created ON webhook_deliveries(hook_id, created_at);