You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4.3 KiB

quotesdb — Architecture

Component Overview

Component Path Description
API binary src/bin/api/main.rs Rust/Axum backend on Cloudflare Workers. Handles all data operations via SQLx and Cloudflare D1.
UI binary src/bin/ui/main.rs Yew (Rust/Wasm) frontend on Cloudflare Pages. Communicates with the API over HTTP.
Shared library src/lib.rs Types and utilities shared between api and ui binaries. Must compile for both host and wasm32.
Integration tests tests/ Standard Cargo integration tests. Spin up the API against in-memory SQLite and make real HTTP requests.
Infra infra/ OpenTofu configuration for Cloudflare Worker, D1 database, and Pages project.

Component Interactions

Browser
  └──> Cloudflare Pages (ui binary compiled to Wasm)
         └──> Cloudflare Workers (api binary)
                └──> Cloudflare D1 (SQLite)

The UI is a static Wasm bundle served from Cloudflare Pages. It makes fetch requests to the Worker API, which reads and writes to a D1 database bound to the Worker.

Integration tests bypass the UI and talk directly to the API over HTTP, using a local in-memory SQLite database.

Build Targets

Artifact Command Compile target
API server cargo run or cargo build host (native)
UI Wasm bundle trunk serve or trunk build wasm32-unknown-unknown
Tests cargo test host (native)

Shared Code Constraints

src/lib.rs must compile for both wasm32-unknown-unknown (ui) and the host target (api). Avoid:

  • Threading primitives (std::thread, std::sync::Mutex)
  • Filesystem access (std::fs)
  • Any API that is not available in a Wasm environment

Use #[cfg(not(target_arch = "wasm32"))] and #[cfg(target_arch = "wasm32")] guards where needed.

Rate Limiting

Rate limiting is enforced at the Cloudflare WAF layer using a cloudflare_ruleset resource with phase = "http_ratelimit" (see infra/rate-limits.tf). All limits are keyed on the client IP address (ip.src). Blocked requests receive a Cloudflare-generated 429 Too Many Requests response before the Worker is invoked.

Configured Limits

Endpoint Method Limit Window Mitigation timeout
PUT /api/quotes PUT 5 requests 10 minutes 10 minutes
POST /api/quotes/:id/report POST 3 requests 1 hour 1 hour
POST /api/quotes/:id POST 10 requests 1 minute 1 minute
DELETE /api/quotes/:id DELETE 10 requests 1 minute 1 minute

GET endpoints are not rate-limited at the WAF layer — Cloudflare's CDN caches most read responses, making per-IP GET limits unnecessary.

Rule Ordering

Cloudflare evaluates http_ratelimit rules top-to-bottom with first-match semantics. The POST /api/quotes/:id/report rule is placed before the general POST /api/quotes/:id rule to prevent the broader pattern from matching report requests.

Plan Requirements

The Cloudflare free tier allows 1 custom rate limiting rule per zone. This configuration uses a single cloudflare_ruleset resource with multiple rules blocks, which counts as one ruleset but consumes multiple rule slots. Verify the account's plan limits before applying — a Pro or higher plan is recommended for the full four-rule set.

Layered Protection

WAF rate limiting complements (not replaces) Turnstile CAPTCHA on the submission form. CAPTCHA handles bot detection for quote creation; rate limiting enforces hard caps across all mutating endpoints including updates, deletes, and abuse reports.

Key Dependency Versions

Resolved versions for the UI Wasm target (scoped to [target.'cfg(target_arch = "wasm32")'.dependencies]):

Crate Version Notes
yew "0.22" Latest stable (0.22.1).
yew-router "0.19" Latest stable (0.19.0). Requires yew ^0.22.0 — compatible.
wasm-bindgen "0.2" Compatible with wasm-bindgen-cli 0.2.108 in the Nix dev shell.

Rationale: yew-router 0.19 explicitly requires yew ^0.22.0, making this the only correct combination with the latest stable Yew. The ^0.2 wasm-bindgen constraint in both crates is satisfied by the Nix-pinned 0.2.108.