130 lines
5.6 KiB
Markdown
130 lines
5.6 KiB
Markdown
# 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`.
|
|
|
|
Run the test suite with `make test`. Test-only packages are kept in `requirements-dev.txt` and are
|
|
not installed in the production image.
|
|
|
|
## 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`.
|
|
|
|
## CI images
|
|
|
|
Pushes to the `main` branch and tags matching `v*` run
|
|
`.gitea/workflows/build.yml`. The workflow tests the application and publishes linux/amd64 images
|
|
to `harbor.pmagnus.cloud/btcprice/btcprice`. Branch builds receive an immutable
|
|
`main-<UTC timestamp>` tag (for Flux selection) and a SHA-qualified companion tag; release tags
|
|
receive the version without `v` and a SHA-qualified companion tag. The image embeds the short Git
|
|
SHA, available from `/api/version`.
|
|
|
|
The workflow requires the repository secrets `HARBOR_ROBOT_USER` and `HARBOR_ROBOT_TOKEN`. It only
|
|
pushes images and intentionally has no Kubernetes credentials.
|
|
|
|
## Production deployment
|
|
|
|
Production is GitOps-managed at **https://btcprice.k8s.pmagnus.cloud**:
|
|
|
|
```text
|
|
git push → Gitea Actions on anvil → Harbor → Flux → k3s → ingress-nginx → Traefik TLS
|
|
```
|
|
|
|
Flux image automation commits the newest approved `main-<UTC timestamp>` image to the separate
|
|
`magnus/deploy` repository. The manifests are in `../deploy/apps/btcprice`, and two replicas run with
|
|
one pod on each k3s node. CI never receives cluster credentials. Do not deploy production with
|
|
Docker Compose or manual `kubectl apply`; rollback by reverting the immutable image change in the
|
|
GitOps repository.
|
|
|
|
btcdata remains an external dependency running on citadel. Kubernetes represents it with a
|
|
selectorless Service and EndpointSlice, so pods use `http://btcdata:4102` without embedding its IP in
|
|
the Deployment.
|
|
|
|
## Legacy deployment (citadel)
|
|
|
|
```bash
|
|
./deploy-citadel.sh
|
|
```
|
|
|
|
Same method as `../elprice/deploy-citadel.sh`: builds the image locally for the target's
|
|
architecture (citadel is x86_64 → `linux/amd64`), copies `docker-compose.citadel.yml` and
|
|
`.env.citadel` to `/home/pmp/apps/btcprice` on the host as `docker-compose.yml` / `.env`,
|
|
pipes the image over SSH with `docker save | docker load`, and runs
|
|
`docker compose up -d --no-build`. No registry involved.
|
|
|
|
The temporary migration fallback listens on **http://192.168.50.195:4041** and proxies to the
|
|
btcdata running on citadel as well. It is retained only for the validation/rollback window; the
|
|
Kubernetes hostname is the production endpoint. Both attach to the external `btc-network` there, so `BTCDATA_URL` is
|
|
`http://btcdata:4102` (set in `.env.citadel`) — the service name inside the network, not a LAN
|
|
address. The deploy script creates `btc-network` on the host if it does not exist yet.
|
|
|
|
Overridable: `BTCPRICE_DEPLOY_HOST`, `BTCPRICE_DEPLOY_DIR`, `BTCPRICE_IMAGE_NAME`,
|
|
`BTCPRICE_PLATFORM`, `BTCPRICE_NETWORK`.
|
|
|
|
## Environment Variables
|
|
|
|
| Variable | Default | Description |
|
|
|---|---|---|
|
|
| `BTCDATA_URL` | `http://localhost:4100` | btcdata service URL the server proxies to |
|
|
| `APP_VERSION` | `development` | Build identifier returned by `/api/version` |
|
|
| `BTCDATA_LOCAL_URL` | — | Used by `make run-local` instead of `BTCDATA_URL` |
|
|
| `DOCKER_BTCDATA_PORT` | `4102` | btcdata's port inside `btc-network` (local compose only) |
|
|
| `BTCPRICE_PORT` | `4041` | Host port in the citadel deployment (`.env.citadel`) |
|
|
|
|
The HTTP port is not configurable via environment: `make run` hardcodes 4000 and the Docker
|
|
image's `CMD` hardcodes 4040.
|
|
|
|
## Health endpoints
|
|
|
|
- `/api/health` is a process-only liveness check and remains healthy during a btcdata outage.
|
|
- `/api/ready` checks `${BTCDATA_URL}/api/health` with a three-second timeout and returns 503 when
|
|
btcdata is unavailable.
|
|
- `/api/version` returns the build SHA embedded by CI.
|
|
|
|
## 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
|
|
`<div id="price-display">` / `<div id="strike-display">` 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.
|