diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..5ff908e --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,135 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +**Pamietnik** (Codename: RALPH) is a life/travel journal consisting of three components: +- `app/` — Android app (Kotlin + Jetpack Compose) +- `backend/` — Go REST API + server-side rendered Web UI +- `README.md` — single source of truth for architecture, requirements, and backlog + +## Backend (`backend/`) + +**Stack:** Go, chi router, pgx/v5 (PostgreSQL), golang-migrate, Argon2id + +**Run locally:** +```bash +cd backend +docker-compose up -d # starts PostgreSQL + API on :8080 +./start.sh # alternative start script +``` + +**Run only PostgreSQL and server manually:** +```bash +docker-compose up -d postgres +go run ./cmd/migrate # run DB migrations +go run ./cmd/server # starts API on :8080 (default) +``` + +**Env vars** (with defaults): +- `DATABASE_URL` — `postgres://ralph:ralph@localhost:5432/ralph?sslmode=disable` +- `LISTEN_ADDR` — `:8080` +- `UPLOAD_DIR` — `./uploads` + +**Tests:** +```bash +go test ./... # all tests +go test ./internal/api/... # handler tests only +``` + +**Create a user:** +```bash +go run ./cmd/createuser +``` + +**DB migrations** live in `migrations/` and are applied via `cmd/migrate`. + +### Backend Architecture + +``` +cmd/ + server/ entry point: wires stores → router → http.Server + migrate/ runs golang-migrate against DATABASE_URL + createuser/ CLI to create users with Argon2id-hashed passwords +internal/ + domain/ shared model types (Trackpoint, Stop, Suggestion, …) + db/ pgx store implementations (trackpoints, stops, suggestions, journal) + auth/ session store (Postgres-backed), Argon2id hashing + api/ + router.go chi router wiring + middleware + middleware.go session auth middleware + ingest.go POST /v1/trackpoints, POST /v1/trackpoints:batch + query.go GET /v1/days, /v1/trackpoints, /v1/stops, /v1/suggestions + webui.go server-side rendered web UI (login, /days, /days/{date}) + journal.go journal entry endpoints + response.go shared response helpers +``` + +Key invariants: +- Idempotency via unique `(device_id, event_id)` — duplicate inserts return 200 OK +- Geocoding is event-driven (per Stop only), never bulk/periodic +- Sessions are stored in PostgreSQL (invalidatable on logout) +- OpenAPI spec must be kept current (`openapi.yaml`); changes only via PR + CI validation + +## Android App (`app/`) + +**Stack:** Kotlin, Jetpack Compose, Room (SQLite), WorkManager, MapLibre + +**Build:** +```bash +cd app +./gradlew assembleDebug +./gradlew installDebug # install on connected device/emulator +``` + +**Tests:** +```bash +./gradlew test # JVM unit tests +./gradlew connectedAndroidTest # instrumentation tests (device required) +./gradlew :app:testDebugUnitTest # single module unit tests +``` + +### Android Architecture + +``` +de.jacek.reisejournal/ + domain/ Trackpoint domain model + data/ Room entities, DAOs, local DB + service/ Background location foreground service + worker/ WorkManager upload worker + ui/ + home/ HomeScreen (Compose) + HomeViewModel + navigation/ NavGraph + theme/ Compose theme +``` + +Key invariants: +- Offline-first: every point persisted to Room before any upload attempt +- All points carry a client-generated `event_id` (UUID) for server-side idempotency +- `source` field distinguishes `"gps"` vs `"manual"` trackpoints +- Upload worker uses `NetworkConnected` constraint + retry/backoff +- No DB or network access directly in Composables — always via ViewModel + +## Project Decisions (from README) + +| Decision | Choice | +|----------|--------| +| Android UI | Jetpack Compose | +| Local DB | Room / SQLite | +| Backend DB | PostgreSQL | +| Maps | MapLibre + OpenStreetMap tiles (configurable source) | +| Geocoding | Nominatim (OSM), cached, provider-swappable via config | +| Auth (Web UI) | Session Cookie (Postgres-backed) | +| Password hashing | Argon2id | +| API spec | OpenAPI 3.1 (`openapi.yaml`) | + +## Open Decisions (TBD) + +- `timestamp` format: `epochMillis` vs RFC3339 +- Android upload auth: X-API-Key vs Bearer/JWT +- HTTP payload: JSON vs Protobuf +- Batch limits (max items / max bytes) +- Retention policy for trackpoints +- Stop detection parameters (min duration, radius) +- Geocoding provider: Nominatim public vs self-hosted vs alternative