Add a log view, and Docker packaging
The Log button shows the running daemon live: feed scans, downloads, torrents and every HTTP request. It reads a ring buffer filled by a tracing layer rather than tailing a file, so it works under Docker where logs go to stdout. The access-log middleware skips /api/logs, or the panel's poll would log itself forever. Detached torrents could leave a row stuck in 'downloading' across a restart, where nothing would ever revisit it; those are requeued at startup. Dockerfile, entrypoint and compose: 114 MB runtime, config bound to 0.0.0.0 on first run since container loopback is unreachable, drops to PUID:PGID for Unraid, and a healthcheck that goes through the control socket so a wedged worker reads as unhealthy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AdXho5tTkjFLeUXKbEjKBh
This commit is contained in:
32
PROGRESS.md
32
PROGRESS.md
@@ -56,6 +56,38 @@ and until now nothing set them.
|
||||
|
||||
---
|
||||
|
||||
## 2026-09-10 — Log view in the app, and Docker
|
||||
|
||||
**Log view.** A ring buffer (2000 lines) fed by a `tracing` layer, exposed at `/api/logs` with a
|
||||
sequence cursor so the UI polls for "everything after N" without duplicates. Tailing a file was the
|
||||
obvious approach and the wrong one: under Docker the logs go to stdout and there is no file. An
|
||||
`access_log` middleware adds a line per HTTP request, so the daemon and the web side share one
|
||||
stream — which is what Ray asked for. That middleware skips `/api/logs` itself, or the panel's
|
||||
2-second poll would generate a line per poll forever, a log of nothing but its own requests.
|
||||
|
||||
UI: sidebar **Log** button, level and text filters, follow-tail toggle, copy button, colour-coded
|
||||
levels.
|
||||
|
||||
**Restart safety.** Detached torrents introduced a `downloading` state, and a row left in it by a
|
||||
restart would sit there forever — the pending queue skips it and nothing else revisits it.
|
||||
`requeue_interrupted()` resets those at startup, leaving alone any row that already has a file.
|
||||
|
||||
**Docker.** Multi-stage build (deps cached separately from source), `debian:bookworm-slim` runtime,
|
||||
114 MB. Entrypoint writes a starter config bound to `0.0.0.0` on first run, because a container's
|
||||
loopback is unreachable from outside, and drops to `PUID:PGID` via gosu. Healthcheck is
|
||||
`ipx status`, which goes through the control socket to the worker and so catches a *wedged* daemon,
|
||||
not just a dead one.
|
||||
|
||||
Built and actually run, not just written: image builds, container starts, writes its config, serves
|
||||
the UI (401 without a token, 200 with), `/api/logs` answers, and the process runs as
|
||||
`uid=99(ipx) gid=100(users)` with files owned `99:100` and the token file at mode 600.
|
||||
|
||||
Worth noting for future testing here: this session's Docker socket belongs to the Unraid host, so
|
||||
`-v /tmp/...:/config` binds a path on the *host*, not one visible from inside this container. The
|
||||
mount looks empty from here while being perfectly correct — verify inside the container instead.
|
||||
|
||||
---
|
||||
|
||||
## 2026-09-10 — Torrents: they work, and one was freezing the whole daemon
|
||||
|
||||
Asked "is torrents working?". The honest answer had been "unverified since step 7" — so it got tested
|
||||
|
||||
Reference in New Issue
Block a user