wip: dashboard wiring (real backend) + Docker compose for production

This commit is contained in:
Nora
2026-06-22 14:07:31 -06:00
parent 4a382c0b16
commit 0677e4fd65
18 changed files with 1879 additions and 155 deletions
+95
View File
@@ -752,6 +752,101 @@ the one-time setup recipe.
`mft.gainwelltechnologies.com:/CO XIX/PROD/coxix_prod_11525703/FromHPE`.
SFTP credentials are fetched from the macOS Keychain at call time.
## Docker
A two-service `docker-compose.yml` is provided for operators who want
a single `docker compose up` instead of running the backend and
frontend in two terminals. Both services are configured to restart
automatically on crash, and the SQLite database lives on a named
volume that survives `docker compose down` (only `down -v` wipes it).
```bash
docker compose up -d --build # build + start both services in the background
docker compose ps # confirm both containers are Up (healthy)
docker compose logs -f # tail both logs
open http://127.0.0.1:8081 # the SPA, served by nginx
```
### What's where
| Service | Image | Published port | Healthcheck | Persistent state |
| ---------- | ---------------------- | ----------------------------- | ---------------------- | ------------------------- |
| `frontend` | `cyclone-frontend:local` | `0.0.0.0:8081 → 80` (LAN-reachable by default; tighten via `CYCLONE_BIND_ADDRESS=127.0.0.1`) | `GET /healthz` | none |
| `backend` | `cyclone-backend:local` | not published (see note) | `GET /api/health` | `cyclone-data` named volume at `/data` |
The frontend's nginx reverse-proxies `/api/*` to the backend over the
compose network, so the SPA talks to `http://same-origin/api/...` and
the Vite dev-server config (`VITE_API_BASE_URL=`) doesn't apply — the
build is invoked with an empty `VITE_API_BASE_URL` and the API calls
are relative.
### Persistence
The backend writes its SQLite file to `/data/cyclone.db` inside the
container, which is backed by the named volume `cyclone-data`. Killing
and restarting the container preserves the DB; rebooting the host
preserves the DB; only `docker compose down -v` removes it. The
container's `HEALTHCHECK` and `restart: unless-stopped` policy mean a
crash is recovered within ~15 seconds (next healthcheck interval)
without operator intervention.
To back up the live database:
```bash
docker compose exec backend sqlite3 /data/cyclone.db ".backup /data/backup.db"
docker cp cyclone-backend:/data/backup.db ./cyclone-backup-$(date +%F).db
```
(SQLite's online backup API — safe to run while the backend is
serving traffic.)
### Crashing the backend on purpose
To confirm the auto-restart wiring:
```bash
docker compose kill -s SIGKILL backend # hard-kill the backend
docker compose ps # backend should restart within seconds
docker compose logs --tail=20 backend # see the uvicorn startup banner again
```
Data is unaffected: the volume survives `kill` and the new process
re-opens the same DB file.
### Talking to the backend directly
The backend port is **not** published by default (everything goes
through the frontend's nginx). To expose it for `curl` debugging,
uncomment the `ports:` block under `backend:` in `docker-compose.yml`
and restart:
```bash
curl http://127.0.0.1:8000/api/health # {"status":"ok","version":"..."}
```
If 8081 clashes with something else on the host, run with a different
published port:
```bash
CYCLONE_WEB_PORT=9000 docker compose up -d
open http://127.0.0.1:9000
```
To reach the UI from another machine on your LAN, open
`http://<host-lan-ip>:8081` — the default bind is `0.0.0.0`. To
re-tighten to loopback-only (matching the standalone install's
local-only posture):
```bash
CYCLONE_BIND_ADDRESS=127.0.0.1 docker compose up -d
```
### Payer config
`config/payers.yaml` is copied into the backend image at build time.
Edit the YAML, rebuild, and either bounce the container or `POST
/api/admin/reload-config` to pick up changes without a rebuild.
## License
No license file yet; this is internal-use software. Add a `LICENSE` file