Nick Meinhold 1f9f07df20 feat(web): add webhook management UI (#394)
* feat(api): add webhook CRUD API router and tests

Add tRPC router for managing workspace webhooks:

- list, create, update, delete endpoints (admin role required)
- test endpoint to send a synthetic payload to a webhook URL
- URL validation, event subscription filtering
- Unit tests for all router procedures
- Integration tests with PGlite test database
- Add vitest config and test infrastructure for API package

Depends on #391 (DB schema & repository).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(api): use assertPermission instead of assertUserInWorkspace

Replace assertUserInWorkspace with assertPermission("workspace:manage")
per project conventions. The permissions system is the preferred
authorization approach for new code.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(api): use @kan/db alias instead of relative imports in tests

Replace relative path imports (../../db/src/...) with the @kan/db
alias configured in vitest.config.ts for consistency and robustness.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(api): use webhookUrlSchema in router input validation

Cherry-pick router-related changes from b2cc9ac:
- Use extracted webhookUrlSchema zod validator in create/update
  input schemas for consistent SSRF checks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(api): replace dynamic import with static import for webhook utility

Add packages/api/src/utils/webhook.ts with sendWebhookToUrl,
createCardWebhookPayload, and webhookUrlSchema. Replace the dynamic
import() in the test endpoint with a static import at the top of the
file for better tree-shaking, type-checking, and readability.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(api): align sendWebhooksForWorkspace tests with merged PR #392

The merged delivery utility uses client-side event filtering
(getActiveByWorkspaceId takes 2 args, not 3). Update test assertions
to match the actual implementation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(web): add webhook management UI

Add settings page for managing workspace webhooks:

- Add webhooks page route and settings navigation link
- Add webhook list view with status toggles and action menus
- Add create/edit modal with URL validation and event selection
- Add delete confirmation dialog
- Add WEBHOOKS_ENABLED env flag for feature gating

Depends on #393 (CRUD API router).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(web): remove dead env vars, extract TableRow, import webhookEvents

- Remove unused WEBHOOK_URL and WEBHOOK_SECRET env vars (leftovers
  from earlier env-var-based design)
- Move TableRow component outside WebhookList to avoid re-creation
  on every render
- Import webhookEvents from @kan/db/schema instead of hardcoding
- Simplify formatDate to only handle Date objects (strings are not
  returned by tRPC/Superjson)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(web): gate webhooks settings tab to admin role

The webhook API requires admin role, but the settings tab was visible
to all users (condition: true). Now matches the API's authorization
requirement, addressing reviewer feedback on PR #394.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(web): use webhookEvents constant for form defaults

Replace hardcoded event arrays with [...webhookEvents] in
NewWebhookModal so default values stay in sync if new events
are added to the schema.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(web): use date-fns with locale for webhook date formatting

Replace hardcoded toLocaleDateString('en-US') with date-fns format()
using the useLocalisation() hook's dateLocale, matching the pattern
used throughout the codebase (ActivityList, DateSelector, etc.).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 22:31:45 +00:00
2026-02-19 13:17:49 +00:00
2024-12-12 14:34:10 +00:00
2024-12-12 14:34:10 +00:00
2024-12-12 14:34:10 +00:00
2026-01-25 22:40:33 +00:00
2025-07-11 22:28:21 +01:00
2025-06-02 13:43:20 +01:00

github-background

Kan

The open-source project management alternative to Trello.

Roadmap · Website · Docs · Discord

License

Features 💫

  • 👁️ Board Visibility: Control who can view and edit your boards
  • 🤝 Workspace Members: Invite members and collaborate with your team
  • 🚀 Trello Imports: Easily import your Trello boards
  • 🔍 Labels & Filters: Organise and find cards quickly
  • 💬 Comments: Discuss and collaborate with your team
  • 📝 Activity Log: Track all card changes with detailed activity history
  • 🎨 Templates : Save time with reusable custom board templates
  • Integrations (coming soon) : Connect your favourite tools

See our roadmap for upcoming features.

Screenshot 👁️

hero-dark

Made With 🛠️

Self Hosting 🐳

One-click Deployments

The easiest way to deploy Kan is through Railway. We've partnered with Railway to maintain an official template that supports the development of the project.

Deploy on Railway

Docker Compose

Alternatively, you can self-host Kan with Docker Compose. This will set up everything for you including your postgres database and automatically run migrations.

  1. Create a .env file with your environment variables (see Environment Variables section below)

  2. Use the provided docker-compose.yml file or create your own with the following configuration:

services:
  migrate:
    image: ghcr.io/kanbn/kan-migrate:latest
    container_name: kan-migrate
    networks:
      - kan-network
    environment:
      - POSTGRES_URL=${POSTGRES_URL}
    depends_on:
      postgres:
        condition: service_healthy
    restart: "no"

  web:
    image: ghcr.io/kanbn/kan:latest
    container_name: kan-web
    ports:
      - "${WEB_PORT:-3000}:3000"
    networks:
      - kan-network
    env_file:
      - .env
    environment:
      - NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL}
      - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET}
      - POSTGRES_URL=${POSTGRES_URL}
      - NEXT_PUBLIC_ALLOW_CREDENTIALS=true
    depends_on:
      migrate:
        condition: service_completed_successfully
    restart: unless-stopped

  postgres:
    image: postgres:15
    container_name: kan-db
    environment:
      - POSTGRES_DB=kan_db
      - POSTGRES_USER=kan
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
    ports:
      - 5432:5432
    volumes:
      - kan_postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U kan -d kan_db"]
      interval: 5s
      timeout: 5s
      retries: 10
    restart: unless-stopped
    networks:
      - kan-network

networks:
  kan-network:

volumes:
  kan_postgres_data:
  1. Start the containers in detached mode:
docker compose up -d

The migrate service will automatically run database migrations before the web service starts. The application will be available at http://localhost:3000 (or the port specified in WEB_PORT).

Managing containers:

  • To stop the containers: docker compose down
  • To view logs: docker compose logs -f
  • To view logs for a specific service: docker compose logs -f web or docker compose logs -f migrate
  • To restart the containers: docker compose restart
  • To rebuild after code changes: docker compose up -d --build

For the complete Docker Compose configuration with all optional features, see docker-compose.yml in the repository.

Local Development 🧑‍💻

  1. Clone the repository (or fork)
git clone https://github.com/kanbn/kan.git
  1. Install dependencies
pnpm install
  1. Copy .env.example to .env and configure your environment variables
  2. Migrate database
pnpm db:migrate
  1. Start the development server
pnpm dev

Environment Variables 🔐

Variable Description Required Example
POSTGRES_URL PostgreSQL connection URL To use external database postgres://user:pass@localhost:5432/db
REDIS_URL Redis connection URL For rate limiting (optional) redis://localhost:6379 or redis://redis:6379 (Docker)
EMAIL_FROM Sender email address For Email "Kan <hello@mail.kan.bn>"
SMTP_HOST SMTP server hostname For Email smtp.resend.com
SMTP_PORT SMTP server port For Email 465
SMTP_USER SMTP username/email No resend
SMTP_PASSWORD SMTP password/token No re_xxxx
SMTP_SECURE Use secure SMTP connection (defaults to true if not set) For Email true
SMTP_REJECT_UNAUTHORIZED Reject invalid certificates (defaults to true if not set) For Email false
NEXT_PUBLIC_DISABLE_EMAIL To disable all email features For Email true
NEXT_PUBLIC_BASE_URL Base URL of your installation Yes http://localhost:3000
NEXT_API_BODY_SIZE_LIMIT Maximum API request body size (defaults to 1mb) No 50mb
BETTER_AUTH_ALLOWED_DOMAINS Comma-separated list of allowed domains for OIDC logins For OIDC/Social login example.com,subsidiary.com
BETTER_AUTH_SECRET Auth encryption secret Yes Random 32+ char string
BETTER_AUTH_TRUSTED_ORIGINS Allowed callback origins No http://localhost:3000,http://localhost:3001
GOOGLE_CLIENT_ID Google OAuth client ID For Google login xxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET Google OAuth client secret For Google login xxx
DISCORD_CLIENT_ID Discord OAuth client ID For Discord login xxx
DISCORD_CLIENT_SECRET Discord OAuth client secret For Discord login xxx
GITHUB_CLIENT_ID GitHub OAuth client ID For GitHub login xxx
GITHUB_CLIENT_SECRET GitHub OAuth client secret For GitHub login xxx
OIDC_CLIENT_ID Generic OIDC client ID For OIDC login xxx
OIDC_CLIENT_SECRET Generic OIDC client secret For OIDC login xxx
OIDC_DISCOVERY_URL OIDC discovery URL For OIDC login https://auth.example.com/.well-known/openid-configuration
TRELLO_APP_API_KEY Trello app API key For Trello import xxx
TRELLO_APP_API_SECRET Trello app API secret For Trello import xxx
S3_REGION S3 storage region For file uploads WEUR
S3_ENDPOINT S3 endpoint URL For file uploads https://xxx.r2.cloudflarestorage.com
S3_ACCESS_KEY_ID S3 access key For file uploads (optional with IRSA) xxx
S3_SECRET_ACCESS_KEY S3 secret key For file uploads (optional with IRSA) xxx
S3_FORCE_PATH_STYLE Use path-style URLs for S3 For file uploads true
S3_AVATAR_UPLOAD_LIMIT Maximum avatar file size in bytes For file uploads 2097152 (2MB)
NEXT_PUBLIC_STORAGE_URL Storage service URL For file uploads https://storage.kanbn.com
NEXT_PUBLIC_STORAGE_DOMAIN Storage domain name For file uploads kanbn.com
NEXT_PUBLIC_USE_VIRTUAL_HOSTED_URLS Use virtual-hosted style URLs (bucket.domain.com) For file uploads (optional) true
NEXT_PUBLIC_AVATAR_BUCKET_NAME S3 bucket name for avatars For file uploads avatars
NEXT_PUBLIC_ATTACHMENTS_BUCKET_NAME S3 bucket name for attachments For file uploads attachments
NEXT_PUBLIC_ALLOW_CREDENTIALS Allow email & password login For authentication true
NEXT_PUBLIC_DISABLE_SIGN_UP Disable sign up For authentication false
NEXT_PUBLIC_WHITE_LABEL_HIDE_POWERED_BY Hide “Powered by kan.bn” on public boards (self-host) For white labelling true
KAN_ADMIN_API_KEY Admin API key for stats and admin endpoints For admin/monitoring your-secret-admin-key

See .env.example for a complete list of supported environment variables.

Contributing 🤝

We welcome contributions! Please read our contribution guidelines before submitting a pull request.

Contributors 👥

License 📝

Kan is licensed under the AGPLv3 license.

Contact 📧

For support or to get in touch, please email henry@kan.bn or join our Discord server.

Languages
TypeScript 97.8%
MDX 0.9%
JavaScript 0.6%
PLpgSQL 0.4%
Dockerfile 0.2%