simple-web-app

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

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:
MCargo.lock | 15---------------
MCargo.toml | 2+-
Msrc/routes.rs | 46+++++++++++++++++++++++++++++++++++++++++++---
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) }