Add three tests verifying hidden-quote filtering behaviour in
NativeRepository:
- list_quotes_excludes_hidden: hidden quotes do not appear in paginated
listing results.
- get_random_quote_excludes_hidden: get_random_quote returns None when
the only quote is hidden.
- get_quote_returns_hidden_quote: get_quote (direct ID lookup) still
returns the quote when it is hidden.
Also refactor the inline row-mapping closure in list_quotes to use the
existing row_to_quote helper, eliminating duplicated column mapping
logic.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add `hidden: bool` to the `Quote` struct and `hidden: Option<bool>` to
`UpdateQuoteInput` in `src/lib.rs`
- Add `ALTER_QUOTES_ADD_HIDDEN` migration constant in `db/migrations.rs`
- Apply the ALTER TABLE migration in `NativeRepository::run_migrations` and
`D1Repository::run_migrations` with try/ignore for idempotency
- Exclude hidden quotes from `list_quotes` (WHERE hidden = 0) and
`get_random_quote` in both native and D1 implementations
- Update all SELECT queries to include the `hidden` column
- Handle `hidden` field in `update_quote` SET clause for both implementations
- Update `MockRepo` and `sample_quote` in handler tests to include `hidden`
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the two-step read-check-write in update_admin_auth_code with a
single atomic UPDATE … WHERE key = 'admin_auth_code' AND value = ?current
in both NativeRepository and D1Repository. Rows-affected count is checked:
zero means the code was absent or mismatched → DbError::Forbidden; one
means success.
Also remove the now-unnecessary replacement2 clone binding in native.rs.
Fix the reset_auth_code handler doc comment to accurately describe that a
missing X-Admin-Code header is caught by the handler itself (before any DB
call), while a wrong-but-present code reaches the DB layer which returns
DbError::Forbidden.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add three new QuoteRepository trait methods and a seed helper:
- update_admin_auth_code(current, new_code): replaces the admin code if
`current` matches; generates a fresh passphrase when new_code is None;
returns DbError::Forbidden on mismatch.
- get_submissions_locked(): reads the submissions_locked key from
admin_config; returns false when the key is absent.
- set_submissions_locked(locked): upserts "1"/"0" into admin_config.
- seed_submissions_locked(): INSERT OR IGNORE "0" — safe to call on every
startup without clobbering an active lock.
Implemented in both NativeRepository (rusqlite) and D1Repository (wasm32).
Updated startup seeding in main.rs (native and wasm32 paths) to call
seed_submissions_locked after the existing admin auth code seeding.
Added 7 unit tests in db/native.rs covering all four specified scenarios:
default false, set-then-get, seed does not overwrite, correct code succeeds,
None new_code generates passphrase, wrong code returns Forbidden, stored
code unchanged after Forbidden.
MockRepo in handlers/mod.rs updated with stub implementations of all four
new trait methods to satisfy the trait bound.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add 6 optional query parameters to GET /api/quotes:
date_after_year/month/day and date_before_year/month/day
Changes:
- QuoteRepository::list_quotes gains date_after and date_before params
- NativeRepository and D1Repository build ISO date prefix WHERE clauses;
quotes with NULL date are excluded when any bound is set
- list_handler validates component ordering (month requires year, etc.)
and returns 400 on invalid combinations
- build_date_bound helper converts y/m/d components to ISO prefix strings
- UI api::list_quotes and browse page gain From/To year filter inputs
- author page call updated to pass None for the new date params
- openapi.yaml extended with 6 new query parameter entries
- 6 new integration tests covering after, before, range, and 400 cases
- 1 new native DB unit test covering all filter combinations
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add an admin_config table storing a single admin auth code that
bypasses per-quote auth checks for update and delete operations.
The code is auto-generated on first startup and printed to stderr.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>