diff --git a/.env.example b/.env.example index bb6db27..fdfbf9f 100644 --- a/.env.example +++ b/.env.example @@ -19,3 +19,9 @@ WEBP_METHOD=2 # Maximum concurrent sessions per user (default: 5, minimum: 1) MAX_SESSIONS_PER_USER=5 + +# Database connection pool size (default: 20) +DB_POOL_SIZE=20 + +# SSR page cache duration in seconds (default: 3600) +SSR_CACHE_SECS=3600 diff --git a/AGENTS.md b/AGENTS.md index 9ccb0be..4b676e4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -41,6 +41,8 @@ RATE_LIMIT_UPLOAD_PER_SEC=2 RATE_LIMIT_UPLOAD_BURST=15 RATE_LIMIT_IMAGE_PER_SEC=10 RATE_LIMIT_IMAGE_BURST=50 +DB_POOL_SIZE=20 # database connection pool size +SSR_CACHE_SECS=3600 # incremental SSR cache TTL ``` Run migrations before first dev server start: diff --git a/Cargo.lock b/Cargo.lock index d02d44a..0722c35 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4379,7 +4379,6 @@ dependencies = [ "tower", "tower-layer", "tower-service", - "tracing", "url", ] diff --git a/Cargo.toml b/Cargo.toml index 42f2570..2ed14e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,9 +15,9 @@ chrono = { version = "0.4", features = ["serde"] } regex = "1.12" pulldown-cmark = "0.13" dotenvy = { version = "0.15", optional = true } -tracing = { version = "0.1", optional = true } +tracing = { version = "0.1", optional = true, features = ["release_max_level_info"] } tracing-subscriber = { version = "0.3", optional = true } -tower-http = { version = "0.6", features = ["trace"], optional = true } +tower-http = { version = "0.6", optional = true } rand = { version = "0.8", features = ["getrandom"] } getrandom = { version = "0.2", features = ["js"] } http = "1" diff --git a/src/api/comments/markdown.rs b/src/api/comments/markdown.rs index aa531d8..9586b60 100644 --- a/src/api/comments/markdown.rs +++ b/src/api/comments/markdown.rs @@ -9,7 +9,7 @@ fn html_escape(s: &str) -> String { } #[cfg(feature = "server")] -pub fn clean_comment_html(input: &str) -> String { +static COMMENT_AMMONIA_BUILDER: std::sync::LazyLock = std::sync::LazyLock::new(|| { let mut builder = ammonia::Builder::default(); builder .rm_tags(["img", "details", "summary"]) @@ -25,8 +25,12 @@ pub fn clean_comment_html(input: &str) -> String { .add_tag_attributes("a", &["class", "aria-hidden", "aria-label"]) .add_tag_attributes("span", &["class"]) .link_rel(Some("nofollow noopener")); + builder +}); - builder.clean(input).to_string() +#[cfg(feature = "server")] +pub fn clean_comment_html(input: &str) -> String { + COMMENT_AMMONIA_BUILDER.clean(input).to_string() } #[cfg(feature = "server")] diff --git a/src/api/markdown.rs b/src/api/markdown.rs index 76139f3..b20c7c2 100644 --- a/src/api/markdown.rs +++ b/src/api/markdown.rs @@ -1,7 +1,7 @@ #![allow(clippy::unused_unit, deprecated, unused_imports)] #[cfg(feature = "server")] -pub fn clean_html(input: &str) -> String { +static AMMONIA_BUILDER: std::sync::LazyLock = std::sync::LazyLock::new(|| { let mut builder = ammonia::Builder::default(); builder .add_generic_attributes(&[ @@ -24,8 +24,12 @@ pub fn clean_html(input: &str) -> String { .add_tag_attributes("h4", &["id", "class"]) .add_tag_attributes("h5", &["id", "class"]) .add_tag_attributes("h6", &["id", "class"]); + builder +}); - builder.clean(input).to_string() +#[cfg(feature = "server")] +pub fn clean_html(input: &str) -> String { + AMMONIA_BUILDER.clean(input).to_string() } #[derive(Debug, Clone)] diff --git a/src/db/pool.rs b/src/db/pool.rs index 804ed31..8cf460a 100644 --- a/src/db/pool.rs +++ b/src/db/pool.rs @@ -16,7 +16,10 @@ pub static DB_POOL: LazyLock = LazyLock::new(|| { let mgr = Manager::from_config(pg_cfg, NoTls, mgr_cfg); Pool::builder(mgr) - .max_size(10) + .max_size(std::env::var("DB_POOL_SIZE") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(20)) .build() .expect("Failed to create database connection pool") }); diff --git a/src/main.rs b/src/main.rs index 4244821..e206be9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,7 +34,6 @@ fn main() { dioxus::server::serve(|| async move { use dioxus::server::{axum, DioxusRouterExt, ServeConfig}; - use tower_http::trace::TraceLayer; tokio::spawn(async { tasks::ip_purge::run_purge().await; @@ -46,7 +45,12 @@ fn main() { let config = ServeConfig::builder().incremental( dioxus::server::IncrementalRendererConfig::default() - .invalidate_after(std::time::Duration::from_secs(300)), + .invalidate_after(std::time::Duration::from_secs( + std::env::var("SSR_CACHE_SECS") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(3600), + )), ); let api_routes = axum::Router::new().route( "/api/upload", @@ -64,8 +68,7 @@ fn main() { let router = api_routes .merge(static_routes) - .merge(dioxus_app) - .layer(TraceLayer::new_for_http()); + .merge(dioxus_app); Ok(router) });