10 Commits (49f70cc5e8990351f0bbcc63ea00d4ef5fe5dc2e)

Author SHA1 Message Date
Elijah Voigt 5dcbb334fa feat(quotesdb): Cloudflare Turnstile CAPTCHA on submit
- infra/turnstile.tf: provision Turnstile widget (managed mode, quotes.elijah.run domain) with site_key and secret_key outputs
- infra/variables.tf: add var.domain (default: quotes.elijah.run)
- src/lib.rs: add cf_turnstile_token: Option<String> (#[serde(default)]) to CreateQuoteInput; update doctest
- Cargo.toml: add reqwest (0.12, rustls-tls) under native-only dependencies
- src/bin/api/handlers/mod.rs: add verify_turnstile() and CAPTCHA gate in create_handler, both gated on #[cfg(not(target_arch = "wasm32"))]
- src/bin/api/db/native.rs: add cf_turnstile_token: None to all CreateQuoteInput struct literals in tests
- api/openapi.yaml: document cf_turnstile_token field in QuoteCreateRequest schema
- index.html: add Turnstile JS script tag
- src/bin/ui/pages/submit.rs: add turnstile_token state, use_effect_with callback registration, widget div, token included in CreateQuoteInput
- docs/LOCAL_DEV.md: add Cloudflare Turnstile CAPTCHA section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt bdf99b32c4 feat(quotesdb): add workers-rs WASM entry point to api binary
- 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
3 months ago
Elijah Voigt 4f1aa5560a test(quotesdb): add integration test suite for all API endpoints
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>
3 months ago
Elijah Voigt bc48924d16 feat(quotesdb): implement API DB layer and all HTTP handlers
DB layer (src/bin/api/db/):
- native.rs: NativeRepository (tokio-rusqlite) implementing all CRUD ops,
  dynamic WHERE for filters, two-phase auth check for update, 13 unit tests
- d1.rs: D1Repository wasm32 stub (all methods return Internal error)
- connection.rs: open() helper — WAL + foreign_keys pragmas
- mod.rs: cfg-gate async_trait (Send on native, ?Send on wasm32)

Handlers (src/bin/api/handlers/mod.rs):
- All 7 routes: GET /api/, random, {id}, list, PUT create, POST update, DELETE
- Router order: random BEFORE {id} (prevents "random" matching as id)
- Auth: X-Auth-Code header validation → 403 on mismatch
- 13 handler unit tests with MockRepo

main.rs: opens DB, runs migrations, wraps in Arc<dyn Repo + Send + Sync>,
  binds on $PORT (default 3000)

Cargo.toml: tower dev-dep for ServiceExt::oneshot in tests

All 32 tests pass (26 api + 6 lib)

Tickets closed: 00aff0 a5049d 6e829e 28e7d9 886bfd 2ce22e 5dbb7d 05f8ae
                d792e2 5d9f5a b20b5a 175382 03bb91

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt 6f7614c0c8 feat(quotesdb): implement UI app shell, API client, storage, and base components
- Add BrowserRouter with all 5 routes (Home, Browse, QuoteDetail, Author, Submit)
- Implement typed API client (list, get, random, create, update, delete)
- Implement sessionStorage auth code helpers (set/get/clear)
- Add ErrorDisplay, QuoteCard, AuthModal, Pagination components
- Add stub page components for initial compilation
- Fix Cargo.toml: uuid js feature for wasm32, getrandom 0.3 wasm_js for rand dep,
  js-sys and Storage web-sys feature for API client and storage module

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt a7b2d6fd4e feat(quotesdb): add QuoteRepository trait, migrations, and thiserror dependency
- db/mod.rs: QuoteRepository async trait + ListResult/DeleteResult/DbError types
- db/migrations.rs: SQL DDL strings for quotes, quote_tags, and indexes
- lib.rs: fix rand 0.9 trait import (SliceRandom → IndexedRandom)
- Cargo.toml: add thiserror = "2" for DbError derive

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt b695cb5b6d feat(quotesdb): add Yew/Wasm UI dependencies to Cargo.toml
- Add yew 0.22 with csr feature for Rust/Wasm SPA frontend
- Add yew-router 0.19 for client-side routing
- Add gloo 0.11 for Web API utilities (timers, fetch, events)
- Add wasm-bindgen 0.2 matching Nix wasm-bindgen-cli 0.2.108
- Add wasm-bindgen-futures 0.4 for async fetch support
- Add web-sys 0.3 for raw DOM/browser API bindings
All UI deps scoped to wasm32 cfg target to avoid host build pollution

Closes ticket 93515e
3 months ago
Elijah Voigt 7bd2bf4ed5 chore(quotesdb): set up Cargo.toml with api and ui dependencies
- Add axum 0.8 + tokio 1 (native-only) for the HTTP API server
- Add rusqlite 0.32 + tokio-rusqlite 0.6 (native-only) for local SQLite
- Add async-trait 0.1 (native-only) for QuoteRepository abstraction
- Add worker 0.5 with d1 feature (wasm32-only) for Cloudflare Workers
- Add rand 0.9 + serde + serde_json (shared) for passphrase/serialisation
- Add build-dependencies: serde_json 1 + serde_yaml 0.9 for build.rs
- Pin rusqlite to 0.32 to match tokio-rusqlite 0.6 transitive dependency

Closes ticket 1f5bb5

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt b0cf17ec83 feat(quotesdb): add generate_id() using UUID v4 — WASM-compatible ID generation
- Add uuid = { version = "1", features = ["v4", "serde"] } to [dependencies]
- Add getrandom = { version = "0.4", features = ["wasm_js"] } under wasm32 cfg
- Implement generate_id() in src/lib.rs with rustdoc and doctest
- Add unit tests for length, hyphen count, and uniqueness

Closes ticket 7a0d9f

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt 91ecd91176 refactor(quotesdb): collapse to single crate with api and ui binaries
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>
3 months ago