mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 04:32:42 +00:00
When cycle tracking is on, the cycle ring takes the Strain slot in the health-scores strip rather than adding a sixth tile: the wellness strip gains a hideStrain flag the insights page sets from the cycle gate, so Strain is hidden for cycle accounts and shown unchanged for everyone else. Fix the oversized vertical gap on the insights overview. The cycle summary card set data-revealed on the same node that carried wellness-tile-rise, but the entrance keyframe selector is a descendant combinator, so the animation never matched and the card stayed at the rise rule's opacity: 0 — invisible yet still occupying its box. Wrap the card so data-revealed sits on an ancestor, restoring the entrance and removing the phantom gap. Move the gated cycle entry directly after Medications (before Insights) in the sidebar nav instead of appending it at the end, spliced by the Medications index so the position survives a list reorder. Add an operator-only password-reset CLI. It runs under plain node in the production standalone image (which strips tsx) via the in-image pg and @node-rs/argon2, reusing the app's Argon2id parameters through a shared .mjs module so a reset hash matches one the app would mint. It matches a single user by username or email, reads the new password without echoing, never prints it back, and exits non-zero on zero or multiple matches. Documented in docs/ops/password-reset.md.
62 lines
2.4 KiB
Markdown
62 lines
2.4 KiB
Markdown
# Password reset (operator)
|
|
|
|
A self-hosted operator can reset a single user's password from inside the
|
|
running container. This is an **operator-only** maintenance path — there is no
|
|
user-facing or API route for it, and it bypasses the normal account-recovery
|
|
flow. Use it only for an account you administer (for example, after a forgotten
|
|
password on a single-user instance).
|
|
|
|
> **Operator-only.** The script writes directly to the `users` table and is
|
|
> intended to be run by the host operator with database access. Treat the new
|
|
> password like any other secret: pass it on stdin (it is read without echoing)
|
|
> rather than as a shell argument where it would land in shell history and the
|
|
> process list.
|
|
|
|
## Run it
|
|
|
|
The reset CLI ships in the image and runs under plain `node` against the built
|
|
runtime (the standalone image strips `tsx`, so this is a `.mjs` that uses the
|
|
in-image `pg` and `@node-rs/argon2` directly and reuses the app's exact Argon2id
|
|
parameters):
|
|
|
|
```sh
|
|
docker compose exec app node scripts/reset-password.mjs <username-or-email>
|
|
```
|
|
|
|
The script prompts for the new password without echoing it, then prints a
|
|
confirmation that names only the user — never the password:
|
|
|
|
```
|
|
New password (input hidden):
|
|
reset-password: password updated for "alice"
|
|
```
|
|
|
|
You can also pass the password as a second argument for non-interactive use,
|
|
but prefer the stdin prompt so the secret stays out of shell history:
|
|
|
|
```sh
|
|
docker compose exec app node scripts/reset-password.mjs alice 'a-strong-passphrase'
|
|
```
|
|
|
|
## Behaviour
|
|
|
|
- The identifier is matched **case-insensitively** against `username` **or**
|
|
`email`, mirroring the login lookup.
|
|
- The match must resolve to exactly one user. Zero matches exits non-zero with
|
|
`no user matches "<id>"`; multiple matches exits non-zero and refuses to guess.
|
|
- The new password must be at least 12 characters (the app's minimum).
|
|
- The password is hashed with Argon2id using the same cost parameters as the
|
|
application (`src/lib/auth/argon2-params.mjs`), so the stored hash is
|
|
identical to one the app would mint.
|
|
- `DATABASE_URL` is read from the environment — the same variable the app uses,
|
|
honouring any `sslmode=` in the connection string.
|
|
|
|
## Source checkout
|
|
|
|
From a source checkout (not the production image) the same script runs under
|
|
Node directly:
|
|
|
|
```sh
|
|
DATABASE_URL='postgres://…' node scripts/reset-password.mjs <username-or-email>
|
|
```
|