Files
btcprice/CLAUDE.md
T
magnusandClaude Opus 5 2af8b6f62d Add citadel deployment
Port of ../elprice/deploy-citadel.sh: build the image locally for the
remote's architecture, ship it over SSH with docker save | docker load,
and run docker compose up -d --no-build in /home/pmp/apps/btcprice.

The deployment compose is standalone rather than joining btc-network,
because citadel runs no btcdata — BTCDATA_URL points at the instance
already running on omarchy (http://192.168.50.42:4102).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BdMTk61JgUdNH86ttMi4ce
2026-09-10 14:24:38 +02:00

75 lines
3.6 KiB
Markdown

# CLAUDE.md
Bitcoin price tracker UI — Python FastAPI web application. Serves the HTML/Tailwind UI and
proxies live price data from btcdata over WebSocket.
## Build & Development Commands
- `make run` — Start dev server on port 4000; creates `.venv` and installs `requirements.txt`
on first run (the `venv` target, keyed off `requirements.txt`)
- `cp .env.example .env` — optional; `.env` is `-include`d, so a missing one is not fatal
- `make run-local` — Same, but with `BTCDATA_URL=$BTCDATA_LOCAL_URL`
- `make tailwind` — Watch Tailwind CSS for changes
- `make test` — Runs `pytest tests/`, but there is no `tests/` directory yet and pytest is not
in `requirements.txt`
- `make venv` — Bootstrap the virtualenv without starting the server
## Docker
Docker runs alongside local dev on different ports:
| Service | Local dev | Docker |
|----------|----------------|--------------------------------------|
| btcprice | localhost:4000 | localhost:4041 (container port 4040) |
| btcdata | localhost:4100 | localhost:4102 |
`docker compose up -d` here starts **btcprice only**. Both projects attach to the external
`btc-network` (`docker network create btc-network` once), and btcdata must be started from
`../btcdata`. Inside the network btcprice reaches it at `http://btcdata:${DOCKER_BTCDATA_PORT:-4102}`.
Note: `Dockerfile` bakes in a stale `ENV BTCDATA_URL=http://btcdata:4101`; compose always
overrides it, so it only matters if the image is run outside compose. `ENV PORT` is likewise
unused — the port in `CMD` is hardcoded.
## Deployment
`./deploy-citadel.sh` — build locally for the remote's arch, `docker save | ssh docker load`,
`docker compose up -d --no-build` in `/home/pmp/apps/btcprice` on citadel (192.168.50.195).
Mirrors `../elprice/deploy-citadel.sh`; no registry.
- `docker-compose.citadel.yml` → remote `docker-compose.yml`; `.env.citadel` → remote `.env`.
- Standalone (no `btc-network`): citadel has no btcdata, so `BTCDATA_URL` points at the btcdata
on omarchy, `http://192.168.50.42:4102`.
- Live at http://192.168.50.195:4041. A separate btcprice also still runs on omarchy at :4041.
## Architecture
- **Framework:** FastAPI
- **Server:** Uvicorn
- **Templates:** Inline HTML strings + HTMX (WebSocket extension)
- **Styling:** Tailwind CSS v4
- **Data backend:** btcdata (separate service at `BTCDATA_URL`, default `http://localhost:4100`)
- **No database** — all data comes from btcdata
### btcdata connection
The browser connects only to btcprice: `ws-connect="/ws/price"` and `ws-connect="/ws/strike"`.
Each endpoint runs `_ws_proxy`, which opens an upstream WebSocket to btcdata's `/api/price/ws`
and `/api/strike/ws`, converts each JSON message to an HTML fragment (`price_json_to_html`,
`strike_json_to_html`) and forwards it. `BTCDATA_URL` is the single point of contact;
`_btcdata_ws_url` rewrites its scheme to `ws`/`wss`.
## Source Layout
btcprice acts as a WebSocket proxy between the browser and btcdata:
1. Browser connects via HTMX WebSocket to `/ws/price` or `/ws/strike`
2. btcprice proxies to btcdata's `/api/price/ws` or `/api/strike/ws`
3. JSON from btcdata is transformed into HTML fragments (in `price_json_to_html` / `strike_json_to_html`)
4. HTMX swaps the HTML into the DOM by matching element IDs (`price-display`, `strike-display`)
### Key Conventions
- Numbers use Danish locale formatting (dots for thousands, commas for decimals) via `da_fmt()`
- Price changes are color-coded: green (up), red (down), gray (unchanged)
- All HTML is built as string concatenation in Python — no template engine