# deckgl_tracksolid Public dashboard that animates a day's vehicle trips from `tracksolid_db`, colored and filtered by `cost_centre`. Stack: deck.gl `TripsLayer` over Mapbox GL, fed by PostgREST in front of TimescaleDB. ``` React + deck.gl SPA → PostgREST → tracksolid_db (Timescale) (web/) (compose/) public.trips_viz_v1 public.trips_for_day(date, text[]) public.trips_for_range(date, date, text[]) public.list_cost_centres() ``` The full design lives in `trips_deckgl_tracksolid.md` — read that first. ## Layout | Path | Purpose | | --- | --- | | `db/migrations/` | Five SQL files. Idempotent. Apply in order. Add only `public.*` objects. | | `compose/` | `docker-compose.yaml` for the PostgREST API + the built web container. | | `web/` | Vite + React + TS SPA. Renders `TripsLayer` + `PathLayer` over Mapbox. | | `trips_deckgl_tracksolid.md` | Design doc / plan. | ## First-time setup 1. **Apply migrations** to `tracksolid_db`: ```bash psql "$ADMIN_DSN" \ -f db/migrations/001_viz_anon_role.sql \ -f db/migrations/002_trips_viz_view.sql \ -f db/migrations/003_trips_for_day_rpc.sql \ -f db/migrations/004_trips_for_range_rpc.sql \ -f db/migrations/005_list_cost_centres_rpc.sql ``` 2. **Set the `viz_anon` login password** out of band (do not commit): ```sql ALTER ROLE viz_anon LOGIN PASSWORD ''; ``` 3. **Configure env**: ```bash cp compose/.env.example compose/.env # fill in VIZ_DATA_PASSWORD, VITE_MAPBOX_TOKEN, … cp web/.env.example web/.env.local # fill in VITE_API_URL (e.g. http://localhost:3000) and VITE_MAPBOX_TOKEN ``` ## Local development ```bash # 1. Start PostgREST against the staging DB cd compose && docker compose up postgrest # 2. Run the web app against it cd web && pnpm install && pnpm dev # → http://localhost:5173 ``` The web app reads `VITE_API_URL` (default `http://localhost:3000`) and `VITE_MAPBOX_TOKEN`. Without a Mapbox token the deck.gl layers still render on a black background — fine for testing data, ugly for demos. ## Production deployment (Coolify) Two services live alongside the existing Dekart stack: - `postgrest` — read-only API at `api.trips.rahamafresh.com`. CORS allow-listed to the web FQDN. - `trips_web` — Caddy serving the built SPA at `trips.rahamafresh.com`. Both inherit env vars from the Coolify resource panel — see `compose/.env.example` for the canonical list. ## Security boundary The `viz_anon` role has **no grants on `tracksolid.*`** — verified in setup. It can only call the four RPCs in `public`, which run `SECURITY DEFINER` and only expose the columns listed in `trips_viz_v1`. Direct queries against any `tracksolid` table return `permission denied for schema tracksolid`. If the public dashboard ever needs to be locked down, add Traefik basic-auth in Coolify (same pattern Dekart uses) — no app-level changes needed.