From 2a85e9dc865f5495cb5d5f7c7fce34d7bdea87db Mon Sep 17 00:00:00 2001 From: Christopher Kahn <92762809+kahnclusions@users.noreply.github.com> Date: Sat, 3 Aug 2024 15:38:26 +0800 Subject: [PATCH] Pre-compress and embed assets in release mode --- Cargo.lock | 213 ++++++++++++++++++++++++++++++----------- Cargo.toml | 4 +- app/src/lib.rs | 2 +- server/Cargo.toml | 8 ++ server/src/fileserv.rs | 95 ++++++++++++------ style/tailwind.css | 2 +- 6 files changed, 236 insertions(+), 88 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0c640fd..f92d4fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + [[package]] name = "aead" version = "0.5.2" @@ -67,6 +73,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy 0.7.35", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -177,8 +195,6 @@ dependencies = [ "memchr", "pin-project-lite", "tokio", - "zstd", - "zstd-safe", ] [[package]] @@ -432,10 +448,6 @@ name = "cc" version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" -dependencies = [ - "jobserver", - "libc", -] [[package]] name = "cfg-if" @@ -610,6 +622,15 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + [[package]] name = "cpufeatures" version = "0.2.12" @@ -689,6 +710,12 @@ dependencies = [ "syn", ] +[[package]] +name = "dary_heap" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7762d17f1241643615821a8455a0b2c3e803784b058693d990b11f2dce25a0ca" + [[package]] name = "dashmap" version = "5.5.3" @@ -1075,6 +1102,15 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -1332,6 +1368,29 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "include-flate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df49c16750695486c1f34de05da5b7438096156466e7f76c38fcdf285cf0113e" +dependencies = [ + "include-flate-codegen", + "lazy_static", + "libflate", +] + +[[package]] +name = "include-flate-codegen" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c5b246c6261be723b85c61ecf87804e8ea4a35cb68be0ff282ed84b95ffe7d7" +dependencies = [ + "libflate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -1379,16 +1438,6 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" -[[package]] -name = "iri-string" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f5f6c2df22c009ac44f6f1499308e7a3ac7ba42cd2378475cc691510e1eef1b" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "itertools" version = "0.12.1" @@ -1404,15 +1453,6 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" -[[package]] -name = "jobserver" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" -dependencies = [ - "libc", -] - [[package]] name = "js-sys" version = "0.3.69" @@ -1657,6 +1697,30 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "libflate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7d5654ae1795afc7ff76f4365c2c8791b0feb18e8996a96adad8ffd7c3b2bf" +dependencies = [ + "adler32", + "core2", + "crc32fast", + "dary_heap", + "libflate_lz77", +] + +[[package]] +name = "libflate_lz77" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be5f52fb8c451576ec6b79d3f4deb327398bc05bbdbd99021a6e77a4c855d524" +dependencies = [ + "core2", + "hashbrown 0.13.2", + "rle-decode-fast", +] + [[package]] name = "linear-map" version = "1.2.0" @@ -2034,7 +2098,7 @@ version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee4364d9f3b902ef14fab8a1ddffb783a1cb6b4bba3bfc1fa3922732c7de97f" dependencies = [ - "zerocopy", + "zerocopy 0.6.6", ] [[package]] @@ -2361,6 +2425,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rle-decode-fast" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" + [[package]] name = "rstml" version = "0.11.2" @@ -2387,6 +2457,44 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "rust-embed" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0" +dependencies = [ + "axum", + "include-flate", + "mime_guess", + "rust-embed-impl", + "rust-embed-utils", + "tokio", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d" +dependencies = [ + "sha2", + "walkdir", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -2603,11 +2711,14 @@ dependencies = [ "axum", "cookie", "futures", + "lazy_static", "leptos", "leptos_axum", + "mime_guess", "qbittorrent-rs", "qbittorrent-rs-proto", "qbittorrent-rs-sse", + "rust-embed", "serde", "serde_json", "tokio", @@ -3130,7 +3241,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ "async-compression", - "base64 0.21.7", "bitflags 2.6.0", "bytes", "futures-core", @@ -3140,18 +3250,15 @@ dependencies = [ "http-body-util", "http-range-header", "httpdate", - "iri-string", "mime", "mime_guess", "percent-encoding", "pin-project-lite", "tokio", "tokio-util", - "tower", "tower-layer", "tower-service", "tracing", - "uuid", ] [[package]] @@ -3726,7 +3833,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6" dependencies = [ "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.6.6", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive 0.7.35", ] [[package]] @@ -3741,35 +3857,18 @@ dependencies = [ ] [[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" - -[[package]] -name = "zstd" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "7.2.0" +name = "zerocopy-derive" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa556e971e7b568dc775c136fc9de8c779b1c2fc3a63defaafadffdbd3181afa" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ - "zstd-sys", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "zstd-sys" -version = "2.0.12+zstd.1.5.6" +name = "zeroize" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13" -dependencies = [ - "cc", - "pkg-config", -] +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index 535580c..9d7db9a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ thiserror = "1" tracing = { version = "0.1" } tokio = { version = "1.33.0", features = ["full"] } tower = { version = "0.4.13", features = ["full"] } -tower-http = { version = "0.5", features = ["full"] } +tower-http = { version = "0.5", features = ["fs", "compression-gzip", "compression-br"] } uuid = { version = "1.9.0", features = [ "v4", "serde", @@ -53,7 +53,7 @@ lib-package = "frontend" # The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup. site-root = "target/site" -hash-files = false +hash-files = true # The site-root relative folder where all compiled output (JS, WASM and CSS) is written # Defaults to pkg diff --git a/app/src/lib.rs b/app/src/lib.rs index 9e968fb..985f3b4 100644 --- a/app/src/lib.rs +++ b/app/src/lib.rs @@ -114,7 +114,7 @@ fn Dashboard() -> impl IntoView { use qbittorrent_rs_sse::sse_sync_maindata; // Create sse signal - let data = sse_sync_maindata("http://localhost:3010/sse"); + let data = sse_sync_maindata("/sse"); view! {
Count: {move || { view! {
diff --git a/server/Cargo.toml b/server/Cargo.toml index 98bab9b..a785e38 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -25,3 +25,11 @@ serde_json.workspace = true cookie = { version = "0.18.1", features = ["secure"] } futures = "0.3.30" tokio-stream = "0.1.15" +lazy_static = "1.4.0" +rust-embed = { version = "8", features = [ + "axum", + "compression", + "mime_guess", + "tokio", +] } +mime_guess = "2.0.4" diff --git a/server/src/fileserv.rs b/server/src/fileserv.rs index e704c70..f433ffa 100644 --- a/server/src/fileserv.rs +++ b/server/src/fileserv.rs @@ -1,43 +1,84 @@ -use app::App; use axum::response::Response as AxumResponse; use axum::{ body::Body, - extract::State, - http::{Request, Response, StatusCode, Uri}, + http::{header, Request, Response, StatusCode, Uri}, response::IntoResponse, }; use leptos::prelude::*; -use tower::ServiceExt; -use tower_http::services::ServeDir; +use std::borrow::Cow; -pub async fn file_and_error_handler( - uri: Uri, - State(options): State, - req: Request, -) -> AxumResponse { - let root = options.site_root.clone(); - let res = get_static_file(uri.clone(), &root).await.unwrap(); +#[cfg(not(debug_assertions))] +const DEV_MODE: bool = false; + +#[cfg(debug_assertions)] +const DEV_MODE: bool = true; + +#[derive(rust_embed::RustEmbed)] +#[folder = "../target/site/"] +struct Assets; + +pub async fn file_and_error_handler(uri: Uri, req: Request) -> AxumResponse { + let accept_encoding = req + .headers() + .get("accept-encoding") + .map(|h| h.to_str().unwrap_or("none")) + .unwrap_or("none") + .to_string(); + let res = get_static_file(uri.clone(), accept_encoding).await.unwrap(); if res.status() == StatusCode::OK { res.into_response() } else { - let handler = leptos_axum::render_app_to_stream(move || view! { }); - handler(req).await.into_response() + (StatusCode::NOT_FOUND, "Not found.").into_response() } } -async fn get_static_file(uri: Uri, root: &str) -> Result, (StatusCode, String)> { - let req = Request::builder() - .uri(uri.clone()) - .body(Body::empty()) - .unwrap(); - // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot` - // This path is relative to the cargo root - match ServeDir::new(root).oneshot(req).await { - Ok(res) => Ok(res.map(Body::new)), - Err(err) => Err(( - StatusCode::INTERNAL_SERVER_ERROR, - format!("Something went wrong: {err}"), - )), +async fn get_static_file( + uri: Uri, + accept_encoding: String, +) -> Result, (StatusCode, String)> { + let (_, path) = uri.path().split_at(1); // split off the first `/` + let mime = mime_guess::from_path(path); + + let (path, encoding) = if DEV_MODE { + // during DEV, don't care about the precompression -> faster workflow + (Cow::from(path), "none") + } else if accept_encoding.contains("br") { + (Cow::from(format!("{}.br", path)), "br") + } else if accept_encoding.contains("gzip") { + (Cow::from(format!("{}.gz", path)), "gzip") + } else { + (Cow::from(path), "none") + }; + + match Assets::get(path.as_ref()) { + Some(content) => { + let body = Body::from(content.data); + + let res = match DEV_MODE { + true => Response::builder() + .header(header::CONTENT_TYPE, mime.first_or_octet_stream().as_ref()) + .header(header::CONTENT_ENCODING, encoding) + .body(body) + .unwrap(), + false => Response::builder() + .header(header::CACHE_CONTROL, "max-age=86400") + .header(header::CONTENT_TYPE, mime.first_or_octet_stream().as_ref()) + .header(header::CONTENT_ENCODING, encoding) + .body(body) + .unwrap(), + }; + + Ok(res.into_response()) + } + + None => { + eprintln!(">> Asset {} not found", path); + for a in Assets::iter() { + eprintln!("Available asset: {}", a); + } + + Err((StatusCode::NOT_FOUND, "Not found".to_string())) + } } } diff --git a/style/tailwind.css b/style/tailwind.css index 7ef8b1a..9daecad 100644 --- a/style/tailwind.css +++ b/style/tailwind.css @@ -15,7 +15,7 @@ html, body { font-style: normal; font-weight: 400; font-display: swap; - src: url(/fonts/Silkscreen/Silkscreen-regular.ttf) format('truetype'); + src: url(/fonts/Silkscreen/Silkscreen-Regular.ttf) format('truetype'); } @font-face { font-family: 'Cubic';