All checks were successful
CI / build-test (push) Successful in 1m28s
- Protect `/webhook` endpoint using the `Authorization` header - Update `README.md` with setup instructions and examples for authentication - Warn when `WEBHOOK_SECRET` is not configured - Add tests for valid, missing, and invalid token scenarios - Update `docker-compose.yml` to support `WEBHOOK_SECRET` configuration
3.4 KiB
3.4 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Commands
# Run all tests with coverage
go test -v -coverprofile=coverage.out -coverpkg=./... ./...
# Run a single test
go test -v -run TestWebhookHandler ./pkg/diunwebhook/
# Run the app locally
go run ./cmd/diunwebhook/
# Build Docker image
docker build -t diun-webhook-dashboard .
# Run with Docker Compose
docker compose up -d
# Frontend (from frontend/ directory)
bun install # install deps
bun run dev # dev server on :5173 (proxies /api and /webhook to :8080)
bun run build # production build → frontend/dist/
CI warns (but does not fail) when coverage drops below 80%.
Architecture
The app is a Go HTTP server that receives DIUN webhook events and exposes them via a JSON API and a React SPA dashboard. Events are persisted to a SQLite database (diun.db) using modernc.org/sqlite (pure Go, no CGO).
Package layout:
pkg/diunwebhook/— core library:DiunEvent,UpdateEntry, andTagstructs; SQLite-backed storage (guarded bysync.Mutex); HTTP handlers (WebhookHandler,UpdatesHandler,DismissHandler,TagsHandler,TagByIDHandler,TagAssignmentHandler)cmd/diunwebhook/main.go— callsInitDB(), wires handlers ontonet/http's default mux, serves./frontend/distat/, listens on:8080(overridable viaPORTenv var), graceful shutdownpkg/diunwebhook/diunwebhook_test.go— external test package (package diunwebhook_test); useshttptestfor handler testspkg/diunwebhook/export_test.go— test-only exportsfrontend/— React SPA (Bun + Vite + React 19 + Tailwind CSS + shadcn/ui + dnd-kit)
Database schema (SQLite):
updates— one row per image (PRIMARY KEYimage), stores full event fields plusreceived_atandacknowledged_attags— user-defined tag/group names (idINTEGER PK,nameUNIQUE)tag_assignments— mapsimage→tag_id(FK with CASCADE delete)
API routes:
POST /webhook— accept a DIUN eventGET /api/updates— return all events (with tag and acknowledged state)PATCH /api/updates/{image}— mark an event as acknowledged (dismiss)GET /api/tags— list all tagsPOST /api/tags— create a tagDELETE /api/tags/{id}— delete a tag (cascades to assignments)PUT /api/tag-assignments— assign an image to a tagDELETE /api/tag-assignments— unassign an image from its tag
Environment variables:
PORT— listen port (default8080)WEBHOOK_SECRET— when set, everyPOST /webhookmust include a matchingAuthorizationheader; when unset, the webhook is open (a warning is logged at startup)
Key data flow:
- DIUN POSTs JSON to
/webhook→WebhookHandlerdecodes intoDiunEvent→ upserted intoupdatestable (latest event per image wins, resets acknowledged state) - React SPA polls
GET /api/updatesevery 5 s →UpdatesHandlerreturns map ofUpdateEntry(includes event, received time, acknowledged flag, and optional tag) - User can dismiss updates via
PATCH /api/updates/{image}and organize images into tag groups via the tag/assignment endpoints
Test helpers exposed from the library package (not part of the public API, only for tests): GetUpdatesMap(), UpdatesReset(), UpdateEvent(), ResetTags().