docs: update AGENTS.md with current commands and features
- Add web-deps, nidusctl, and migrate commands to build section - Document ICS/webcal subscriptions and Birthdays calendar - Note Migrate() is idempotent - Add additional database tables - Include Docker/CI and database migration details
This commit is contained in:
@@ -9,16 +9,18 @@ collections. A small server-rendered web UI (templ + Tailwind + htmx) at
|
|||||||
## Build, test, lint
|
## Build, test, lint
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make build # go build -o bin/davserver ./cmd/server
|
make build # go build -o bin/davserver ./cmd/server + ./bin/nidusctl
|
||||||
make run # build + ./bin/davserver -config config.yaml
|
make run # build + ./bin/davserver -config config.yaml
|
||||||
make test # go test ./... -v -race
|
make test # go test ./... -v -race
|
||||||
make lint # golangci-lint run ./...
|
make lint # golangci-lint run ./... (no config file; uses defaults)
|
||||||
make tidy # go mod tidy
|
make tidy # go mod tidy
|
||||||
go test ./internal/store/ -run TestStoreRoundTrip -v # single test
|
make web-deps # install Tailwind CLI + TypeScript (needed once, or after web/package.json changes)
|
||||||
make templ-generate # regenerate *_templ.go after editing internal/web/templates/*.templ
|
make templ-generate # regenerate *_templ.go after editing internal/web/templates/*.templ
|
||||||
make web-css # templ-generate + rebuild web/static/app.css (needs `make web-deps` once, Node.js/npm)
|
make web-css # templ-generate + rebuild web/static/app.css
|
||||||
make web-ts # compile web/ts/*.ts to web/static/*.js
|
make web-ts # compile web/ts/*.ts to web/static/*.js
|
||||||
make web-assets # web-css + web-ts (everything under web/static/)
|
make web-assets # web-css + web-ts (everything under web/static/)
|
||||||
|
make nidusctl # build + run nidusctl with ARGS (e.g. `make nidusctl ARGS="user list"`)
|
||||||
|
go run ./tools/migrate -config config.yaml # restructure data directory
|
||||||
```
|
```
|
||||||
|
|
||||||
Test files: `internal/store/store_test.go`, `internal/webdav/handler_test.go`,
|
Test files: `internal/store/store_test.go`, `internal/webdav/handler_test.go`,
|
||||||
@@ -51,14 +53,15 @@ Test files: `internal/store/store_test.go`, `internal/webdav/handler_test.go`,
|
|||||||
if it's nil. `auth.NewContext(ctx, p)` is the test-only inverse, used to
|
if it's nil. `auth.NewContext(ctx, p)` is the test-only inverse, used to
|
||||||
build authenticated contexts without a real Basic Auth handshake.
|
build authenticated contexts without a real Basic Auth handshake.
|
||||||
- `internal/store` — the single source of truth for all persisted data.
|
- `internal/store` — the single source of truth for all persisted data.
|
||||||
A thin filesystem KV abstraction: `<data_dir>/<user>/<collection>/<objectID>`.
|
A thin filesystem KV abstraction: `<data_dir>/<user>/[calendars|addressbooks|files]/<name>`.
|
||||||
All collection/object names pass through `sanitize()` (via
|
All collection/object names pass through `sanitize()` (via
|
||||||
`filepath.Base` + strip `..`) to prevent path traversal — preserve this
|
`filepath.Base` + strip `..`) to prevent path traversal — preserve this
|
||||||
when adding new store methods. Writes use temp-file + rename for atomicity
|
when adding new store methods. Writes use temp-file + rename for atomicity
|
||||||
(`PutObject`). Locking is sharded per-user (`lockFor(user)`, a
|
(`PutObject`). Locking is sharded per-user (`lockFor(user)`, a
|
||||||
`map[string]*sync.RWMutex` guarded by its own mutex) rather than one
|
`map[string]*sync.RWMutex` guarded by its own mutex) rather than one
|
||||||
global lock, so different users' requests don't serialize against each
|
global lock, so different users' requests don't serialize against each
|
||||||
other.
|
other. `Migrate()` restructures data from the old format (`cal-<name>`,
|
||||||
|
`card-<name>`) to the new unified layout and is idempotent.
|
||||||
- `internal/caldav` and `internal/carddav` — implement the
|
- `internal/caldav` and `internal/carddav` — implement the
|
||||||
`caldav.Backend`/`carddav.Backend` interfaces from `github.com/emersion/go-webdav`
|
`caldav.Backend`/`carddav.Backend` interfaces from `github.com/emersion/go-webdav`
|
||||||
on top of `store.Store`. Calendars are stored as collections prefixed
|
on top of `store.Store`. Calendars are stored as collections prefixed
|
||||||
@@ -100,6 +103,12 @@ Test files: `internal/store/store_test.go`, `internal/webdav/handler_test.go`,
|
|||||||
shared data is never copied — it's read/written directly under the
|
shared data is never copied — it's read/written directly under the
|
||||||
owner's own `store.Store` namespace, just addressed via the synthetic
|
owner's own `store.Store` namespace, just addressed via the synthetic
|
||||||
name from the grantee's requests.
|
name from the grantee's requests.
|
||||||
|
**ICSSubscriptions**: read-only calendars backed by remote ICS/webcal
|
||||||
|
URLs (see `db.CreateICSSubscription`, `internal/db/ics.go`) are exposed
|
||||||
|
alongside real calendars under `/cal/home/<name>/` (shared namespace;
|
||||||
|
see `internal/caldav/ics.go`). `Birthdays` is a computed calendar
|
||||||
|
synthesized from contacts' BDAY fields (see `internal/caldav/birthdays.go`,
|
||||||
|
`internal/birthdays/birthdays.go`).
|
||||||
- `internal/db` — a small `database/sql` wrapper around
|
- `internal/db` — a small `database/sql` wrapper around
|
||||||
`modernc.org/sqlite` (pure Go, no CGO) at `<data_dir>/nidus.db`. Only
|
`modernc.org/sqlite` (pure Go, no CGO) at `<data_dir>/nidus.db`. Only
|
||||||
one open connection is used (`SetMaxOpenConns(1)`) since SQLite allows a
|
one open connection is used (`SetMaxOpenConns(1)`) since SQLite allows a
|
||||||
@@ -118,6 +127,9 @@ Test files: `internal/store/store_test.go`, `internal/webdav/handler_test.go`,
|
|||||||
cleans those up in a transaction. `modernc.org/sqlite` has no typed
|
cleans those up in a transaction. `modernc.org/sqlite` has no typed
|
||||||
unique-constraint error, so `isUniqueConstraintErr()` string-matches the
|
unique-constraint error, so `isUniqueConstraintErr()` string-matches the
|
||||||
driver's error message.
|
driver's error message.
|
||||||
|
**Additional tables**: `birthday_calendars` (per-user display color for
|
||||||
|
the computed Birthdays calendar), `ics_subscriptions` (remote ICS/webcal
|
||||||
|
calendar subscriptions), `web_sessions` (server-side session tokens).
|
||||||
- `internal/webdav` — plain-file WebDAV via `golang.org/x/net/webdav`,
|
- `internal/webdav` — plain-file WebDAV via `golang.org/x/net/webdav`,
|
||||||
mounted at the single fixed URL `/files/` for all users (no username in
|
mounted at the single fixed URL `/files/` for all users (no username in
|
||||||
the path either). `NewHandler` caches one `*xwebdav.Handler` per
|
the path either). `NewHandler` caches one `*xwebdav.Handler` per
|
||||||
@@ -128,6 +140,10 @@ Test files: `internal/store/store_test.go`, `internal/webdav/handler_test.go`,
|
|||||||
- `tools/hashpwd` — standalone CLI (`go run ./tools/hashpwd <password>`) to
|
- `tools/hashpwd` — standalone CLI (`go run ./tools/hashpwd <password>`) to
|
||||||
generate bcrypt hashes for ad-hoc testing (no longer needed for normal
|
generate bcrypt hashes for ad-hoc testing (no longer needed for normal
|
||||||
user setup — see `nidusctl user create` below).
|
user setup — see `nidusctl user create` below).
|
||||||
|
- `tools/migrate` — restructures data directory from old format (`cal-<name>`,
|
||||||
|
`card-<name>`) to unified layout (`calendars/`, `addressbooks/`,
|
||||||
|
`files/`). Run automatically on server startup; invoke manually with
|
||||||
|
`go run ./tools/migrate -config config.yaml`.
|
||||||
- `tools/nidusctl` — standalone admin CLI (`go run ./tools/nidusctl
|
- `tools/nidusctl` — standalone admin CLI (`go run ./tools/nidusctl
|
||||||
-config config.yaml <user|calendar|addressbook> ...`) — the only way to
|
-config config.yaml <user|calendar|addressbook> ...`) — the only way to
|
||||||
create/delete users, calendars, and address books, plus manage sharing
|
create/delete users, calendars, and address books, plus manage sharing
|
||||||
@@ -190,6 +206,14 @@ Test files: `internal/store/store_test.go`, `internal/webdav/handler_test.go`,
|
|||||||
no runtime dependency on Node.js or the `web/` directory being present —
|
no runtime dependency on Node.js or the `web/` directory being present —
|
||||||
Node/npm are only needed when actually changing templates/styles/TS
|
Node/npm are only needed when actually changing templates/styles/TS
|
||||||
(`make web-assets` rebuilds everything under `web/static/`).
|
(`make web-assets` rebuilds everything under `web/static/`).
|
||||||
|
**Web UI features**: dashboard (calendars/address books/ICS subscriptions
|
||||||
|
management), login (cookie-based sessions), share management (create/update/
|
||||||
|
revoke share grants via htmx), files browser (inline preview, upload, download),
|
||||||
|
contacts manager (vCard import/export, edit fields), calendar view (month/week,
|
||||||
|
per-calendar colors), account settings (name/email/password). The
|
||||||
|
synthetic `Birthdays` calendar (computed from contacts' BDAY fields) and
|
||||||
|
ICS/webcal subscriptions are managed via the web UI just like real
|
||||||
|
calendars.
|
||||||
|
|
||||||
## Conventions
|
## Conventions
|
||||||
|
|
||||||
@@ -211,3 +235,5 @@ Test files: `internal/store/store_test.go`, `internal/webdav/handler_test.go`,
|
|||||||
- Logging uses `log/slog` structured fields (e.g. `logger.Warn("...", "user", u, "error", err)`), passed down explicitly to every constructor (`NewBackend`, `NewHandler`, `NewMiddleware`) rather than a global logger.
|
- Logging uses `log/slog` structured fields (e.g. `logger.Warn("...", "user", u, "error", err)`), passed down explicitly to every constructor (`NewBackend`, `NewHandler`, `NewMiddleware`) rather than a global logger.
|
||||||
- Config module path is `github.com/yourusername/caldav-server` (go.mod name
|
- Config module path is `github.com/yourusername/caldav-server` (go.mod name
|
||||||
predates the `nidus` repo rename) — import paths still use this, not `nidus`.
|
predates the `nidus` repo rename) — import paths still use this, not `nidus`.
|
||||||
|
- **Docker/CI**: Docker image pushed to `git.arnef.de/arnef/nidus` on tags (`v*`) or releases (`.github/workflows/docker-release.yml`), multi-arch (`linux/amd64`, `linux/arm64`). Use `docker-compose` for local dev with healthcheck on `/healthz`.
|
||||||
|
- **Database schema** lives in `internal/db/db.go` `migrate()` — all tables are created with `CREATE TABLE IF NOT EXISTS` and schema changes are applied via `ALTER TABLE` in `migrateAddColumns()`. No external migration framework.
|
||||||
|
|||||||
Reference in New Issue
Block a user