From ac6f16a4fedf3338f2026bd2b9ef2b00aa5be7fa Mon Sep 17 00:00:00 2001 From: Sivert Date: Tue, 15 Sep 2026 11:58:09 +0200 Subject: [PATCH] Webhooks get a docs page with a live card preview (GRYT-1187) There was no page for webhooks, only six rows in the Server API table. The new one is under Build, next to bots. It covers making a webhook in the app, the URL, curl examples, the full payload, pictures, mentions, the fallback line older clients show, errors, rate limits and three recipes. The payload tables are read from a copy of the server's openapi/webhooks.json and drawn with fumadocs' TypeTable, so nothing about the fields is written by hand. The .md copy of the page gets the same tables as markdown. The preview is a JSON editor with four presets next to the real WebhookCard from @gryt/ui 0.34. It checks the payload as you type and shows the answer the server would give, and it can copy the payload as a curl command. The checks are a small copy of the server's rules rather than zod, to keep the page light. scripts/check-webhook-spec.mjs keeps both honest. It fetches server main and fails when the spec copy differs, when the limits differ, when a problem or warning code has no explanation, or when the preview and the server's own zod schema disagree about any of 3,000 generated payloads. It runs in CI as yarn test:webhook-spec. The preview loads through next/dynamic. Every docs page is one route, so a plain import put its 17 KB of gzipped JS on all of them. Now only this page loads it. About 12 KB of that is @gryt/ui's card, and 8.5 KB of the card is tailwind-merge. Two bugs found on the way have their own tasks: bad JSON and bodies over 2 MB get a 500 instead of a 4xx (GRYT-1198), and webhook rows overflow the server settings dialog (GRYT-1199). The page describes what happens today. Co-Authored-By: Claude Opus 5 --- .github/workflows/ci.yml | 4 + content/docs/build/index.mdx | 7 +- content/docs/build/meta.json | 3 +- content/docs/build/server-api.mdx | 2 + content/docs/build/webhooks.mdx | 349 +++++++++ package.json | 6 +- public/webhooks/create-webhook.png | Bin 0 -> 106518 bytes public/webhooks/sample-icon.png | Bin 0 -> 337 bytes public/webhooks/sample-image.jpg | Bin 0 -> 11813 bytes public/webhooks/sample-thumb.jpg | Bin 0 -> 1197 bytes scripts/check-webhook-spec.mjs | 147 ++++ source.config.ts | 13 +- src/app/global.css | 5 + src/components/webhooks/codes.ts | 27 + src/components/webhooks/inline-markdown.tsx | 48 ++ src/components/webhooks/openapi.json | 775 ++++++++++++++++++++ src/components/webhooks/presets.ts | 104 +++ src/components/webhooks/preview-loader.tsx | 8 + src/components/webhooks/preview.tsx | 241 ++++++ src/components/webhooks/reference.tsx | 47 ++ src/components/webhooks/spec.ts | 102 +++ src/components/webhooks/validate.ts | 331 +++++++++ yarn.lock | 10 +- 23 files changed, 2219 insertions(+), 10 deletions(-) create mode 100644 content/docs/build/webhooks.mdx create mode 100644 public/webhooks/create-webhook.png create mode 100644 public/webhooks/sample-icon.png create mode 100644 public/webhooks/sample-image.jpg create mode 100644 public/webhooks/sample-thumb.jpg create mode 100644 scripts/check-webhook-spec.mjs create mode 100644 src/components/webhooks/codes.ts create mode 100644 src/components/webhooks/inline-markdown.tsx create mode 100644 src/components/webhooks/openapi.json create mode 100644 src/components/webhooks/presets.ts create mode 100644 src/components/webhooks/preview-loader.tsx create mode 100644 src/components/webhooks/preview.tsx create mode 100644 src/components/webhooks/reference.tsx create mode 100644 src/components/webhooks/spec.ts create mode 100644 src/components/webhooks/validate.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 193cc4b..2275eb2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,6 +22,10 @@ jobs: # to be swept, and that list only ever shrinks. - run: yarn test:comment-length + # The Webhooks page reads server main's openapi/webhooks.json, and its preview + # mirrors the payload rules. This fails when either drifts from the server. + - run: yarn test:webhook-spec + # `next build` type-checks as part of building and there is no separate # typecheck script, so this is both. - run: yarn build diff --git a/content/docs/build/index.mdx b/content/docs/build/index.mdx index acd6dd7..e740d29 100644 --- a/content/docs/build/index.mdx +++ b/content/docs/build/index.mdx @@ -4,7 +4,7 @@ description: Bots, addons, plugins, and the packages you can build against icon: Blocks --- -Four ways to extend Gryt, and they run in different places. Which one you want +Five ways to extend Gryt, and they run in different places. Which one you want depends on where your code needs to live. ## Pick where your code runs @@ -25,6 +25,11 @@ depends on where your code needs to live. description="Runs in the server, sees messages and members" href="/docs/build/server-plugins" /> + /api/webhooks// +``` + +The token is the whole of the security. Anyone who has the URL can post as the +webhook, so keep it out of public repositories and logs. There's no way to +rotate a token. If a URL leaks, delete the webhook and make a new one. + +## Send a message + +```bash +curl -X POST 'https://gryt.example.com/api/webhooks/WEBHOOK_ID/TOKEN' \ + -H 'Content-Type: application/json' \ + -d '{"text": "Hello from curl"}' +``` + +That's a whole message. `text` takes the same markdown as the chat box. +Add `cards` for something with a title, colour, fields and pictures: + +```bash +curl -X POST 'https://gryt.example.com/api/webhooks/WEBHOOK_ID/TOKEN' \ + -H 'Content-Type: application/json' \ + -d '{ + "display_name": "Backups", + "cards": [{ + "title": "Backup finished", + "description": "All 3 volumes copied in 2m 41s.", + "color": "#3fb27f" + }] + }' +``` + +The server answers once the message is posted: + +```json +{ + "message_id": "7999e063-ccdb-46d3-8cd3-b1decc35dfd6", + "conversation_id": "general", + "warnings": [] +} +``` + +## Try a card + +Edit the JSON and the card on the right redraws. It's drawn with the same +`WebhookCard` component from [`@gryt/ui`](/docs/build/ui) that the app uses, +and the checks under the editor follow the server's rules, so a payload that +passes here passes there. **Copy as curl** gives you a command with a +placeholder URL to swap for yours. + + + +Two things differ from the app. The preview loads pictures straight from the +URLs you type, from your own browser. Gryt doesn't: the server downloads them +once when the message is posted (see [Pictures](#pictures)). And the preview +only does bold, italics, strikethrough, inline code and links. The app's +markdown also does code blocks, lists, emoji and mentions. + +## Payload + +This reference is read from the server's own OpenAPI document, +[`openapi/webhooks.json`](https://github.com/Gryt-chat/server/blob/main/openapi/webhooks.json), +which is generated from the schema the route checks payloads with. A check in +CI fails when this page and that file disagree. + +### Message + + + +A message needs `text`, at least one card, or both. `display_name` and +`avatar_url` only change this one message. The webhook's own name and picture +stay as they are. + +### Card + + + +A card needs at least one of `title`, `description`, `fields`, `image_url` or +`author`. The title, description, author name, footer text and every field's +name and value count towards a limit of 6000 characters, added up across all +the cards in the message. + +Timestamps show in each reader's own time zone. + +### Author + + + +### Field + + + +Inline fields sit side by side as long as there's room. A field that isn't +inline gets a row to itself. + +### Footer + + + +## Pictures + +`avatar_url`, `image_url`, `thumbnail_url` and both `icon_url`s are downloaded +by the Gryt server when the message is posted. It stores a copy and the message +points at that copy. People reading the channel load the picture from your Gryt +server and never contact the host it came from, so that host can't see who +read the message or when. + +That also means the picture is fixed at post time. Changing the file at the +original URL afterwards changes nothing in Gryt. + +- PNG, JPEG, WebP and GIF. SVG is refused. The server checks the file's bytes, + not its name or `Content-Type` +- Up to 8 MB for `image_url` and `thumbnail_url`, 1 MB for `avatar_url` and the + icons, and 32 MB for all the pictures in one message +- 12 seconds to download all of them +- Private and local addresses are refused, and so is a chain of more than 5 + redirects +- The same URL used twice in a message is downloaded once + +A picture that fails doesn't stop the message. It posts without that picture, +and the reply lists what went wrong under `warnings`. Because of the downloads, +a message with pictures can take a few seconds to answer. + +## Mentions + +Only `text` can notify anyone. A mention is a markdown link to `mention:` and +the member's ID: + +```json +{ "text": "Deploy failed, [@Sivert](mention:user_166ad0aa-aef6-494d-8475-9c842fbc29b2) can you look?" } +``` + +To get an ID, right-click the member in the member list and pick **Copy ID**. + +Mentions inside cards draw like mentions but never notify, so a card listing +ten commit authors doesn't ping all ten. + +## Older clients + +A client that can't draw cards shows a single line instead. When a message has +cards and no `text`, the server writes that line itself: the first card's +title, or its author's name if it has no title, plus a count like "(+2 more)" +when there are more cards. When the first card has neither, it says "Posted a +card", or "Posted 3 cards". + +Mentions and links are broken on purpose in that line, so it can't ping anyone +or unfurl. Clients that do draw cards hide it. If you send `text` as well, +older clients show your `text` and nothing else. + +## When it goes wrong + +A refused payload posts nothing. Every problem comes back at once, up to 20, +each with a `path` into your JSON: + +```json +{ + "error": "invalid_payload", + "message": "The payload has 3 problems.", + "problems": [ + { "path": "cards[0].color", "code": "invalid_color", "message": "Use #rrggbb or an integer from 0 to 16777215." }, + { "path": "cards[0].timestamp", "code": "invalid_timestamp", "message": "Invalid ISO datetime" }, + { "path": "cards[0]", "code": "empty_card", "message": "A card needs a title, description, fields, image_url or author." } + ] +} +``` + +`limit` is there too when the problem is a limit. Match on `code`, since +`message` is for people and can change. + + + +Some answers have no `problems` list. The first two are from before cards +existed, and they come back instead of `invalid_payload` when they're the only +problem: + +| Status | Body | When | +|---|---|---| +| 400 | `{"error": "empty_message"}` | No `text` (or only spaces) and no cards | +| 400 | `{"error": "message_too_long"}` | `text` is over 4,000 characters | +| 400 | `{"error": "no_channel"}` | The webhook has no channel set | +| 404 | `{"error": "not_found"}` | No webhook with that ID and token | +| 429 | `{"error": "rate_limited", "retry_after_ms": 59988}` | See [Rate limits](#rate-limits) | + + +JSON that doesn't parse gets a `500` with `"error": "internal_error"` and the +parser's message today. So does a body over 2 MB. Both should really be 4xx +answers and will probably change. Don't match on them. Treat a 5xx as "check +the request, then try again later". + + +### Warnings + +A message that posts can still come back with `warnings`: + +```json +{ + "message_id": "7999e063-ccdb-46d3-8cd3-b1decc35dfd6", + "conversation_id": "general", + "warnings": [ + { "path": "embeds", "code": "unknown_key", "message": "embeds isn't part of a webhook message and was ignored." }, + { "path": "cards[0].image_url", "code": "blocked", "message": "That address isn't allowed, so the picture was left out." } + ] +} +``` + +Keys Gryt doesn't know are ignored rather than refused, so a payload written for +another chat app mostly works. Log `warnings` from anything automated, or a +missing picture goes unnoticed. + + + +## Rate limits + +Each webhook gets a burst of 15 requests, then about one every 2 seconds, or 30 +a minute. Go over and that webhook gets `429` for 60 seconds, with +`retry_after_ms` saying how long is left. + +It counts every request to the webhook's URL, including refused ones, and it's +per webhook rather than per caller, so two scripts sharing a URL share the +limit. The counter lives in the server's memory and resets when it restarts. +[Rate limiting](/docs/host/rate-limiting) has the server's other limits. + +## Recipes + +### Deploy notice from GitHub Actions + +Save the webhook URL as a repository secret called `GRYT_WEBHOOK_URL`, then add +a step at the end of the deploy job: + +```yaml +- name: Tell Gryt + if: always() + env: + GRYT_WEBHOOK_URL: ${{ secrets.GRYT_WEBHOOK_URL }} + STATUS: ${{ job.status }} + run: | + if [ "$STATUS" = "success" ]; then color="#3fb27f"; else color="#e5484d"; fi + jq -n \ + --arg status "$STATUS" --arg color "$color" \ + --arg repo "$GITHUB_REPOSITORY" --arg sha "${GITHUB_SHA::7}" \ + --arg actor "$GITHUB_ACTOR" \ + --arg url "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" \ + '{ + display_name: "Deploys", + cards: [{ + title: "Deploy \($status): \($repo)", + url: $url, + color: $color, + fields: [ + { name: "Commit", value: $sha, inline: true }, + { name: "By", value: $actor, inline: true } + ], + timestamp: (now | todate) + }] + }' | + curl -fsS -X POST "$GRYT_WEBHOOK_URL" \ + -H 'Content-Type: application/json' --data-binary @- +``` + +`jq` is already on GitHub's runners. It builds the JSON for you, so a quote in +a name can't break the payload. + +### Uptime alert from cron + +A script that checks a URL and posts only when the answer changes, so a site +that's down all night sends two messages instead of 500: + +```bash +#!/bin/sh +# Run from cron every minute: * * * * * /usr/local/bin/check-api.sh +WEBHOOK_URL="https://gryt.example.com/api/webhooks/WEBHOOK_ID/TOKEN" +TARGET="https://api.example.com/health" +STATE=/var/tmp/check-api.state + +code=$(curl -s -o /dev/null -m 10 -w '%{http_code}' "$TARGET") +[ "$code" = "200" ] && now=up || now=down +[ "$(cat "$STATE" 2>/dev/null)" = "$now" ] && exit 0 +echo "$now" > "$STATE" + +if [ "$now" = "down" ]; then + title="$TARGET is down"; color="#e5484d" +else + title="$TARGET is back"; color="#3fb27f" +fi + +jq -n --arg title "$title" --arg color "$color" --arg code "$code" '{ + display_name: "Uptime", + cards: [{ + title: $title, + color: $color, + fields: [{ name: "Status", value: $code, inline: true }], + timestamp: (now | todate) + }] +}' | curl -fsS -X POST "$WEBHOOK_URL" -H 'Content-Type: application/json' --data-binary @- +``` + +### Plain text from any script + +No `jq`, no cards. Fine for a quick "it's done": + +```bash +curl -fsS -X POST "$WEBHOOK_URL" \ + -H 'Content-Type: application/json' \ + -d "{\"text\": \"Backup on $(hostname) finished at $(date -u +%H:%M) UTC\"}" +``` + +Keep anything you didn't write yourself out of a string like that. A file name +with a quote in it breaks the JSON. Use `jq -n --arg` like the recipes above +when the text comes from somewhere else. + +## Managing webhooks over the API + +The Webhooks tab uses these, and so can you, with a member's access token that +has **Manage webhooks**. They're listed in [Server API](/docs/build/server-api#webhooks), +and the request and response shapes are in the same +[`openapi/webhooks.json`](https://github.com/Gryt-chat/server/blob/main/openapi/webhooks.json). diff --git a/package.json b/package.json index e1b5b76..941e941 100644 --- a/package.json +++ b/package.json @@ -8,10 +8,11 @@ "dev": "next dev --turbo", "start": "next start", "test:comment-length": "node scripts/check-comment-length.mjs", + "test:webhook-spec": "node scripts/check-webhook-spec.mjs", "postinstall": "fumadocs-mdx" }, "dependencies": { - "@gryt/ui": "^0.32.0", + "@gryt/ui": "^0.34.0", "fumadocs-core": "^16.15.10", "fumadocs-mdx": "^15.4.0", "fumadocs-mermaid": "^1.3.0", @@ -33,6 +34,7 @@ "eslint-config-next": "15.5.2", "postcss": "^8.5.6", "tailwindcss": "^4.1.13", - "typescript": "^5.9.2" + "typescript": "^5.9.2", + "zod": "^4.6.5" } } diff --git a/public/webhooks/create-webhook.png b/public/webhooks/create-webhook.png new file mode 100644 index 0000000000000000000000000000000000000000..cda0c3737d6c352351deefb15d63f5739ea215f7 GIT binary patch literal 106518 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
?Fj^(vFeatnZ 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 lH$3!1s-5s_#1ko*%I8gRB~x5T(?)H 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 QV7M@Ny85}Sb4q9e0Pe<&V*mgE literal 0 HcmV?d00001 diff --git a/public/webhooks/sample-image.jpg b/public/webhooks/sample-image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f8d30e8795bcdac420acaf59ff13c705b3cf4276 GIT binary patch literal 11813 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 JTOc}i;(ve`zTW@< literal 0 HcmV?d00001 diff --git a/public/webhooks/sample-thumb.jpg b/public/webhooks/sample-thumb.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5da4c354f86b852456060eb45a019cdd31c5cdeb GIT binary patch literal 1197 zcmex=0xm|f)U*_9klY8Gb zY5RVD`?$>QM#}tr?&Y_Gxat?@tvPYa{>AOx>NlI76u(`jxvf>?cXm_mGVZci@0FKd ziwM2lbb9})qEmKg0T{L?c$!V8ZN{uUQM0Gk?_Cpdexm-~<>&SH?lnsJYOdrTJ)w45 zeRbM?<($uF-{%zhZ+jwA?{;tE`7KfY+qVS2Er@H&-S+tO7MaSj#l6aA+&eD4^_Fwd zi4--=FWwL=b2KiuJBg1~}oSeRLr{J&G zCk2XfzFJl6e7^MM!|%&96}~U`bjfnO-y89<@Z-7}e}8=K;??Whm=khj>wGy~jmPeX z=PpjuUA1FM;?a%4V#lq_lY^IU?pS$r-3Wy?0b#w_b)xEaQV;+lRJy-pI^RYVY2k+mv<*jGEVYHMO{of+vb=M}Gi%LUStc~&FuDBy GZvp_Be^Pn? literal 0 HcmV?d00001 diff --git a/scripts/check-webhook-spec.mjs b/scripts/check-webhook-spec.mjs new file mode 100644 index 0000000..4fe15f8 --- /dev/null +++ b/scripts/check-webhook-spec.mjs @@ -0,0 +1,147 @@ +// Fails when the Webhooks page drifts from Gryt-chat/server: the vendored spec, the limits, or the preview's validation. +// Reads server main over HTTPS, or a local checkout from GRYT_SERVER_DIR. + +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { dirname, join } from "node:path"; +import { fileURLToPath, pathToFileURL } from "node:url"; +import { isDeepStrictEqual } from "node:util"; + +const root = join(dirname(fileURLToPath(import.meta.url)), ".."); +const SPEC = "src/components/webhooks/openapi.json"; +const RAW = "https://raw.githubusercontent.com/Gryt-chat/server/main"; + +async function serverFile(path) { + if (process.env.GRYT_SERVER_DIR) return readFileSync(join(process.env.GRYT_SERVER_DIR, path), "utf8"); + const res = await fetch(`${RAW}/${path}`); + if (!res.ok) throw new Error(`${RAW}/${path} answered ${res.status}`); + return res.text(); +} + +const failures = []; +const fail = (msg) => failures.push(msg); + +const [specText, schemasTs, limitsTs] = await Promise.all([ + serverFile("openapi/webhooks.json"), + serverFile("src/routes/webhookSchemas.ts"), + serverFile("src/utils/messageLimits.ts"), +]); + +if (!isDeepStrictEqual(JSON.parse(specText), JSON.parse(readFileSync(join(root, SPEC), "utf8")))) { + fail(`${SPEC} differs from openapi/webhooks.json on server main. Copy it over and check the page still says the same.`); +} + +// Node won't strip types under node_modules, so the server source sits in a temp dir, pointed at our zod. +const work = mkdtempSync(join(tmpdir(), "gryt-webhook-check-")); +process.on("exit", () => rmSync(work, { recursive: true, force: true })); +writeFileSync(join(work, "messageLimits.ts"), limitsTs); +writeFileSync( + join(work, "webhookSchemas.ts"), + schemasTs + .replace(/from "\.\.\/utils\/messageLimits"/, 'from "./messageLimits.ts"') + .replace(/from "zod"/, `from ${JSON.stringify(import.meta.resolve("zod"))}`), +); + +const server = await import(pathToFileURL(join(work, "webhookSchemas.ts")).href); +const { MESSAGE_MAX_LENGTH } = await import(pathToFileURL(join(work, "messageLimits.ts")).href); +const docs = await import(pathToFileURL(join(root, "src/components/webhooks/validate.ts")).href); +const { WEBHOOK_PRESETS } = await import(pathToFileURL(join(root, "src/components/webhooks/presets.ts")).href); +const { PROBLEM_CODES, WARNING_CODES } = await import(pathToFileURL(join(root, "src/components/webhooks/codes.ts")).href); + +const spec = JSON.parse(specText).components.schemas; +const listed = { + problem: [spec.InvalidPayload.properties.problems.items.properties.code.enum, PROBLEM_CODES], + warning: [spec.WebhookMessageSent.properties.warnings.items.properties.code.enum, WARNING_CODES], +}; +for (const [kind, [codes, words]] of Object.entries(listed)) { + const missing = codes.filter((code) => !words[code]); + const extra = Object.keys(words).filter((code) => !codes.includes(code)); + if (missing.length) fail(`codes.ts has no words for the ${kind} code(s) ${missing.join(", ")}.`); + if (extra.length) fail(`codes.ts explains ${kind} code(s) the spec doesn't have: ${extra.join(", ")}.`); +} + +if (!isDeepStrictEqual({ ...server.WEBHOOK_LIMITS, text: MESSAGE_MAX_LENGTH }, { ...docs.WEBHOOK_LIMITS })) { + fail(`WEBHOOK_LIMITS in validate.ts don't match the server's: ${JSON.stringify(server.WEBHOOK_LIMITS)}, text ${MESSAGE_MAX_LENGTH}`); +} + +/** The route's answer, as src/routes/webhooks.ts builds it. */ +function serverVerdict(body) { + const parsed = server.webhookMessageSchema.safeParse(body ?? {}, { reportInput: true }); + if (parsed.success) return { ok: true, warnings: server.unknownKeys(body).map(({ path, code }) => ({ path, code })) }; + const problems = server.problemsFrom(parsed.error); + if (problems.length === 1 && problems[0].code === "empty_message") return { ok: false, error: "empty_message" }; + if (problems.length === 1 && problems[0].path === "text" && problems[0].code === "too_long") return { ok: false, error: "message_too_long" }; + return { ok: false, error: "invalid_payload", problems: problems.map(({ path, code, limit }) => ({ path, code, limit })) }; +} + +function docsVerdict(body) { + const v = docs.checkWebhookPayload(body); + if (v.ok) return { ok: true, warnings: v.warnings.map(({ path, code }) => ({ path, code })) }; + if (v.body.error !== "invalid_payload") return { ok: false, error: v.body.error }; + return { ok: false, error: "invalid_payload", problems: v.body.problems.map(({ path, code, limit }) => ({ path, code, limit })) }; +} + +// Seeded, so a failure names a case that reproduces. +let seed = 1187; +const rand = () => ((seed = (seed * 1103515245 + 12345) % 2147483648) / 2147483648); +const pick = (list) => list[Math.floor(rand() * list.length)]; + +const ODD_VALUES = [ + undefined, null, true, 0, 1.5, -1, 16777215, 16777216, "", " ", "x", " padded ", "#3fb27f", "#3FB27G", "red", + "https://example.com/a.png", " https://example.com/b.png ", "http:example.com", "ftp://example.com", "https://", + `https://example.com/${"a".repeat(2048)}`, "2026-09-15T07:42:00Z", "2026-09-15T07:42:00+02:00", "2026-09-15", + "2026-02-30T00:00:00Z", "a".repeat(257), "b".repeat(1025), "c".repeat(4001), [], {}, [{}], { name: "n" }, + { name: "n", value: "v" }, { name: " ", value: "v", inline: "yes" }, { text: "footer", icon_url: "nope" }, +]; + +function mutate(value, depth = 0) { + if (Array.isArray(value)) { + const copy = value.map((v) => (rand() < 0.3 ? mutate(v, depth + 1) : v)); + if (rand() < 0.15) copy.push(copy.length ? structuredClone(pick(copy)) : pick(ODD_VALUES)); + if (rand() < 0.1 && copy.length) copy.splice(Math.floor(rand() * copy.length), 1); + if (rand() < 0.05) while (copy.length && copy.length < 30) copy.push(structuredClone(copy[0])); + return copy; + } + if (value && typeof value === "object") { + const copy = { ...value }; + for (const key of Object.keys(copy)) { + const r = rand(); + if (r < 0.1) delete copy[key]; + else if (r < 0.25) copy[key] = pick(ODD_VALUES); + else if (r < 0.5) copy[key] = mutate(copy[key], depth + 1); + } + if (rand() < 0.1) copy[pick(["extra", "embeds", "username", "inline", "name", "text"])] = pick(ODD_VALUES); + return copy; + } + return rand() < 0.3 ? pick(ODD_VALUES) : value; +} + +const cases = [ + ...WEBHOOK_PRESETS.map((p) => p.payload), + {}, [], { text: " " }, { text: "c".repeat(4001) }, { text: "c".repeat(4001), cards: [{}] }, + { cards: [] }, { cards: Array.from({ length: 11 }, () => ({ title: "t" })) }, + { cards: Array.from({ length: 2 }, () => ({ description: "d".repeat(3001) })) }, + { cards: [{ description: "d".repeat(4000), fields: [{ name: "n".repeat(256), value: "v".repeat(1024) }, { name: "n".repeat(256), value: "v".repeat(1000) }] }] }, +]; +for (let i = 0; i < 3000; i++) cases.push(mutate(structuredClone(pick(WEBHOOK_PRESETS).payload))); + +for (const preset of WEBHOOK_PRESETS) { + if (!docsVerdict(preset.payload).ok) fail(`The "${preset.id}" preset doesn't pass validation.`); +} + +let mismatches = 0; +for (const body of cases) { + const want = serverVerdict(body); + const got = docsVerdict(body); + if (isDeepStrictEqual(want, got)) continue; + if (++mismatches <= 5) { + fail(`Validation differs for ${JSON.stringify(body).slice(0, 300)}\n server: ${JSON.stringify(want).slice(0, 600)}\n docs: ${JSON.stringify(got).slice(0, 600)}`); + } +} +if (mismatches > 5) fail(`...and ${mismatches - 5} more payloads where validation differs.`); + +if (failures.length) { + console.error(`check-webhook-spec: ${failures.length} problem(s)\n\n${failures.join("\n\n")}`); + process.exit(1); +} +console.log(`check-webhook-spec: spec and limits match server main, and ${cases.length} payloads validate the same way.`); diff --git a/source.config.ts b/source.config.ts index 36d649d..d0c3d50 100644 --- a/source.config.ts +++ b/source.config.ts @@ -1,6 +1,7 @@ import { defineConfig, defineDocs } from 'fumadocs-mdx/config'; import { metaSchema, pageSchema } from 'fumadocs-core/source/schema'; import { remarkMdxMermaid } from 'fumadocs-mermaid'; +import { markdownTable } from './src/components/webhooks/spec'; export const docs = defineDocs({ docs: { @@ -11,7 +12,17 @@ export const docs = defineDocs({ headingIds: false, // remarkMdxMermaid turned the fence into ; a model reads the fence better. stringify(node) { - if (node.type !== 'mdxJsxFlowElement' || node.name !== 'Mermaid') return; + if (node.type !== 'mdxJsxFlowElement') return; + const attrs = Object.fromEntries( + node.attributes.flatMap((attr) => + attr.type === 'mdxJsxAttribute' && typeof attr.value === 'string' ? [[attr.name, attr.value]] : [], + ), + ); + // The Webhooks page's tables come from the server's spec; the preview only exists on the web page. + if (node.name === 'WebhookPreview') return '_The interactive card preview is on the web version of this page._'; + const table = node.name ? markdownTable(node.name, attrs) : undefined; + if (table) return table; + if (node.name !== 'Mermaid') return; const chart = node.attributes.find( (attr) => attr.type === 'mdxJsxAttribute' && attr.name === 'chart', ); diff --git a/src/app/global.css b/src/app/global.css index c247e50..fbce34f 100644 --- a/src/app/global.css +++ b/src/app/global.css @@ -5,6 +5,11 @@ /* Gryt's tokens, unprocessed. Dark on :root, and .light swaps the scales. */ @import '@gryt/ui/theme.css'; +/* The one @gryt/ui component the docs draw, on the Webhooks page. Its focus ring lives in a shared + file full of other components' classes, so that one is listed instead of scanned. */ +@source '../../node_modules/@gryt/ui/dist/components/WebhookCard'; +@source inline('focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gryt-accent-light'); + @theme { --font-sans: var(--font-atkinson), sans-serif; --font-mono: var(--font-atkinson-mono), monospace; diff --git a/src/components/webhooks/codes.ts b/src/components/webhooks/codes.ts new file mode 100644 index 0000000..243542b --- /dev/null +++ b/src/components/webhooks/codes.ts @@ -0,0 +1,27 @@ +// What each code in openapi.json means, in words. The spec check fails when a code has no line here. + +export const PROBLEM_CODES: Record = { + required: "Missing, or empty once spaces are trimmed off.", + wrong_type: "The wrong kind of value, like a number where text belongs.", + too_long: "Over the character limit in `limit`.", + too_many: "More cards or fields than `limit` allows.", + invalid_url: "Not an http or https URL.", + invalid_color: "Not `#rrggbb`, and not a whole number from 0 to 16777215.", + invalid_timestamp: "Not ISO 8601 with a time zone. `2026-09-15T07:42:00Z` works, `2026-09-15` doesn't.", + empty_card: "The card has no title, description, fields, image_url or author.", + total_too_long: "All the cards' text together is over 6000 characters.", + invalid_format: "A string in a shape the server doesn't accept.", + invalid: "Anything else the server refused.", +}; + +export const WARNING_CODES: Record = { + unknown_key: "A key Gryt doesn't know. It was ignored and the rest posted.", + blocked: "The picture's address is private or local, or it redirected more than 5 times.", + fetch_failed: "The picture's host answered with an error, or couldn't be reached.", + too_large: "Over 8 MB for an image or thumbnail, or over 1 MB for an icon or avatar.", + timeout: "The pictures weren't all downloaded within 12 seconds.", + unsupported_type: "Not a PNG, JPEG, WebP or GIF. SVG is refused. The server looks at the bytes, not the file name.", + invalid_image: "The file claimed to be an image but couldn't be decoded.", + media_budget: "The message's pictures went over 32 MB together.", + store_failed: "The server couldn't save the picture.", +}; diff --git a/src/components/webhooks/inline-markdown.tsx b/src/components/webhooks/inline-markdown.tsx new file mode 100644 index 0000000..6c400fb --- /dev/null +++ b/src/components/webhooks/inline-markdown.tsx @@ -0,0 +1,48 @@ +import { Fragment, type ReactNode } from 'react'; + +// Bold, italics, strikethrough, inline code and links. The app's chat markdown does more; this covers what cards mostly use. + +const TOKEN = /(`[^`\n]+`)|(\*\*[^*\n]+\*\*)|(__[^_\n]+__)|(~~[^~\n]+~~)|(\*[^*\n]+\*)|(_[^_\n]+_)|(\[[^\]\n]+\]\((https?:\/\/[^)\s]+)\))/; + +function inline(text: string, key: string): ReactNode[] { + const out: ReactNode[] = []; + let rest = text; + let n = 0; + while (rest) { + const m = TOKEN.exec(rest); + if (!m) { + out.push(rest); + break; + } + if (m.index > 0) out.push(rest.slice(0, m.index)); + const [whole] = m; + const k = `${key}-${n++}`; + if (m[1]) out.push({whole.slice(1, -1)}); + else if (m[2] || m[3]) out.push({inline(whole.slice(2, -2), k)}); + else if (m[4]) out.push({inline(whole.slice(2, -2), k)}); + else if (m[5] || m[6]) out.push({inline(whole.slice(1, -1), k)}); + else if (m[7]) { + const label = whole.slice(1, whole.indexOf('](')); + out.push( + + {label} + , + ); + } + rest = rest.slice(m.index + whole.length); + } + return out; +} + +export function renderInlineMarkdown(text: string): ReactNode { + return ( + + {text.split('\n').map((line, i) => ( + + {i > 0 ? '\n' : null} + {inline(line, String(i))} + + ))} + + ); +} diff --git a/src/components/webhooks/openapi.json b/src/components/webhooks/openapi.json new file mode 100644 index 0000000..d98bea2 --- /dev/null +++ b/src/components/webhooks/openapi.json @@ -0,0 +1,775 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Gryt webhooks", + "version": "1", + "description": "Post messages into a Gryt channel from anywhere that can send an HTTP request, and manage the webhooks that do it. Every path is relative to your Gryt server.", + "license": { + "name": "AGPL-3.0-or-later", + "identifier": "AGPL-3.0-or-later" + } + }, + "servers": [ + { + "url": "https://{server}", + "variables": { + "server": { + "default": "gryt.example.com", + "description": "Your Gryt server's address." + } + } + } + ], + "components": { + "securitySchemes": { + "bearerAuth": { + "type": "http", + "scheme": "bearer", + "description": "A member's access token. Needs the manage_webhooks permission." + } + }, + "schemas": { + "WebhookMessage": { + "type": "object", + "properties": { + "text": { + "description": "Markdown. Required unless there are cards. The only part of a message whose mentions notify.", + "type": "string", + "maxLength": 4000 + }, + "display_name": { + "description": "Posts under this name instead of the webhook's. Cut to 64 characters.", + "type": "string" + }, + "avatar_url": { + "description": "Posts with this picture instead of the webhook's. Fetched once and stored, same as card images.", + "type": "string", + "maxLength": 2048, + "format": "uri" + }, + "cards": { + "maxItems": 10, + "type": "array", + "items": { + "type": "object", + "properties": { + "title": { + "description": "Plain text.", + "type": "string", + "minLength": 1, + "maxLength": 256 + }, + "url": { + "description": "Makes the title a link.", + "type": "string", + "maxLength": 2048, + "format": "uri" + }, + "description": { + "description": "Markdown. Mentions don't notify anyone.", + "type": "string", + "minLength": 1, + "maxLength": 4000 + }, + "color": { + "anyOf": [ + { + "type": "string", + "pattern": "^#[0-9a-fA-F]{6}$" + }, + { + "type": "integer", + "minimum": 0, + "maximum": 16777215 + } + ], + "description": "The card's colour, as `#rrggbb` or an integer from 0 to 16777215." + }, + "author": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Plain text." + }, + "url": { + "type": "string", + "maxLength": 2048, + "format": "uri", + "description": "An http or https URL." + }, + "icon_url": { + "type": "string", + "maxLength": 2048, + "format": "uri", + "description": "An http or https URL to a PNG, JPEG, WebP or GIF. The server fetches it once, when the message is posted, and stores a copy. Private and local addresses are refused." + } + }, + "required": [ + "name" + ] + }, + "fields": { + "maxItems": 25, + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Plain text." + }, + "value": { + "type": "string", + "minLength": 1, + "maxLength": 1024, + "description": "Markdown." + }, + "inline": { + "default": false, + "description": "Inline fields share a row.", + "type": "boolean" + } + }, + "required": [ + "name", + "value" + ] + } + }, + "image_url": { + "type": "string", + "maxLength": 2048, + "format": "uri", + "description": "An http or https URL to a PNG, JPEG, WebP or GIF. The server fetches it once, when the message is posted, and stores a copy. Private and local addresses are refused." + }, + "thumbnail_url": { + "type": "string", + "maxLength": 2048, + "format": "uri", + "description": "An http or https URL to a PNG, JPEG, WebP or GIF. The server fetches it once, when the message is posted, and stores a copy. Private and local addresses are refused." + }, + "footer": { + "type": "object", + "properties": { + "text": { + "type": "string", + "minLength": 1, + "maxLength": 2048, + "description": "Plain text." + }, + "icon_url": { + "type": "string", + "maxLength": 2048, + "format": "uri", + "description": "An http or https URL to a PNG, JPEG, WebP or GIF. The server fetches it once, when the message is posted, and stores a copy. Private and local addresses are refused." + } + }, + "required": [ + "text" + ] + }, + "timestamp": { + "description": "ISO 8601, with a time zone.", + "type": "string", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$" + } + } + } + } + } + }, + "WebhookCreate": { + "type": "object", + "properties": { + "channel_id": { + "default": "", + "description": "The channel the webhook posts to.", + "type": "string" + }, + "display_name": { + "description": "Cut to 64 characters. Defaults to \"Webhook\".", + "type": "string" + }, + "avatar_file_id": { + "description": "An uploaded file you can read.", + "type": [ + "string", + "null" + ] + } + } + }, + "WebhookUpdate": { + "type": "object", + "properties": { + "channel_id": { + "type": "string" + }, + "display_name": { + "description": "Cut to 64 characters.", + "type": "string" + }, + "avatar_file_id": { + "description": "An uploaded file you can read, or null to remove it.", + "type": [ + "string", + "null" + ] + } + } + }, + "WebhookMessageSent": { + "type": "object", + "properties": { + "message_id": { + "type": "string" + }, + "conversation_id": { + "type": "string" + }, + "warnings": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "code": { + "type": "string", + "enum": [ + "unknown_key", + "blocked", + "fetch_failed", + "too_large", + "timeout", + "unsupported_type", + "invalid_image", + "media_budget", + "store_failed" + ], + "description": "`unknown_key` is a key that was ignored. Every other code is a picture that was left out." + }, + "message": { + "type": "string" + } + }, + "required": [ + "path", + "code", + "message" + ] + } + } + }, + "required": [ + "message_id", + "conversation_id", + "warnings" + ] + }, + "InvalidPayload": { + "type": "object", + "properties": { + "error": { + "type": "string", + "const": "invalid_payload" + }, + "message": { + "type": "string" + }, + "problems": { + "maxItems": 20, + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string", + "description": "Where in the payload, like `cards[0].fields[3].value`." + }, + "code": { + "type": "string", + "enum": [ + "required", + "wrong_type", + "too_long", + "too_many", + "invalid_url", + "invalid_color", + "invalid_timestamp", + "empty_card", + "total_too_long", + "invalid_format", + "invalid" + ] + }, + "limit": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "message": { + "type": "string" + } + }, + "required": [ + "path", + "code", + "message" + ] + } + } + }, + "required": [ + "error", + "message", + "problems" + ] + }, + "Error": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "error" + ] + }, + "Webhook": { + "type": "object", + "properties": { + "webhook_id": { + "type": "string" + }, + "channel_id": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "avatar_file_id": { + "type": [ + "string", + "null" + ] + }, + "token": { + "type": "string" + }, + "created_by_server_user_id": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + }, + "required": [ + "webhook_id", + "channel_id", + "display_name", + "avatar_file_id", + "token", + "created_by_server_user_id", + "created_at", + "updated_at" + ] + } + } + }, + "paths": { + "/api/webhooks/{webhookId}/{token}": { + "post": { + "operationId": "executeWebhook", + "summary": "Post a message", + "security": [], + "description": "Posts text, cards or both into the webhook's channel. Pictures are downloaded by the server before it answers, so the call can take a few seconds. A picture that fails is left out and listed under `warnings`, and the message still posts. Limited to 30 messages a minute per webhook.", + "parameters": [ + { + "name": "webhookId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "token", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebhookMessage" + } + } + } + }, + "responses": { + "200": { + "description": "Posted.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebhookMessageSent" + } + } + } + }, + "400": { + "description": "The payload was refused, or the webhook has no channel. Nothing was posted.", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/InvalidPayload" + }, + { + "$ref": "#/components/schemas/Error" + } + ] + } + } + } + }, + "404": { + "description": "No webhook with that id and token.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "413": { + "description": "The body is over 256 KB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "429": { + "description": "Too many messages. Try again after `retry_after_ms`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "const": "rate_limited" + }, + "retry_after_ms": { + "type": "integer" + } + } + } + } + } + } + } + } + }, + "/api/webhooks": { + "get": { + "operationId": "listWebhooks", + "summary": "List webhooks", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "The webhooks in channels you can see.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Webhook" + } + } + } + } + } + } + }, + "403": { + "description": "No manage_webhooks permission.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "post": { + "operationId": "createWebhook", + "summary": "Create a webhook", + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebhookCreate" + } + } + } + }, + "responses": { + "201": { + "description": "Created. `url` is what to post to.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Webhook" + }, + { + "type": "object", + "properties": { + "url": { + "type": "string" + } + } + } + ] + } + } + } + }, + "400": { + "description": "The body was refused.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidPayload" + } + } + } + }, + "403": { + "description": "No manage_webhooks permission.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "404": { + "description": "The avatar file doesn't exist or you can't read it.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/api/webhooks/{webhookId}": { + "get": { + "operationId": "getWebhook", + "summary": "Get a webhook", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "name": "webhookId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The webhook.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Webhook" + }, + { + "type": "object", + "properties": { + "url": { + "type": "string" + } + } + } + ] + } + } + } + }, + "404": { + "description": "No such webhook.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "patch": { + "operationId": "updateWebhook", + "summary": "Update a webhook", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "name": "webhookId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebhookUpdate" + } + } + } + }, + "responses": { + "200": { + "description": "Updated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Webhook" + } + } + } + }, + "400": { + "description": "The body was refused.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidPayload" + } + } + } + }, + "404": { + "description": "No such webhook, or an avatar file you can't read.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "delete": { + "operationId": "deleteWebhook", + "summary": "Delete a webhook", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "name": "webhookId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Deleted.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "ok": { + "const": true + } + } + } + } + } + }, + "404": { + "description": "No such webhook.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + } + } +} diff --git a/src/components/webhooks/presets.ts b/src/components/webhooks/presets.ts new file mode 100644 index 0000000..a27d541 --- /dev/null +++ b/src/components/webhooks/presets.ts @@ -0,0 +1,104 @@ +// Starting payloads for the preview on the Webhooks page. Every one of them has to pass the server's rules. + +const ASSETS = "https://docs.gryt.chat/webhooks"; + +export interface WebhookPreset { + id: string; + label: string; + payload: Record; +} + +export const WEBHOOK_PRESETS: WebhookPreset[] = [ + { + id: "minimal", + label: "Minimal", + payload: { + cards: [ + { + title: "Backup finished", + description: "All 3 volumes copied in 2m 41s.", + color: "#3fb27f", + }, + ], + }, + }, + { + id: "rich", + label: "Rich", + payload: { + text: "api v2.14.0 is live.", + display_name: "Deploys", + avatar_url: `${ASSETS}/sample-icon.png`, + cards: [ + { + author: { name: "ci.example.com", url: "https://example.com/runner", icon_url: `${ASSETS}/sample-icon.png` }, + title: "Deploy finished: api v2.14.0", + url: "https://example.com/deploys/2140", + description: "Rolled out to **eu-north** and **us-east** in 4 minutes.\nTwo migrations ran, and `0047_threads` took the longest.", + color: "#3fb27f", + fields: [ + { name: "Environment", value: "production", inline: true }, + { name: "Commit", value: "a1b2c3d", inline: true }, + { name: "Duration", value: "4m 12s", inline: true }, + ], + thumbnail_url: `${ASSETS}/sample-thumb.jpg`, + image_url: `${ASSETS}/sample-image.jpg`, + footer: { text: "ci.example.com", icon_url: `${ASSETS}/sample-icon.png` }, + timestamp: "2026-09-15T07:42:00Z", + }, + ], + }, + }, + { + id: "fields", + label: "Lots of fields", + payload: { + display_name: "Nightly report", + cards: [ + { + title: "Nightly build, 15 September", + url: "https://example.com/builds/nightly", + color: 5793266, + fields: [ + { name: "Linux", value: "passed", inline: true }, + { name: "macOS", value: "passed", inline: true }, + { name: "Windows", value: "passed", inline: true }, + { name: "Android", value: "passed", inline: true }, + { name: "iOS", value: "1 flaky test", inline: true }, + { name: "Web", value: "passed", inline: true }, + { name: "Tests", value: "4,812 run, 0 failed, 1 retried" }, + { name: "Slowest", value: "`voice-reconnect` at 48 s" }, + ], + footer: { text: "Built from main" }, + timestamp: "2026-09-15T03:00:00Z", + }, + ], + }, + }, + { + id: "alert", + label: "Alert", + payload: { + text: "The API is down.", + display_name: "Uptime", + cards: [ + { + author: { name: "status.example.com", url: "https://example.com/status" }, + title: "api.example.com is not responding", + url: "https://example.com/status/api", + description: "3 checks in a row failed from 2 regions.", + color: "#e5484d", + fields: [ + { name: "Status", value: "503", inline: true }, + { name: "Down for", value: "4 minutes", inline: true }, + { name: "Last healthy", value: "07:38 UTC", inline: true }, + ], + footer: { text: "Checked every 60 s" }, + timestamp: "2026-09-15T07:42:00Z", + }, + ], + }, + }, +]; + +export const PLACEHOLDER_WEBHOOK_URL = "https://gryt.example.com/api/webhooks/WEBHOOK_ID/TOKEN"; diff --git a/src/components/webhooks/preview-loader.tsx b/src/components/webhooks/preview-loader.tsx new file mode 100644 index 0000000..823143f --- /dev/null +++ b/src/components/webhooks/preview-loader.tsx @@ -0,0 +1,8 @@ +'use client'; + +import dynamic from 'next/dynamic'; + +// Every docs page shares one route, so a plain import would ship the preview and its card to all of them. +export const WebhookPreview = dynamic(() => import('./preview').then((m) => m.WebhookPreview), { + loading: () =>