v3.8.0 - per-user history deletes and a registration switch
Two things drove this release: a reported bug where a logged-in non-admin could not remove anything from their own history, and the need to run a public demo instance in multi-user mode without collecting stranger accounts. Diagnosing the first turned up four more defects in the same code paths, all fixed here.
Cache deletes are scoped to the caller (closes #49)
DELETE /api/cache/:id and DELETE /api/cache used to sit behind an admin-only guard, so a non-admin got 403 {"error":"Admin required"} while the UI still offered them the button. The premise held only halfway: conversions is deduplicated by URL and shared between all users, but each user's history is a separate user_fetches join table where unlinking a row affects nobody else.
An admin (and every caller when PULLMD_AUTH_MODE is disabled or unset) still purges the shared row. A regular user now unlinks only their own history entry, so the shared row and its /s/:id share link survive and other users keep it. Both responses gained scope: "user" | "global", delete-all gained removed, and existing status codes are unchanged.
For API consumers: in single-admin and multi-user modes, a non-admin DELETE /api/cache* now answers 200 with scope: "user" where it previously answered 403.
PULLMD_ALLOW_SIGNUP
Default on, so nothing changes for an existing instance. Setting it to false (or 0/no/off) means the /signup routes are not mounted at all: GET and POST answer 404, no account can be created by a crafted request, the login page drops its "create an account" link, and /api/config reports signupOpen: false.
Paired with the new node scripts/admin.js create-user <email>, so a closed instance is not a dead end.
Also fixed
- Orphaned history rows inflated the archive count.
pruneOlddropped conversions older than 90 days without removing the matchinguser_fetchesrows, andcountForUsercounts without the joinhistoryPageForUseruses, so the archive advertised entries it could never return and drifted further with every prune. Existing databases repair themselves on the next start. reset-passwordsilently did nothing when the password came from a pipe - and had done so for as long as the command existed. The non-interactive branch passed a callback tonode:readline/promises'question(), which returns a promise and ignores callbacks, so the process exited 0 without writing anything. Interactive use was never affected.- Read-only admin CLI commands wrote to the database.
list-usersran the auth migration, which on an auth-disabled instance either aborted with a stack trace or silently bootstrapped an admin and claimed every existing conversion for it. - Failed deletes are visible now instead of being reduced to a red border, including from inside the archive view, where the shared error banner was suppressed by an inline style.
- The login page no longer links to
/signupin modes where that route does not exist.
Backward compatible for anyone setting none of the new variables. 1091 tests.
Full notes: CHANGELOG.md