feat(v2.0d): webhooks + public REST API — closes #35, completes v2.0#46
Merged
Conversation
Last of four v2.0 PRs. Closes the engagement-and-growth milestone
and the entire v2.0 roadmap.
Schema (Drizzle migration 0010)
-------------------------------
- webhooks: id, label, url, secret (48-char nanoid), events JSON,
enabled, audit fields.
- webhook_deliveries: per-attempt log. webhookId CASCADE, event,
payload, responseStatus, responseExcerpt (256-char cap),
durationMs, attempt, nextAttemptAt, ok.
- api_keys: id, label, key_hash UNIQUE (SHA-256 hex of raw key),
prefix (kp_live_xxxx — kept for display only), scopes JSON,
expiresAt, revokedAt, lastUsedAt, audit fields.
Webhook dispatcher
------------------
- $lib/server/webhooks/index.ts.
- WebhookEvent union: article.{publish,unpublish,delete} /
comment.approve / form.submit / subscriber.confirm.
- HMAC-SHA256 sign body with webhook secret. Headers:
X-Khaopad-Signature: sha256=<hex>
X-Khaopad-Event: <event>
X-Khaopad-Delivery: <uuid>
- 5s fetch timeout, 3 inline attempts, 250ms / 1500ms backoff.
- Best-effort writes a webhook_deliveries row for every attempt,
success or fail. Operator debugs from CMS.
- dispatchEvent() is fire-and-forget at every call site so the
originating action never pays the network round-trip.
Wired into 5 events
-------------------
- article.publish / article.unpublish (article edit save +
togglePublish)
- article.delete (article delete action)
- comment.approve (only on approve — spam/archive don't fire)
- form.submit (public POST /api/forms/[key])
- subscriber.confirm (the email click target)
Public REST API
---------------
- $lib/server/api-auth/index.ts: parses Authorization: Bearer …,
delegates to provider.authenticateApiKey(), enforces scopes.
- /api/public/articles (paginated, ?locale=en|th, ?limit, ?page)
- /api/public/articles/[slug]
- /api/public/categories
- /api/public/tags
- /api/public/pages
- All routes require Authorization: Bearer kp_live_…. Per-key
scopes: articles:read, categories:read, tags:read, pages:read,
or *:read for the bundle. Drafts and future-dated published
articles 404 — consumers never see unpublished content.
- lastUsedAt bumped fire-and-forget on every successful auth.
CMS UI
------
- /cms/webhooks: list, create, edit, rotate-secret, delete. Subscribe
to specific events via checkbox grid. Show signing secret in a
collapsible details for verification setup.
- /cms/api-keys: list, create with one-time secret display (copy
button + clear "won't be shown again" warning), revoke (soft —
rejected at auth), delete (hard).
- Both pages admin+ gated. Sidebar entries under Admin group.
i18n: 39 new cms_webhooks_* / cms_api_keys_* keys (EN + TH).
Migration 0010 already applied to live D1.
Closes #35. Completes v2.0 (engagement-and-growth) and the entire
roadmap. Backlog (OAuth, block editor, AI authoring, multi-site,
A/B, gated content) stays explicitly uncommitted.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
thunpisit
added a commit
to codustry/khaopad-example
that referenced
this pull request
May 2, 2026
) (#14) Cherry-picks upstream PR codustry#46. Migration 0010 already applied to live D1. Field-merged i18n: 39 new cms_webhooks_* / cms_api_keys_* keys (EN + TH). Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Last v2.0 PR. Closes #35. Completes the engagement-and-growth milestone and the entire roadmap through v2.0.
Schema (migration 0010)
Webhook dispatcher
Wired into 5 events
`article.publish` / `article.unpublish` / `article.delete` / `comment.approve` / `form.submit` / `subscriber.confirm`
Public REST API
CMS
i18n
39 new keys (EN + TH).
Migration
0010 already applied to live D1.
Roadmap state after this merges
✅ v2.0 complete. Every version v1.0 → v2.0 is shipped.
Backlog (not committed): OAuth providers, block editor, AI authoring, multi-site, A/B, gated content.
Test plan
🤖 Generated with Claude Code