Add web UI: login, dashboard, and share management (templ + Tailwind + htmx)

New internal/web package mounted at /ui/, separate from DAV Basic Auth:

- Cookie-based sessions (opaque random tokens in a new web_sessions
  SQLite table, internal/db/sessions.go), checked against the same
  cfg.Users/bcrypt credentials as DAV Basic Auth.
- Dashboard listing the logged-in user's own calendars/address books,
  who they're shared with, and what's shared with them.
- Share/unshare directly from the dashboard, updated in place via htmx
  partial swaps (POST to create/update, DELETE to revoke). Always
  verifies the resource actually belongs to the logged-in user before
  granting a share.
- Templates written in templ (internal/web/templates/*.templ, generated
  *_templ.go committed), styled with Tailwind CSS v4 (web/input.css,
  compiled to web/static/app.css), with htmx vendored as a static file
  for the dynamic bits. Both are embedded into the binary at build time
  (web/staticassets.go) so the compiled server has no Node.js/web/
  runtime dependency.
- Wired into cmd/server/main.go at /ui/ alongside the existing /cal/,
  /card/, /files/ routes; welcome page links to it.
- Tests: internal/web/server_test.go covers login success/failure, the
  login-required redirect, dashboard rendering, share/unshare including
  the htmx-v2-sends-DELETE-params-as-query-string quirk, and rejecting
  shares of resources the user doesn't own.
- Docs: README (new 'Web UI' section, updated sharing section, project
  layout, dependencies) and copilot-instructions updated accordingly.
  Makefile: new templ-generate/web-deps/web-css targets.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-08-19 07:12:56 +02:00
co-authored by Copilot
parent 58d74a29cd
commit ab3c7f44d5
27 changed files with 2764 additions and 16 deletions
+48 -5
View File
@@ -14,6 +14,8 @@ A self-hosted **CalDAV**, **CardDAV**, and **WebDAV** server written in Go.
- Per-user isolated collections
- **Calendar/address book sharing** — grant other users read or write
access to your calendars/address books
- **Web UI** — a small dashboard (login, manage shares) at `/ui/`, built
with templ + Tailwind + htmx
- Auto-discovery via `/.well-known/caldav` and `/.well-known/carddav`
- Optional **TLS** (or use a reverse proxy)
- Structured logging (text or JSON)
@@ -146,8 +148,8 @@ grantee's own home-set alongside their own calendars — no separate account
or extra client configuration needed.
Sharing grants are stored in a small SQLite database at
`<data_dir>/nidus.db` (not in `config.yaml`) and managed with the
`nidusctl` CLI (there's no web UI yet):
`<data_dir>/nidus.db` (not in `config.yaml`) and can be managed either via
the `nidusctl` CLI or the web UI's dashboard (see below):
```bash
# Give bob write access to alice's "work" calendar
@@ -173,6 +175,35 @@ Read-only shares reject any write (PUT/DELETE) with `403 Forbidden`.
---
## Web UI
A small server-rendered dashboard is served at `/ui/` (separate from the
DAV endpoints, which stay on HTTP Basic Auth):
- **Login** (`/ui/login`) — cookie-based session, stored server-side in
`nidus.db` (`web_sessions` table), independent of DAV Basic Auth.
- **Dashboard** (`/ui/`) — lists your own calendars/address books, who
they're shared with, and any resources other users have shared with you.
- **Share management** — add/remove shares directly from the dashboard
(same effect as `nidusctl`); updates happen in place via
[htmx](https://htmx.org/) without a full page reload.
- **Logout** (`/ui/logout`).
Implementation: [templ](https://templ.guide/) for type-safe Go HTML
templates, [Tailwind CSS v4](https://tailwindcss.com/) for styling, and
htmx for the sprinkles of dynamic behavior (form submission via
POST/DELETE, partial page swaps) — no separate JS build/framework needed.
The compiled CSS and the htmx bundle are embedded into the Go binary
(`web/staticassets.go`), so no Node.js is required at runtime, only when
you change styles or templates during development:
```bash
make web-deps # once, installs the Tailwind CLI (needs Node.js/npm)
make web-css # regenerate templ code + rebuild web/static/app.css
```
---
## TLS / Reverse proxy
### Self-signed certificate (development)
@@ -242,15 +273,19 @@ users:
caldav-server/
├── cmd/server/ # main entrypoint
├── internal/
│ ├── auth/ # HTTP Basic Auth middleware
│ ├── auth/ # HTTP Basic Auth middleware (DAV endpoints)
│ ├── caldav/ # CalDAV backend
│ ├── carddav/ # CardDAV backend
│ ├── config/ # YAML config loader
│ ├── db/ # SQLite store (calendar/address book shares)
│ ├── db/ # SQLite store (shares, web UI sessions)
│ ├── store/ # filesystem storage layer
│ ├── web/ # web UI (cookie sessions, dashboard, share mgmt)
│ │ └── templates/ # templ templates (+ generated *_templ.go)
│ └── webdav/ # WebDAV file handler
├── tools/hashpwd/ # bcrypt password hasher CLI
├── tools/nidusctl/ # sharing-grant admin CLI
├── web/ # front-end assets: Tailwind input/config, static/
│ └── static/ # compiled app.css + htmx.min.js (embedded into the binary)
├── config.example.yaml # sample configuration (copy to config.yaml)
├── Dockerfile
├── docker-compose.yaml
@@ -279,4 +314,12 @@ go test ./... -race
| `golang.org/x/crypto` | bcrypt |
| `golang.org/x/net` | `golang.org/x/net/webdav` |
| `gopkg.in/yaml.v3` | YAML config parsing |
| `modernc.org/sqlite` | Pure-Go SQLite driver (calendar/address book shares) |
| `modernc.org/sqlite` | Pure-Go SQLite driver (shares, web UI sessions) |
| `github.com/a-h/templ` | Type-safe Go HTML templates (web UI) |
Front-end (dev-only, not required at runtime — see [Web UI](#web-ui)):
| Tool | Purpose |
|------|---------|
| Tailwind CSS v4 (`web/package.json`) | Utility-first CSS, compiled to `web/static/app.css` |
| [htmx](https://htmx.org/) (`web/static/htmx.min.js`, vendored) | Partial page updates without a JS framework |