g1t/apps/web/app/lib/webhooks.server.ts
Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Webhooks: every event, to your own addresses, signed and retried | 1 | import type { Hook, HookDelivery, HookOwner, User, Viewer } from "@g1t/contracts"; |
| 2 | ||
| 3 | import { webhooks } from "./services.server"; | |
| 4 | import { unwrap } from "./session.server"; | |
| 5 | ||
| 6 | /** What a webhooks page shows: the webhooks, and one's deliveries if it is open. */ | |
| 7 | export type WebhooksData = { | |
| 8 | hooks: Hook[]; | |
| 9 | open: string | null; | |
| 10 | deliveries: HookDelivery[]; | |
| 11 | }; | |
| 12 | ||
| 13 | export async function loadWebhooks(owner: HookOwner, viewer: Viewer, request: Request): Promise<WebhooksData> { | |
| 14 | const hooks = unwrap(await webhooks.list(viewer, owner)); | |
| 15 | const wanted = new URL(request.url).searchParams.get("hook"); | |
| 16 | const open = hooks.find((hook) => hook.id === wanted)?.id ?? null; | |
| 17 | const deliveries = open ? unwrap(await webhooks.deliveries(viewer, owner, open)) : []; | |
| 18 | return { hooks, open, deliveries }; | |
| 19 | } | |
| 20 | ||
| 21 | /** What a webhooks form asked for, done. */ | |
| 22 | export type WebhooksAction = { | |
| 23 | error?: string; | |
| 24 | /** A webhook just made, and its secret if g1t made it: shown once. */ | |
| 25 | created?: { hook: Hook; secret: string | null }; | |
| 26 | /** The latest delivery a ping or redelivery made. */ | |
| 27 | sent?: HookDelivery; | |
| 28 | }; | |
| 29 | ||
| 30 | export async function actOnWebhooks(owner: HookOwner, actor: User, form: FormData): Promise<WebhooksAction> { | |
| 31 | const id = String(form.get("id") ?? ""); | |
| 32 | switch (form.get("intent")) { | |
| 33 | case "create": { | |
| 34 | const events = form.get("which") === "some" ? form.getAll("event").map(String) : ["*"]; | |
| 35 | if (events.length === 0) return { error: "Choose at least one event, or send everything." }; | |
| 36 | const created = await webhooks.create(actor, owner, { | |
| 37 | url: String(form.get("url") ?? ""), | |
| 38 | events, | |
| 39 | secret: String(form.get("secret") ?? "").trim() || undefined, | |
| 40 | }); | |
| 41 | return created.ok ? { created: created.value } : { error: created.error.message }; | |
| 42 | } | |
| 43 | case "toggle": { | |
| 44 | const updated = await webhooks.update(actor, owner, id, { active: form.get("active") === "true" }); | |
| 45 | return updated.ok ? {} : { error: updated.error.message }; | |
| 46 | } | |
| 47 | case "delete": { | |
| 48 | const removed = await webhooks.delete(actor, owner, id); | |
| 49 | return removed.ok ? {} : { error: removed.error.message }; | |
| 50 | } | |
| 51 | case "ping": { | |
| 52 | const sent = await webhooks.ping(actor, owner, id); | |
| 53 | return sent.ok ? { sent: sent.value } : { error: sent.error.message }; | |
| 54 | } | |
| 55 | case "redeliver": { | |
| 56 | const sent = await webhooks.redeliver(actor, owner, String(form.get("delivery") ?? "")); | |
| 57 | return sent.ok ? { sent: sent.value } : { error: sent.error.message }; | |
| 58 | } | |
| 59 | default: | |
| 60 | return { error: "Nothing to do." }; | |
| 61 | } | |
| 62 | } |