**docs:** update README.md and .claude/CLAUDE.md with new features, architecture, and API changes
All checks were successful
CI / build-test (push) Successful in 1m3s

- Reflect addition of React SPA, SQLite persistence, and tag management
- Update quick start guide for backend and frontend setup
- Document full API routes and database schema
- Revise project structure for recent refactor
- Improve production and testing notes for clarity
This commit is contained in:
2026-02-25 21:58:22 +01:00
parent 399168e4eb
commit db9f47649d
2 changed files with 80 additions and 31 deletions

View File

@@ -9,7 +9,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
go test -v -coverprofile=coverage.out -coverpkg=./... ./... go test -v -coverprofile=coverage.out -coverpkg=./... ./...
# Run a single test # Run a single test
go test -v -run TestWebhookHandler ./test/diunwebhook/ go test -v -run TestWebhookHandler ./pkg/diunwebhook/
# Run the app locally # Run the app locally
go run ./cmd/diunwebhook/ go run ./cmd/diunwebhook/
@@ -19,22 +19,44 @@ docker build -t diun-webhook-dashboard .
# Run with Docker Compose # Run with Docker Compose
docker compose up -d 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%. CI warns (but does not fail) when coverage drops below 80%.
## Architecture ## Architecture
The app is a minimal Go HTTP server that receives [DIUN](https://crazymax.dev/diun/) webhook events and exposes them via a JSON API and static HTML dashboard. All states are in-memory (no persistence). The app is a Go HTTP server that receives [DIUN](https://crazymax.dev/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:** **Package layout:**
- `pkg/diunwebhook/` — core library: `DiunEvent` struct, in-memory `updates` map (guarded by `sync.Mutex`), and HTTP handlers (`WebhookHandler`, `UpdatesHandler`) - `pkg/diunwebhook/` — core library: `DiunEvent`, `UpdateEntry`, and `Tag` structs; SQLite-backed storage (guarded by `sync.Mutex`); HTTP handlers (`WebhookHandler`, `UpdatesHandler`, `DismissHandler`, `TagsHandler`, `TagByIDHandler`, `TagAssignmentHandler`)
- `cmd/diunwebhook/main.go` — wires the handlers and static file server onto `net/http`'s default mux, listens on `:8080` - `cmd/diunwebhook/main.go` calls `InitDB()`, wires handlers onto `net/http`'s default mux, serves `./frontend/dist` at `/`, listens on `:8080` (overridable via `PORT` env var), graceful shutdown
- `test/diunwebhook/` — external test package (`package diunwebhook_test`) that imports `pkg/diunwebhook`; uses `httptest` for handler tests - `pkg/diunwebhook/diunwebhook_test.go` — external test package (`package diunwebhook_test`); uses `httptest` for handler tests
- `static/` — served verbatim at `/` - `pkg/diunwebhook/export_test.go` — test-only exports
- `frontend/` — React SPA (Bun + Vite + React 19 + Tailwind CSS + shadcn/ui + dnd-kit)
**Database schema (SQLite):**
- `updates` — one row per image (PRIMARY KEY `image`), stores full event fields plus `received_at` and `acknowledged_at`
- `tags` — user-defined tag/group names (`id` INTEGER PK, `name` UNIQUE)
- `tag_assignments` — maps `image``tag_id` (FK with CASCADE delete)
**API routes:**
- `POST /webhook` — accept a DIUN event
- `GET /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 tags
- `POST /api/tags` — create a tag
- `DELETE /api/tags/{id}` — delete a tag (cascades to assignments)
- `PUT /api/tag-assignments` — assign an image to a tag
- `DELETE /api/tag-assignments` — unassign an image from its tag
**Key data flow:** **Key data flow:**
1. DIUN POSTs JSON to `/webhook``WebhookHandler` decodes into `DiunEvent`stored in `updates[event.Image]` (the latest event per image wins) 1. DIUN POSTs JSON to `/webhook``WebhookHandler` decodes into `DiunEvent`upserted into `updates` table (latest event per image wins, resets acknowledged state)
2. Dashboard JS polls `GET /api/updates``UpdatesHandler` returns a full map as JSON 2. React SPA polls `GET /api/updates` every 5 s`UpdatesHandler` returns map of `UpdateEntry` (includes event, received time, acknowledged flag, and optional tag)
3. 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()`. **Test helpers exposed from the library package** (not part of the public API, only for tests): `GetUpdatesMap()`, `UpdatesReset()`, `UpdateEvent()`, `ResetTags()`.

View File

@@ -1,17 +1,23 @@
# DIUN Webhook Dashboard # DIUN Webhook Dashboard
A tiny Go web app that receives [DIUN](https://crazymax.dev/diun/) webhook events and shows the latest image updates in a simple UI. A Go web app that receives [DIUN](https://crazymax.dev/diun/) webhook events and shows image updates in a modern dashboard. Events are persisted to SQLite so they survive restarts.
- Receives DIUN webhooks at `POST /webhook` - Receives DIUN webhooks at `POST /webhook`
- Serves a minimal dashboard at `/` - Serves a React SPA dashboard at `/`
- Exposes a read-only API at `GET /api/updates` - REST API for updates, tags, and acknowledgements
- Stores events in memory (ephemeral) - Persistent storage via SQLite (`diun.db`)
- Tag/group system to organize images
- Dismiss (acknowledge) updates you've reviewed
## Quick start ## Quick start
### Run locally (Go 1.26+) ### Run locally (Go 1.26+)
```bash ```bash
go run . # Build the frontend first
cd frontend && bun install && bun run build && cd ..
# Start the server
go run ./cmd/diunwebhook/
# open http://localhost:8080 # open http://localhost:8080
``` ```
@@ -49,41 +55,62 @@ Expected JSON payload (simplified):
``` ```
## API ## API
- `POST /webhook` — accept a DIUN event JSON body.
- `GET /api/updates` — return the latest events as a JSON object keyed by image name.
- `/` — static HTML dashboard.
Note: data is only kept in-memory and will be reset on restart. | Method | Endpoint | Description |
|--------|----------|-------------|
| `POST` | `/webhook` | Accept a DIUN event JSON body |
| `GET` | `/api/updates` | Return all events (keyed by image) with tag and acknowledged state |
| `PATCH` | `/api/updates/{image}` | Mark an event as acknowledged (dismiss) |
| `GET` | `/api/tags` | List all tags |
| `POST` | `/api/tags` | Create a new tag |
| `DELETE` | `/api/tags/{id}` | Delete a tag (cascades to assignments) |
| `PUT` | `/api/tag-assignments` | Assign an image to a tag |
| `DELETE` | `/api/tag-assignments` | Unassign an image from its tag |
## Project Structure ## Project Structure
- `cmd/diunwebhook/` — main application source and tests ```
- `static/` — static assets cmd/diunwebhook/ — main application entrypoint
- `Dockerfile`, `docker-compose.yml`, `go.mod`, `go.sum` — project config/build files pkg/diunwebhook/ — core library (handlers, DB, models)
frontend/ — React SPA (Bun + Vite + React 19 + Tailwind + shadcn/ui)
.gitea/workflows/ — CI/CD workflows (Gitea Actions)
Dockerfile — 3-stage multi-stage build
docker-compose.yml — single-service compose file
```
## Development ## Development
- Code: `main.go`
- Static assets: `static/`
- Container image: `Dockerfile`
## Production notes **Backend:**
- Behind a reverse proxy, ensure the app is reachable at `/webhook` from DIUN. ```bash
- Persistence is not implemented; hook up a store (e.g., BoltDB/Redis/Postgres) if you need durability. go run ./cmd/diunwebhook/
- Consider adding auth, rate limiting, or a secret/token on the webhook endpoint if exposed publicly. ```
**Frontend (dev server with hot reload):**
```bash
cd frontend
bun install
bun run dev # http://localhost:5173, proxies API to :8080
```
## Testing ## Testing
Run unit tests and check coverage: Run unit tests and check coverage:
```bash ```bash
go test -v -cover go test -v -coverprofile=coverage.out -coverpkg=./... ./...
``` ```
Aim for 8090% coverage. Coverage below 80% will emit a warning in CI but will not fail the pipeline. Aim for 80-90% coverage. Coverage below 80% will emit a warning in CI but will not fail the pipeline.
## CI/CD with Gitea Actions ## CI/CD with Gitea Actions
A sample Gitea Actions workflow is provided in `.gitea/workflows/ci.yml` to automate build, test, and coverage checks. - **CI** (`.gitea/workflows/ci.yml`): Runs on push/PR to `develop`. Checks formatting (`gofmt`), runs `go vet`, tests with coverage, and builds the binary.
- **Release** (`.gitea/workflows/release.yml`): Manual dispatch. Runs tests, bumps version, tags, builds and pushes Docker image to Gitea registry, creates a release with changelog.
## Production notes
- Behind a reverse proxy, ensure the app is reachable at `/webhook` from DIUN.
- Data is persisted to `diun.db` in the working directory. Mount a volume to preserve data across container recreations.
- Consider adding auth, rate limiting, or a secret/token on the webhook endpoint if exposed publicly.
## License ## License
MIT — see `LICENSE`. MIT — see `LICENSE`.