User admin in the web UI, admin-only log, unread-first OPML feeds
- Settings > Manage users: add an account (password, or none for proxy
sign-in), toggle admin, remove. Backed by GET/POST /api/users and
PATCH/DELETE /api/users/{id}, 403 for non-admins. The only admin
cannot be demoted or removed.
- GET /api/logs is admin-only and the Log button is hidden for others;
the log names every account, feed and failed sign-in.
- Feeds inside an OPML list those with unread items first, in the
sidebar folder and on the subscription's page.
- Deploying is now buildx --push to 192.168.1.130:5000 and recreating
the ipodderx service of the Arcane project content; CLAUDE.md and the
README's Docker section say so.
- Tests: Playwright for user admin, the last-admin guard, 403s for a
non-admin and the unread ordering (new Aardvark Radio fixture); a unit
test for last_admin; the smoke test drives usersModal.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0173mGu6rK18Ne7UGTwAaVJV
This commit is contained in:
45
CLAUDE.md
45
CLAUDE.md
@@ -6,31 +6,37 @@ made here.
|
||||
|
||||
## Where things are
|
||||
|
||||
The live install on this machine:
|
||||
Production is the `iPodderX` container on Tower (192.168.1.130), the `ipodderx` service of the
|
||||
Arcane project `content`: `/mnt/fast/arcane/projects/content/compose.yaml`. That file is what runs;
|
||||
`docker-compose.yml` in this repo is a copy, and editing it changes nothing in production.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Binary | `/config/.cargo/bin/ipx` |
|
||||
| Config | `/config/.config/ipx/config.toml` |
|
||||
| Database | `/config/.local/share/ipx/state.db` |
|
||||
| Downloads | `/mnt/user/audio/ipx` |
|
||||
| Web UI | `0.0.0.0:8099`, also `ipodderx.sdf1.net` via a Cloudflare tunnel |
|
||||
| | Host | In the container |
|
||||
|---|---|---|
|
||||
| Image | `192.168.1.130:5000/ipodderx:latest` | |
|
||||
| Config | `/mnt/fast/appdata/ipodderx/config.toml` | `/config/config.toml` |
|
||||
| Database | `/mnt/user/ipodderx/state.db` | `/data/state.db` |
|
||||
| Downloads | `/mnt/user/ipodderx/downloads` | `/downloads` |
|
||||
| Web UI | `192.168.1.130:8099`, also `ipodderx.sdf1.net` via a Cloudflare tunnel | `0.0.0.0:8099` |
|
||||
|
||||
Deploying a change is: build, stop, copy, start.
|
||||
Deploying a change is: build and push the image, then pull it and recreate the container.
|
||||
|
||||
```sh
|
||||
cargo build --release
|
||||
pkill -x ipx; sleep 2
|
||||
cp target/release/ipx /config/.cargo/bin/ipx
|
||||
setsid nohup /config/.cargo/bin/ipx --config /config/.config/ipx/config.toml daemon \
|
||||
>/tmp/ipx.log 2>&1 </dev/null &
|
||||
docker buildx build --tag 192.168.1.130:5000/ipodderx:latest . --push
|
||||
docker compose -f /mnt/fast/arcane/projects/content/compose.yaml pull ipodderx
|
||||
docker compose -f /mnt/fast/arcane/projects/content/compose.yaml up -d ipodderx
|
||||
docker logs --tail 20 iPodderX
|
||||
```
|
||||
|
||||
**`pkill -x ipx`, never `pkill -f ipx`.** `-f` matches the shell running the command and kills the
|
||||
session (exit 144). This has happened more than once.
|
||||
**Name the service.** A bare `up -d` recreates every container in `content`, beets and immich
|
||||
included. Run `pull` before `up`, because `up` reuses whatever `latest` the host already has.
|
||||
|
||||
The daemon is not supervised: it will not survive a reboot. `contrib/` has a systemd unit nobody
|
||||
has installed.
|
||||
The healthcheck runs `ipx status` against the control socket, so `(healthy)` in `docker ps` means
|
||||
the worker is alive, not just the web port. The container restarts on its own after a reboot.
|
||||
|
||||
Before the container, ipx ran by hand in code-server, with its files in `/config/.config/ipx/` and
|
||||
`/config/.local/share/ipx/`. Those are still there and the container does not read them. If you run
|
||||
a daemon by hand for testing, stop it with **`pkill -x ipx`, never `pkill -f ipx`**. `-f` matches
|
||||
the shell running the command and kills the session (exit 144). This has happened more than once.
|
||||
|
||||
## Before you touch the page
|
||||
|
||||
@@ -110,10 +116,7 @@ Deliberate simplifications get a `ponytail:` comment naming the ceiling and the
|
||||
|
||||
## Known gaps
|
||||
|
||||
* No user administration in the web UI; `ipx user` on the box only.
|
||||
* Cloudflare's `Cf-Access-Jwt-Assertion` is not verified — ipx trusts the hop plus `trusted_proxies`
|
||||
(documented in [docs/sso.md](docs/sso.md)).
|
||||
* The Docker image predates multi-user; `docker compose build` before relying on it.
|
||||
* Downloads land root-owned; Unraid shares want `99:100`.
|
||||
* A feed's `<description>` subtitle is dropped whenever `content:encoded` exists, which loses
|
||||
Substack-style subtitles.
|
||||
|
||||
Reference in New Issue
Block a user