From 0dd6eba5893c3afa3189b9b0ca8c911cebe91e71 Mon Sep 17 00:00:00 2001 From: owain Date: Mon, 8 Jun 2026 13:19:55 +0100 Subject: [PATCH] docs: refresh CLAUDE.md (beat service, TanStack Query, env vars, no tests) Co-Authored-By: Claude Opus 4.8 --- CLAUDE.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index a53c71d..baa7048 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -29,6 +29,8 @@ Everything runs in Docker Compose. There is no way to run individual services wi The app is served on port 80 by nginx, which proxies `/api/*` to the backend (port 8000) and serves the React SPA for everything else. +There are no automated tests. Verification is done by running the app and observing behaviour. + ## Building and deploying `docker-compose.yml` — build from source (dev/CI). @@ -56,6 +58,7 @@ docker compose -f docker-compose.deploy.yml up -d | `redis` | Celery broker + result backend | | `backend` | FastAPI (async) — uvicorn, single worker | | `worker` | Celery worker — synchronous SQLAlchemy (asyncio incompatible with prefork) | +| `beat` | Celery Beat scheduler — runs `sync_all_garmin_connect` every 30 minutes | | `frontend` | React SPA built by Vite at container build time | | `nginx` | Reverse proxy, serves the SPA | @@ -69,7 +72,7 @@ docker compose -f docker-compose.deploy.yml up -d - `services/wellness_parser.py` — parses Garmin wellness FIT files (metrics, sleep, HRV, SPO2, etc.) - `services/route_matcher.py` — bounding-box pre-filter + DTW (Dynamic Time Warping) for GPS track similarity - `services/garmin_connect_sync.py` — Garmin Connect API integration; `authenticate_garmin()` tries stored OAuth token first, falls back to email/password; Garmin credentials stored Fernet-encrypted using `SECRET_KEY` as the key -- `workers/tasks.py` — Celery tasks: `process_activity_file`, `parse_wellness_fit`, `detect_route`, `compute_personal_records`, `process_garmin_health_zip` +- `workers/tasks.py` — Celery tasks: `process_activity_file`, `parse_wellness_fit`, `detect_route`, `compute_personal_records`, `process_garmin_health_zip`, `sync_all_garmin_connect` (beat-scheduled) ### Key design decisions @@ -86,6 +89,7 @@ docker compose -f docker-compose.deploy.yml up -d - `App.jsx` — React Router v6, `RequireAuth` wrapper, all routes defined here - `hooks/useAuth.js` — Zustand store for auth state, reads JWT from `localStorage`, handles PocketID token-in-URL flow - `utils/api.js` — Axios instance with JWT interceptor and 401→redirect handler +- TanStack Query (`@tanstack/react-query`) handles all server-state fetching and caching; Zustand is used only for auth state - `utils/format.js` — shared formatting helpers: `formatDuration`, `formatPace`, `formatDistance`, `formatCadence`, `hrZoneColor`, `sportIcon`, `sportColor`, etc. - `pages/` — one file per route; includes `SegmentsPage` for route segment management - `components/activity/` — `ActivityMap` (Leaflet), `MetricTimeline` (Recharts), `HRZoneBar`, `LapTable` @@ -100,9 +104,14 @@ Required in `.env` (or passed to Docker Compose): | Variable | Purpose | |----------|---------| | `DATABASE_URL` | Full async DB URL (`postgresql+asyncpg://...`) | -| `SECRET_KEY` | JWT signing key — generate with `openssl rand -hex 32` | +| `SECRET_KEY` | JWT signing key — generate with `openssl rand -hex 32`; also used as Fernet key for Garmin credentials | +| `ADMIN_USERNAME` | Admin account username (default: `admin`) | | `ADMIN_PASSWORD` | Seeds the admin user on first start | | `REDIS_URL` | Celery broker | +| `DB_USER` / `DB_PASSWORD` | Postgres credentials (compose-level; default: `milevault`) | +| `REDIS_PASSWORD` | Redis auth (compose-level; default: `milevault`) | +| `HTTP_PORT` | Host port for nginx (default: `80`) | +| `FILE_STORE_PATH` | Where uploaded FIT files are stored (default: `/data/files`) | | `BASE_URL` | Used for PocketID OAuth callback redirect URI | | `VITE_MAPBOX_TOKEN` | Optional — enables satellite tile layer | | `POCKETID_ISSUER` / `POCKETID_CLIENT_ID` / `POCKETID_CLIENT_SECRET` | Optional OIDC |