SQLite has no boolean type, so raw rows return explicit as 0/1. The
podcast edit form round-trips whatever GET /api/podcasts/:id sends it,
and the update schema requires z.boolean() — so saving any change
without also touching the explicit checkbox failed validation with
"Expected boolean, received number".
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds an "unlisted" flag on episodes rather than reusing the existing
hard-delete route, since a hard delete cascades to purchases/earnings
(ON DELETE CASCADE) and would wipe a producer's sales history and any
unwithdrawn earnings for that episode. Unlisting only affects feed.xml
output — the episode, its purchases, and reseller listings all stay
intact and it can be relisted at any time.
Wires up the missing frontend for it too: the podcast settings page
had no episode list or management controls at all before this, despite
the backend already exposing full episode CRUD.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Lets the admin restrict which pubkeys may log in, enforced server-side
at /api/auth/login before a session is issued. Disabled by default;
the admin and the bootstrap (no-admin-claimed-yet) case always pass.
Manageable via the existing settings UI/API (npub or hex, one per line).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Renames the repo directory and every podpuddle/PODPUDDLE reference
across code, config, and docs to podsteadr/PODSTEADR (package names,
Docker Compose project/service/volume names, env var names, UI/RSS
strings). Existing Docker volume data (uploaded blobs, mediamtx
recordings, the server's sqlite DB and its nostr identity key) was
migrated to new podsteadr_-prefixed volumes with matching filenames
so it isn't orphaned by the rename.