The origin was 3–8 ms per page, almost all of it per-statement overhead: an issue page ran two statements per pick (~55 on a 25-article issue) and every signed-in page wrote its session row back because `take_flash` called `Session::remove`, which marks the session modified even when the key is absent. - `Db::get_articles` loads an issue's articles and their social rows in two statements; both branches of `web::issue::load` use it. The single-id and batch queries share one projection via a macro. - `take_flash` reads before removing, and touches a signed-in session at most once a day so the inactivity expiry still slides. Anonymous requests never create a session. - `Server-Timing: app;dur=<ms>` on every response, outermost layer. - `reject_early_data`: 425 for a non-safe method that arrived as TLS 0-RTT data, so nginx `ssl_early_data on` is safe (RFC 8470 §5.2). - `[profile.release]`: fat LTO, one codegen unit (binary 46 → 29 MB). Docs: the Cloudflare proxy was retired on 2026-09-05 after measuring +43 ms per signed-in page from Boston; README reverse-proxy section is now the direct setup (upstream keepalive, 0-RTT lines) and the CDN runbook carries a retired-status banner. Dev seed, app-side: `/` 21 → 5 statements, 3.7 → 1.0 ms; `/feed.xml` 44 → 12, 9.1 → 3.0 ms; session writes per signed-in page 1 → 0. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Va5eMEmWEnjMXBsBob5FDW
76 lines
2.1 KiB
TOML
76 lines
2.1 KiB
TOML
[package]
|
|
name = "daily-epub"
|
|
version = "0.3.0"
|
|
edition = "2024"
|
|
|
|
[dependencies]
|
|
ammonia = "4.1.4"
|
|
anyhow = "1.0.104"
|
|
askama = "0.16.0"
|
|
async-trait = "0.1.92"
|
|
axum = "0.8.9"
|
|
axum-login = { git = "https://github.com/maxcountryman/axum-login.git", rev = "151c72d7a1b4646830f86b4332e6bd6e34d719a7", version = "0.18.0" }
|
|
base64 = "0.23.1"
|
|
clap = { version = "4.6.6", features = ["derive"] }
|
|
dom_smoothie = "0.18.0"
|
|
epub-builder = "0.8.3"
|
|
figment = { version = "0.10.19", features = ["toml", "env"] }
|
|
futures = "0.3.34"
|
|
hex = "0.4.3"
|
|
hmac = "0.13.0"
|
|
image = "0.25.10"
|
|
jiff = { version = "0.2.35", features = ["serde"] }
|
|
libc = "0.2.189"
|
|
password-auth = "1.0.0"
|
|
rand = "0.10.2"
|
|
reqwest = { version = "0.13.4", default-features = false, features = [
|
|
"rustls",
|
|
"gzip",
|
|
"json",
|
|
"charset",
|
|
"http2",
|
|
"query",
|
|
"system-proxy",
|
|
] }
|
|
resvg = "0.48.1"
|
|
rpassword = "7.5.4"
|
|
scraper = "0.27.0"
|
|
serde = { version = "1.0.229", features = ["derive"] }
|
|
serde_json = "1.0.151"
|
|
sha2 = "0.11.0"
|
|
sqlx = { version = "0.9.0", default-features = false, features = [
|
|
"derive",
|
|
"json",
|
|
"macros",
|
|
"migrate",
|
|
"runtime-tokio",
|
|
"sqlite",
|
|
"tls-rustls-ring",
|
|
] }
|
|
thiserror = "2.0.20"
|
|
time = "0.3.55"
|
|
tiny-skia = "0.12.0"
|
|
tokio = { version = "1.53.1", features = ["full"] }
|
|
tokio-util = { version = "0.7.19", features = ["io"] }
|
|
toml = "1.1.4"
|
|
toml_edit = "0.25"
|
|
tower-http = { version = "0.7.0", features = ["trace", "fs", "request-id"] }
|
|
tower_governor = { version = "0.8.0", features = ["axum"] }
|
|
tracing = "0.1.44"
|
|
tracing-subscriber = { version = "0.3.23", features = ["env-filter"] }
|
|
url = { version = "2.5.8", features = ["serde"] }
|
|
zip = { version = "6", default-features = false, features = ["deflate"] }
|
|
|
|
[profile.release]
|
|
# Per-request cost in the web server is JSON decode and template render, both
|
|
# CPU-bound and spread across crates; whole-program LTO and one codegen unit
|
|
# buy a slice of that for a longer release build (tests use the dev profile).
|
|
lto = "fat"
|
|
codegen-units = 1
|
|
|
|
[dev-dependencies]
|
|
figment = { version = "0.10.19", features = ["test", "toml", "env"] }
|
|
roxmltree = "0.21.1"
|
|
tempfile = "3.27.0"
|
|
tower = { version = "0.5.3", features = ["util"] }
|