Fix System page crash, retire "held" copy, and slim CLAUDE.md
All checks were successful
Deploy / deploy (push) Successful in 27s
All checks were successful
Deploy / deploy (push) Successful in 27s
- system.html crashed with a 500 whenever SABnzbd history loaded: the trend() macro compared the formatted "avg_speed" string (e.g. "900 KB/s") against a number. Added a real numeric avg_speed_kbps field and pointed the macro at that instead. - Replaced "held"/"not held" wording with "in library"/"not in library" on Home and Discover. - Moved CLAUDE.md's Deployment/reverse-proxy and account-management sections into two on-demand skills (deploy-arr-summary, manage-accounts) so they only load when actually relevant, with a one-line pointer left in their place. - Updated PRODUCT.md's restructure note now that meridian-marquee has merged to main. - Dropped two stale/broken entries from settings.local.json's permission allowlist (a rule from a different machine/path, and one with a corrupted token). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NBw1QodG8ZMYHrGeQfqyDS
This commit is contained in:
parent
bb3e9af056
commit
08491c0c9b
9 changed files with 153 additions and 133 deletions
115
CLAUDE.md
115
CLAUDE.md
|
|
@ -166,119 +166,14 @@ app from a shell on an explicit `PORT` is the working path.
|
|||
|
||||
## Deployment
|
||||
|
||||
**Target:** Unraid server, container path `/mnt/user/appdata/greendale`
|
||||
(note the directory name differs from the container/repo name), built from
|
||||
`Dockerfile`/`docker-compose.yml` in this repo. Container name
|
||||
`arr-summary`, port `5055` (overridable via `HOST_PORT`) → container port
|
||||
`5000`.
|
||||
|
||||
**Deploy mechanism: Forgejo push-to-deploy**, same pattern as the other
|
||||
three projects on this box — see `card-grader`'s `CLAUDE.md` for the full
|
||||
pipeline mechanics. Push to `main` on the `forgejo` remote
|
||||
(`https://git.hippofam.com/ninja_hippo/arr-summary.git`) triggers
|
||||
`.forgejo/workflows/deploy.yml`, which syncs into
|
||||
`/mnt/user/appdata/greendale` (excluding `.git`, `.env`, `data/`) and runs
|
||||
`docker compose up -d --build`.
|
||||
|
||||
**Restarting the container manually: use Compose, not the Unraid Docker
|
||||
UI.** Every deploy recreates the container with a new Docker ID (same
|
||||
name, `arr-summary`). Unraid's Docker tab caches the old ID, so hitting
|
||||
Restart there after a deploy throws "No such container" — harmless, but
|
||||
confusing. From the Unraid terminal:
|
||||
```bash
|
||||
cd /mnt/user/appdata/greendale && docker compose restart
|
||||
```
|
||||
`docker compose up -d --force-recreate` for a guaranteed-clean recreate;
|
||||
`docker ps --filter name=arr-summary` to check real state regardless of
|
||||
what the Unraid UI shows.
|
||||
|
||||
**Confirming a deploy actually landed:** the dashboard footer renders
|
||||
`v<APP_VERSION> · <APP_VERSION_DATE>` from constants near the top of
|
||||
`app.py` (both env-overridable, injected into every template by the
|
||||
`inject_version` context processor). Bump them when cutting a deploy —
|
||||
loading the page is then the quickest honest check that the new build is
|
||||
live, without trusting the Unraid Docker tab's cached container IDs.
|
||||
|
||||
## Reverse proxy (Nginx Proxy Manager)
|
||||
|
||||
Two NPM proxy hosts front this app and `git.hippofam.com`, both using
|
||||
Let's Encrypt certs issued via **DNS-01 through Porkbun** — required,
|
||||
not optional: **Cox blocks inbound port 80** on this connection, so
|
||||
HTTP-01 challenges (and plain `http://` requests generally) can never
|
||||
work here. `http://hippofam.com` will always time out from outside the
|
||||
LAN; only `https://` resolves. Both proxy hosts have Force SSL on.
|
||||
|
||||
`hippofam.com` → `http://192.168.86.33:5055` (the Unraid box, this
|
||||
container's port). Verify the destination IP/port here before assuming
|
||||
an outage is DNS or app-level — it has drifted to the wrong host before.
|
||||
|
||||
On 2026-08-26 the apex host's own cert (issued Aug 2025) went dark: NPM's
|
||||
UI still showed it "In Use" with a valid, unexpired date, but nginx
|
||||
presented no certificate at all for `hippofam.com` (TLS alert 112,
|
||||
"unrecognized name") — the on-disk cert files had rotted independently
|
||||
of the DB record NPM's UI reads from. `git.hippofam.com`, on a newer
|
||||
cert from the same account, was unaffected. Fixed by pointing the proxy
|
||||
host at the `*.hippofam.com` + `hippofam.com` wildcard cert already
|
||||
issued and sitting unused, which forced NPM to regenerate the nginx
|
||||
config with known-good cert files. If `hippofam.com` ever throws a TLS
|
||||
handshake error again with NPM's UI reporting all green, suspect this
|
||||
same on-disk/DB drift before anything else — check with:
|
||||
```bash
|
||||
openssl s_client -connect <public-ip>:443 -servername hippofam.com </dev/null
|
||||
```
|
||||
A clean run shows `subject=/CN=...` and `Verify return code: 0`; the
|
||||
broken state shows `no peer certificate available`.
|
||||
|
||||
**Persistent state, never touched by deploys:** `.env` holds every API key
|
||||
and `LOGIN_PASSWORD` — this project was already doing this correctly
|
||||
before the Forgejo migration (compose file uses `${VAR}` substitution from
|
||||
`.env`, no hardcoded secrets), unlike `n64-tracker` which needed fixing.
|
||||
Runtime settings written by the Settings page (API-key overrides,
|
||||
timezone, changed login password) live in `settings.json` at
|
||||
`SETTINGS_FILE`, which the compose file points at `/data/settings.json`
|
||||
on the `./data` bind mount — i.e. `/mnt/user/appdata/greendale/data`.
|
||||
That mount is load-bearing: without it the file sits on the container
|
||||
filesystem, and every `up -d --build` recreates the container and
|
||||
silently discards it. `data/` is likewise excluded from the deploy
|
||||
rsync, which runs `--delete` and would otherwise remove it for not
|
||||
being in the repo.
|
||||
See the `deploy-arr-summary` skill for the Forgejo push-to-deploy
|
||||
pipeline, manual container restart steps, deploy verification, and the
|
||||
Nginx Proxy Manager / TLS setup and its Cox-port-80/DNS-01 constraint.
|
||||
|
||||
## Logins
|
||||
|
||||
Two modes, chosen by whether `settings.json` holds a non-empty `users` map:
|
||||
|
||||
- **Multi-user** (once any account exists): username + password, each
|
||||
password stored as a salted pbkdf2 hash under `users` in `settings.json`.
|
||||
`verify_credentials()` is the only entry point; `load_users()` re-reads
|
||||
the file per check, so adding a user or changing a password is live
|
||||
immediately in every gunicorn worker — no restart, unlike the API keys.
|
||||
- **Legacy single shared password** (no accounts yet): the original
|
||||
`LOGIN_PASSWORD` / `settings.json.login_password` path, username ignored.
|
||||
This fallback exists so deploying multi-user support can't lock an
|
||||
existing install out; the login page hides the username field entirely
|
||||
until accounts exist.
|
||||
|
||||
Manage accounts with `manage_users.py`, which prompts for passwords
|
||||
(never echoed, never a CLI argument, so they stay out of shell history)
|
||||
and only ever writes hashes:
|
||||
|
||||
```bash
|
||||
docker exec -it arr-summary python3 manage_users.py list
|
||||
docker exec -it arr-summary python3 manage_users.py add <username>
|
||||
docker exec -it arr-summary python3 manage_users.py passwd <username>
|
||||
docker exec -it arr-summary python3 manage_users.py remove <username>
|
||||
```
|
||||
|
||||
**Adding the first account switches the whole dashboard to per-user
|
||||
logins and the shared password stops working** — verify you can sign in
|
||||
as that user before closing the shell. `remove` refuses to delete the
|
||||
last account for the same reason.
|
||||
|
||||
Users live in `settings.json` rather than an env var deliberately: a
|
||||
pbkdf2 hash contains `$`, which docker compose interpolates when reading
|
||||
`.env`. `PASSWORD_HASH_METHOD` is pinned to pbkdf2 rather than Werkzeug's
|
||||
default scrypt, which needs a hashlib built against OpenSSL and is absent
|
||||
on the macOS system Python.
|
||||
See the `manage-accounts` skill for the multi-user vs. legacy
|
||||
single-shared-password login modes and the `manage_users.py` CLI.
|
||||
|
||||
**Never put a real password in the repo** — not in `app.py`, not in
|
||||
`settings.json` committed to git, not in a fixture. `.env` and `data/`
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue