commit 628eacfe094f6c0c59c5d10a312ae20abc2d158f
parent 799c3db15832316cd75854b4a80f39b243da3eb6
Author: Silas Brack <silasbrack@gmail.com>
Date: Sat, 25 Apr 2026 11:25:56 +0200
fix: embed static files for Nix deployment
ServeDir doesn't work when running from Nix store.
Use include_bytes! to embed CSS at compile time.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat:
3 files changed, 44 insertions(+), 19 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -273,12 +273,6 @@ dependencies = [
]
[[package]]
-name = "http-range-header"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c"
-
-[[package]]
name = "httparse"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -633,7 +627,6 @@ version = "1.52.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6"
dependencies = [
- "bytes",
"libc",
"mio",
"pin-project-lite",
@@ -692,21 +685,13 @@ dependencies = [
"bitflags",
"bytes",
"futures-core",
- "futures-util",
"http",
"http-body",
- "http-body-util",
- "http-range-header",
- "httpdate",
- "mime",
- "mime_guess",
- "percent-encoding",
"pin-project-lite",
"tokio",
"tokio-util",
"tower-layer",
"tower-service",
- "tracing",
]
[[package]]
diff --git a/Cargo.toml b/Cargo.toml
@@ -7,7 +7,7 @@ edition = "2021"
axum = "0.8"
askama = "0.12"
tokio = { version = "1", features = ["macros", "rt-multi-thread", "net"] }
-tower-http = { version = "0.6", features = ["compression-gzip", "fs"] }
+tower-http = { version = "0.6", features = ["compression-gzip"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
thiserror = "2"
diff --git a/src/routes.rs b/src/routes.rs
@@ -1,9 +1,48 @@
-use axum::{routing::{get, post}, Router};
-use tower_http::services::ServeDir;
+use axum::{
+ Router,
+ body::Body,
+ http::{StatusCode, header},
+ response::{IntoResponse, Response},
+ routing::{get, post},
+};
use crate::handlers;
use crate::state::AppState;
+// Embed static files at compile time
+const STYLE_CSS: &[u8] = include_bytes!("../static/style.css");
+const STYLE_CSS_GZ: &[u8] = include_bytes!("../static/style.css.gz");
+
+async fn serve_style(headers: axum::http::HeaderMap) -> Response {
+ // Check if client accepts gzip
+ let accepts_gzip = headers
+ .get(header::ACCEPT_ENCODING)
+ .and_then(|v| v.to_str().ok())
+ .map(|s| s.contains("gzip"))
+ .unwrap_or(false);
+
+ if accepts_gzip {
+ Response::builder()
+ .status(StatusCode::OK)
+ .header(header::CONTENT_TYPE, "text/css")
+ .header(header::CONTENT_ENCODING, "gzip")
+ .header(header::CACHE_CONTROL, "public, max-age=31536000")
+ .body(Body::from(STYLE_CSS_GZ))
+ .unwrap()
+ } else {
+ Response::builder()
+ .status(StatusCode::OK)
+ .header(header::CONTENT_TYPE, "text/css")
+ .header(header::CACHE_CONTROL, "public, max-age=31536000")
+ .body(Body::from(STYLE_CSS))
+ .unwrap()
+ }
+}
+
+async fn static_404() -> impl IntoResponse {
+ (StatusCode::NOT_FOUND, "Not found")
+}
+
pub fn create_router(state: AppState) -> Router {
Router::new()
.route("/", get(handlers::show_home_page))
@@ -15,6 +54,7 @@ pub fn create_router(state: AppState) -> Router {
.route("/login", post(handlers::login))
.route("/register", get(handlers::show_register))
.route("/register", post(handlers::register))
- .nest_service("/static", ServeDir::new("static").precompressed_gzip())
+ .route("/static/style.css", get(serve_style))
+ .route("/static/{*path}", get(static_404))
.with_state(state)
}