From e15f00e98c2927ddd26b8496df1c2ffe8f1e7dd9 Mon Sep 17 00:00:00 2001 From: Per Magnus Petersen Date: Thu, 10 Sep 2026 13:07:22 +0200 Subject: [PATCH] Update doc --- .env.example | 16 +++++++++ CLAUDE.md | 55 ++++++++++++++++++----------- Makefile | 26 +++++++++----- README.md | 98 +++++++++++++++++++++++++++++++++++----------------- 4 files changed, 135 insertions(+), 60 deletions(-) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..70e1fb8 --- /dev/null +++ b/.env.example @@ -0,0 +1,16 @@ +# Copy to .env and fill in. .env is gitignored. +# The Makefile does `include .env`, so this file must exist before `make run`. + +# --- Local dev (make run) --- +# btcdata instance the server proxies WebSockets to. The code falls back to +# http://localhost:4100 when unset, but the Makefile needs the file to exist. +BTCDATA_URL=http://localhost:4100 + +# Used by `make run-local` to point at a btcdata on this machine while +# BTCDATA_URL above points elsewhere (e.g. a deployed instance). +BTCDATA_LOCAL_URL=http://localhost:4100 + +# --- Docker (docker compose up -d) --- +# Port btcdata listens on inside btc-network. Must match DOCKER_BTCDATA_PORT +# in ../btcdata/.env. +DOCKER_BTCDATA_PORT=4102 diff --git a/CLAUDE.md b/CLAUDE.md index c31038c..bf44591 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,39 +1,54 @@ # CLAUDE.md -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Overview - -Bitcoin price tracker UI — Python FastAPI web application. Serves an HTML/Tailwind page with live price updates streamed from btcdata (separate service) via WebSocket using HTMX. +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 (connects to btcdata at BTCDATA_URL from `.env`) -- `make run-local` — Start dev server using BTCDATA_LOCAL_URL from `.env` -- `make tailwind` — Watch Tailwind CSS for changes (run in separate terminal) -- `make test` — Run tests with pytest (`tests/` directory) -- `npm run css:build` — One-off Tailwind CSS build +- `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 | -| btcdata | localhost:4100 | localhost:4101 | +| Service | Local dev | Docker | +|----------|----------------|--------------------------------------| +| btcprice | localhost:4000 | localhost:4041 (container port 4040) | +| btcdata | localhost:4100 | localhost:4102 | -Uses external `btc-network` Docker network. btcdata must be running separately (or via its own docker-compose). +`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. ## Architecture -- **Framework:** FastAPI + Uvicorn -- **Templates:** Inline HTML strings in Python + HTMX (WebSocket extension) -- **Styling:** Tailwind CSS v4 — scans `app/**/*.py` for class names (configured in `resources/css/input.css` via `@source`) -- **Data backend:** btcdata (separate service at BTCDATA_URL, default http://localhost:4100) +- **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 -### Data Flow +### 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` diff --git a/Makefile b/Makefile index e7ac9e2..b9e7c10 100644 --- a/Makefile +++ b/Makefile @@ -1,19 +1,29 @@ -include .env +-include .env export -.PHONY: clean run run-docker tailwind test +VENV := .venv +UVICORN := $(VENV)/bin/uvicorn + +.PHONY: clean run run-local tailwind test venv + +venv: $(UVICORN) + +$(UVICORN): requirements.txt + python3 -m venv $(VENV) + $(VENV)/bin/pip install --upgrade pip + $(VENV)/bin/pip install -r requirements.txt clean: rm -rf __pycache__ app/__pycache__ -run: - .venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 4000 --reload +run: venv + $(UVICORN) app.main:app --host 0.0.0.0 --port 4000 --reload -run-local: - BTCDATA_URL=$(BTCDATA_LOCAL_URL) .venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 4000 --reload +run-local: venv + BTCDATA_URL=$(BTCDATA_LOCAL_URL) $(UVICORN) app.main:app --host 0.0.0.0 --port 4000 --reload tailwind: npm run css:watch -test: - .venv/bin/python -m pytest tests/ +test: venv + $(VENV)/bin/python -m pytest tests/ diff --git a/README.md b/README.md index 489e6c5..e754e99 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,66 @@ -# btcprice - -Bitcoin price tracker UI. Serves an HTML page with live price updates streamed from [btcdata](../btcdata) via WebSocket using HTMX. - -## Quick Start - -```bash -# Start btcdata first (provides price data) -cd ../btcdata && make run - -# Local dev -make run # starts on port 4000 -make tailwind # watch Tailwind CSS (separate terminal) - -# Docker (starts both btcprice + btcdata) -docker compose up -d # btcprice on 4041, btcdata on 4101 -``` - -## Environment Variables - -| Variable | Default | Description | -|---|---|---| -| `PORT` | `4000` | HTTP server port | -| `BTCDATA_URL` | `http://192.168.50.42:4100` | btcdata service URL (browser connects via WebSocket) | - -## How It Works - -The server renders an HTML page with HTMX and the WebSocket extension (`hx-ext="ws"`). HTMX connects directly to btcdata's WebSocket endpoint (`BTCDATA_URL/api/price/ws`), which sends HTML fragments. HTMX swaps the received `
` into the DOM automatically. Price changes are color-coded (green up, red down). Reconnection is handled by HTMX with exponential backoff. - -## Tech Stack - -Clojure · Kit · Undertow · Reitit · Hiccup · HTMX · Tailwind CSS v4 +# btcprice + +Bitcoin price tracker UI. Serves an HTML page with live price updates streamed from +[btcdata](../btcdata), which this server proxies over WebSocket and renders as HTML fragments +for HTMX. + +## Quick Start + +```bash +# Start btcdata first (provides price data) +cd ../btcdata && make run + +# Local dev — creates .venv and installs requirements on first run +make run # starts on port 4000 +make tailwind # watch Tailwind CSS (separate terminal) +``` + +Optionally `cp .env.example .env` to override `BTCDATA_URL`; without it the code default +(`http://localhost:4100`) applies. `make run-local` requires a `.env` defining +`BTCDATA_LOCAL_URL`. + +## Docker + +`docker compose up -d` starts **btcprice only** — it does not bring up btcdata. Both compose +files attach to the external `btc-network`, so create it once and start btcdata separately: + +```bash +docker network create btc-network # once +cd ../btcdata && docker compose up -d +cd ../btcprice && docker compose up -d +``` + +| Service | Local dev | Docker | +|----------|----------------|---------------------------------------| +| btcprice | localhost:4000 | localhost:4041 (container port 4040) | +| btcdata | localhost:4100 | localhost:4102 | + +Inside `btc-network`, btcprice reaches btcdata as `http://btcdata:4102`. + +## Environment Variables + +| Variable | Default | Description | +|---|---|---| +| `BTCDATA_URL` | `http://localhost:4100` | btcdata service URL the server proxies to | +| `BTCDATA_LOCAL_URL` | — | Used by `make run-local` instead of `BTCDATA_URL` | +| `DOCKER_BTCDATA_PORT` | `4102` | btcdata's port inside `btc-network` (compose only) | + +The HTTP port is not configurable via environment: `make run` hardcodes 4000 and the Docker +image's `CMD` hardcodes 4040. + +## How It Works + +The browser loads a page with HTMX and the WebSocket extension, which connects back to +**btcprice** at `/ws/price` and `/ws/strike` — the browser never contacts btcdata directly. + +Each of those endpoints opens an upstream WebSocket to btcdata (`/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 to the browser. HTMX swaps the received +`
` / `
` into the DOM. Price changes are +color-coded (green up, red down). Reconnection is handled by HTMX. + +## Tech Stack + +Python · FastAPI · Uvicorn · websockets · HTMX · Tailwind CSS v4 + +The Clojure implementation this replaced is on the `clojure` branch.