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>
- Gate native Tokio/Axum main() with #[cfg(not(target_arch = "wasm32"))]
- Add #![cfg_attr(target_arch = "wasm32", no_main)] to suppress missing-main error
- Add #[worker::event(fetch)] entry point using worker::HttpRequest / http::Response<axum::body::Body>
- Enable `http` feature on worker dep so fetch handler uses standard http types
- Add axum (json+query features), tower-service, and http to wasm32 deps
- Move async-trait to shared [dependencies] so both targets have it
- Make db::d1 module pub so main.rs can access D1Repository on wasm32
- Fix worker::d1::Database → D1Database and PreparedStatement → D1PreparedStatement
- Add #[cfg_attr(target_arch = "wasm32", worker::send)] to all 7 handler fns
so their futures satisfy Axum's Handler<Send> bound on single-threaded wasm32
- Remove redundant #![cfg(target_arch="wasm32")] from d1.rs (module
declaration in mod.rs already gates it)
- Remove unused D1Repository re-export from db/mod.rs
- Drop unused page/total_count fields from UI ListResponse struct
(only total_pages is consumed by the browse page)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace all 7 stub methods in src/bin/api/db/d1.rs with full working
implementations using the Cloudflare D1 API from workers-rs 0.5.
Implements:
- run_migrations: executes four DDL statements via db.exec()
- list_quotes: dynamic WHERE clause with positional params, COUNT query,
paginated SELECT, per-quote tag fetch
- get_quote: prepared statement with first::<QuoteRow>()
- get_random_quote: ORDER BY RANDOM() LIMIT 1
- create_quote: INSERT + batch tag insert + read-back for timestamps
- update_quote: auth check, dynamic SET clause, optional tag replacement,
read-back of updated row
- delete_quote: auth check, DELETE, returns DeleteResult enum
Also adds helper structs (QuoteRow, AuthRow, TagRow, CountRow),
fetch_tags() helper method, and unsafe Send/Sync impls required for
Arc<dyn QuoteRepository + Send + Sync> on single-threaded wasm32.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Detect 404 from random quote endpoint and render a friendly
'Nothing here yet — Submit a quote!' message with a link to
/submit instead of the generic error display.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace 'Submit another' link with 'Browse all quotes' -> Route::Browse
- Change date input from type=text to type=date, remove placeholder
- Make author optional (defaults to Anonymous), update label
- Clarify auth code label to 'Edit/delete passphrase'
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds 26 integration tests in handlers::integration_tests using a real
NativeRepository backed by a NamedTempFile SQLite database. Each test
gets an isolated database via test_router(). tempfile added to
[dev-dependencies]. Closes tickets: 5f5ba0, 9b581f, 789d0f, aa0eab,
f9f448, 4a4c26, 93f1b6, fae330, 8c87db, 893eba, e8f5cf, ce1e4f.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- HomePage: fetches random quote on mount, displays with QuoteCard and browse/submit links
- BrowsePage: paginated list with author and tag filter inputs, Pagination component
- QuotePage: view/edit/delete with AuthModal gating, 403/404 handling, sessionStorage auth
- AuthorPage: lists quotes by author with tag filter and pagination
- SubmitPage: full form with all fields, success state showing auth code prominently
- Tag filter (d3d502) integrated into Browse and Author pages
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the three separate sub-crates (api/, ui/, tests/) with a single
Cargo crate at the quotesdb/ root. Shared code lives in src/lib.rs; the
api and ui are multi-binary targets; integration tests use the standard
Cargo tests/ layout. Trunk files moved to project root with data-bin="ui".
Closes ticket b38032.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>