-
-
Notifications
You must be signed in to change notification settings - Fork 264
feat: add webhook support for card events #343
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
- Mark OpenProject as stopped (replaced by Kan.bn) - Update services table and Caddy routing - Note calendar sync pending on webhook PR: kanbn/kan#343 - Update backup services list Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
MaxwellMergeSlam
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review - PR #343
Summary: Adds optional webhook support for card events (create, update, move, delete) with HMAC signature verification.
Changes reviewed:
- New
packages/api/src/utils/webhook.ts- Webhook utility with signature generation - Modified
packages/api/src/routers/card.ts- Added webhook calls to create/update/delete procedures - Modified
apps/web/src/env.ts- Added WEBHOOK_URL and WEBHOOK_SECRET env vars
Quality Assessment:
| Aspect | Status | Notes |
|---|---|---|
| Code Quality | ✅ pass | Clean, well-typed code following project patterns |
| Tests | No tests added (but api package has no existing tests) | |
| Security | ✅ pass | HMAC-SHA256 signature for payload verification |
| Performance | ✅ pass | Non-blocking (void + async), failures logged but don't block |
Issues found:
-
Minor - Missing timeout for fetch (
packages/api/src/utils/webhook.ts:62)- The fetch call has no timeout, which could cause issues if the webhook endpoint is slow
- Consider adding
AbortControllerwith a reasonable timeout (e.g., 5-10 seconds)
-
Minor - Date serialization (
packages/api/src/utils/webhook.ts:17)dueDateis typed asDate | nullbut JSON.stringify will serialize it as an ISO string- The type in
WebhookPayloadshould reflect this (string | nullin the serialized form)
-
Minor - Email exposure (
packages/api/src/routers/card.ts:180,980)- User email is included in webhook payloads, which may be a privacy concern depending on the webhook consumer
- Consider making email inclusion configurable or use a hashed identifier
Suggestions:
- Consider adding a retry mechanism for failed webhook deliveries (with exponential backoff)
- Could add support for multiple webhook URLs in the future
- Documentation in the README about the webhook feature would help users configure it
Verdict: APPROVE
This PR adds a clean, well-designed webhook feature that follows the project's patterns. The implementation is non-blocking and secure with HMAC signatures. The minor issues noted are suggestions for improvement rather than blocking concerns. The PR provides good value for enabling external integrations like calendar sync, notifications, and audit logging.
|
Addressed the review feedback in commit 1f7d7ec:
Thanks for the thorough review! |
aa22cfb to
3fbd91e
Compare
MaxwellMergeSlam
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review - PR #343
Summary: Adds webhook support for card events with full CRUD management UI, HMAC signature verification, and comprehensive test coverage.
Changes reviewed:
- Database schema:
workspace_webhookstable with RLS enabled - API router: CRUD operations + test endpoint with proper auth checks
- Webhook utility: Fire-and-forget delivery with 10s timeout
- UI: Settings page, webhook list, create/edit modal, delete confirmation
- Tests: 45 tests (16 unit + 15 router + 14 integration with PGlite)
- Localization: All 9 languages updated
Quality Assessment:
| Aspect | Status | Notes |
|---|---|---|
| Code Quality | ✅ pass | Clean, well-structured code following existing patterns |
| Tests | ✅ pass | Excellent coverage - unit, router, and integration tests all pass |
| Security | ✅ pass | Admin-only access, HMAC-SHA256 signatures, input validation |
| Performance | ✅ pass | Fire-and-forget delivery, 10s timeout prevents blocking |
Highlights:
- Proper authorization: All endpoints require admin role via
assertUserInWorkspace - Webhook isolation: Webhooks are workspace-scoped with FK constraints
- Robust delivery:
Promise.allSettledensures one failure doesn't affect others - Good UX: Test button, active/inactive toggle, secret field handling
Minor observations (non-blocking):
env.tsaddsWEBHOOK_URLandWEBHOOK_SECRETwhich appear unused (webhooks are now per-workspace)- Consider adding retry logic for failed deliveries in future iteration
Verdict: APPROVE
This is a well-implemented feature with solid test coverage and proper security controls. The code follows existing patterns in the codebase and is ready to merge.
a79a7ce to
f0ef99c
Compare
Add optional webhook functionality to notify external services when cards are created, updated, moved, or deleted. Configuration: - WEBHOOK_URL: URL to POST webhook payloads to - WEBHOOK_SECRET: Optional secret for HMAC signature verification Webhook payload includes: - Event type (card.created, card.updated, card.moved, card.deleted) - Card data (id, title, description, dueDate, listId, boardId) - User who performed the action - Changes object (for updates) showing old/new values The webhook is fire-and-forget (non-blocking) to avoid impacting API response times. Failures are logged but don't affect the main operation. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add 10s timeout to webhook fetch using AbortController - Fix dueDate type to string (reflects JSON serialization) - Remove email from user payload for privacy Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add database schema for per-workspace webhook configuration: - workspaceWebhooks table with url, secret, events, active fields - Foreign keys to workspace and user tables - Row-level security enabled Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add repository functions for workspace webhooks: - create, update, getByPublicId, hardDelete - getAllByWorkspaceId for listing - getActiveByWorkspaceId for sending webhooks Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add new webhook utility functions: - sendWebhookToUrl: Send to specific URL with result - sendWebhooksForWorkspace: Query DB and send to all active webhooks - Keep legacy sendWebhook for backwards compatibility Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add webhook router with endpoints: - list: Get all webhooks for workspace - create: Add new webhook - update: Modify webhook settings - delete: Remove webhook - test: Send test payload All endpoints require admin role. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update card create/update/delete to send webhooks to all active workspace webhooks instead of just env var webhook. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add React components for webhook management: - WebhookList: Table with edit/test/delete actions - NewWebhookModal: Create/edit form with event checkboxes - DeleteWebhookConfirmation: Confirmation dialog Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add WebhookSettings page with modals - Add webhooks.tsx page route - Add Webhooks tab to settings navigation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove WEBHOOK_URL/WEBHOOK_SECRET env var fallback. Webhooks are now only configured via the database/UI. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add comprehensive test coverage for webhook functionality: - webhook.test.ts: Tests for createCardWebhookPayload, sendWebhookToUrl, and sendWebhooksForWorkspace utility functions - webhook.test.ts (router): Tests for list, create, update, delete, and test procedures including authorization checks Also adds vitest to @kan/api package for running tests. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add integration tests that use a real in-memory PGlite database: - test-db.ts: Helper to create fresh PGlite instances with migrations - webhook.integration.test.ts: 14 tests covering all webhook repo CRUD ops Integration tests verify actual database behavior vs mocked unit tests. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Reset locale files to main and recompile. Webhook translation strings will use English fallback until properly extracted. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add name fields to existing Drizzle queries in list and card repos, then pass them through to createCardWebhookPayload at all three webhook call sites (create, update/move, delete). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ef2d68f to
51400c7
Compare
Summary
This PR adds webhook functionality to notify external services when cards are created, updated, moved, or deleted. Webhooks are configured per-workspace through the Settings UI.
Configuration
Navigate to Settings → Webhooks to:
Webhook Payload
{ "event": "card.created|card.updated|card.moved|card.deleted", "timestamp": "2024-01-29T10:30:00.000Z", "data": { "card": { "id": "123", "title": "Card title", "description": "Card description", "dueDate": "2024-02-01T00:00:00.000Z", "listId": "456", "boardId": "789" }, "user": { "id": "user-id", "name": "User Name" }, "changes": { "title": { "from": "Old Title", "to": "New Title" } } } }Headers
Content-Type: application/jsonX-Webhook-Event: Event typeX-Webhook-Timestamp: ISO timestampX-Webhook-Signature: HMAC-SHA256 signature (if secret is configured)Signature Verification
If a webhook secret is configured, the payload is signed with HMAC-SHA256:
Database Changes
New
workspace_webhookstable:id,publicId- identifiersworkspaceId- FK to workspacename- user-friendly nameurl- webhook endpoint URLsecret- optional HMAC secretevents- JSON array of subscribed eventsactive- enable/disable togglecreatedBy,createdAt,updatedAt- audit fieldsUse Cases
Test Coverage
Test Plan
card.movedevent🤖 Generated with Claude Code