feat: Add embed and noembed feature flags for HTTP assets/templates

This commit is contained in:
selfhoster selfhoster 2026-09-08 17:24:01 +02:00
commit 1bbdb04dc9
5 changed files with 88 additions and 9 deletions

46
Cargo.lock generated
View file

@ -125,6 +125,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bitflags"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
version = "0.12.1" version = "0.12.1"
@ -369,6 +375,12 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "http-range-header"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c"
[[package]] [[package]]
name = "httparse" name = "httparse"
version = "1.10.1" version = "1.10.1"
@ -504,11 +516,13 @@ dependencies = [
"http", "http",
"ldap3_proto", "ldap3_proto",
"minijinja", "minijinja",
"minijinja-embed",
"serde", "serde",
"static-serve", "static-serve",
"tokio", "tokio",
"tokio-util", "tokio-util",
"tower", "tower",
"tower-http",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
"uuid", "uuid",
@ -564,6 +578,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "minijinja-embed"
version = "2.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2795289c499a1331f73166ff8b8e0e68fdc81ef0b385eecb0e4de96f1638b66"
[[package]] [[package]]
name = "minimal-lexical" name = "minimal-lexical"
version = "0.2.1" version = "0.2.1"
@ -964,6 +984,7 @@ version = "1.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed"
dependencies = [ dependencies = [
"bytes",
"libc", "libc",
"mio", "mio",
"pin-project-lite", "pin-project-lite",
@ -1013,6 +1034,31 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "tower-http"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08a05a66a4fdd61cbbe0a1d755ffe0ca6aba159dd4820936a0ff8a8278245b9c"
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",
]
[[package]] [[package]]
name = "tower-layer" name = "tower-layer"
version = "0.3.3" version = "0.3.3"

View file

@ -13,11 +13,13 @@ futures-util = { version = "0.3.34", features = ["sink"] }
http = { version = "1.5.0", optional = true } http = { version = "1.5.0", optional = true }
ldap3_proto = "0.8.1" ldap3_proto = "0.8.1"
minijinja = { version = "2.24.0", optional = true } minijinja = { version = "2.24.0", optional = true }
minijinja-embed = { version = "2.24.0", optional = true }
serde = { version = "1.0.229", features = ["derive"] } serde = { version = "1.0.229", features = ["derive"] }
static-serve = { version = "0.6.3", optional = true } static-serve = { version = "0.6.3", optional = true }
tokio = { version = "1.53.1", features = ["macros", "net", "rt", "time", "sync"] } tokio = { version = "1.53.1", features = ["macros", "net", "rt", "time", "sync"] }
tokio-util = { version = "0.7.19", features = ["codec"] } tokio-util = { version = "0.7.19", features = ["codec"] }
tower = "0.5.3" tower = "0.5.3"
tower-http = { version = "0.7.1", features = ["fs"], optional = true }
tracing = "0.1.44" tracing = "0.1.44"
tracing-subscriber = "0.3.23" tracing-subscriber = "0.3.23"
uuid = { version = "1.26.0", features = ["v4"] } uuid = { version = "1.26.0", features = ["v4"] }
@ -30,5 +32,12 @@ perf = "deny"
style = "deny" style = "deny"
[features] [features]
default = [ "http" ] default = [ "http", "noembed" ]
http = ["dep:axum", "dep:axum-extra", "dep:http", "dep:minijinja", "dep:static-serve"] # Embed assets and templates in binary
embed = [ "dep:minijinja-embed", "dep:static-serve" ]
# Don't embed assets and templates in binary
noembed = [ "dep:tower-http" ]
http = ["dep:axum", "dep:axum-extra", "dep:http", "dep:minijinja"]
[build-dependencies]
minijinja-embed = { version = "2.24.0", optional = true }

4
build.rs Normal file
View file

@ -0,0 +1,4 @@
fn main() {
#[cfg(feature = "embed")]
minijinja_embed::embed_templates!("templates");
}

View file

@ -65,10 +65,12 @@ impl DatabaseInterface for MemoryDatabase {
self.create_user(new_user).await self.create_user(new_user).await
} }
fn list_users(&self, domain: Option<String>) -> impl Future<Output = Result<Vec<User>, BoxedError>> { fn list_users(
&self,
domain: Option<String>,
) -> impl Future<Output = Result<Vec<User>, BoxedError>> {
let users = if let Some(domain) = domain { let users = if let Some(domain) = domain {
self self.users
.users
.iter() .iter()
.filter(|user| user.domain.as_ref() == Some(&domain)) .filter(|user| user.domain.as_ref() == Some(&domain))
.cloned() .cloned()

View file

@ -5,8 +5,13 @@ use axum::routing::{get, post};
use axum::serve::Listener as AxumListener; use axum::serve::Listener as AxumListener;
use axum_extra::extract::cookie::CookieJar; use axum_extra::extract::cookie::CookieJar;
use http::StatusCode; use http::StatusCode;
use minijinja::{Environment, context, path_loader}; #[cfg(not(feature = "embed"))]
use minijinja::path_loader;
use minijinja::{Environment, context};
#[cfg(feature = "embed")]
use static_serve::embed_assets; use static_serve::embed_assets;
#[cfg(not(feature = "embed"))]
use tower_http::services::ServeDir;
use crate::db::{Database, DatabaseInterface, Operation}; use crate::db::{Database, DatabaseInterface, Operation};
use crate::listener::{Listener, ListenerKind}; use crate::listener::{Listener, ListenerKind};
@ -67,6 +72,9 @@ pub struct HttpState<D: DatabaseInterface> {
impl<D: DatabaseInterface> HttpState<D> { impl<D: DatabaseInterface> HttpState<D> {
pub fn new(db: Database<D>) -> Self { pub fn new(db: Database<D>) -> Self {
let mut templates = Environment::new(); let mut templates = Environment::new();
#[cfg(feature = "embed")]
minijinja_embed::load_templates!(&mut templates);
#[cfg(not(feature = "embed"))]
templates.set_loader(path_loader("templates")); templates.set_loader(path_loader("templates"));
Self { Self {
db, db,
@ -77,9 +85,19 @@ impl<D: DatabaseInterface> HttpState<D> {
} }
pub async fn http_listen<D: DatabaseInterface>(listener: Listener, db: Database<D>) { pub async fn http_listen<D: DatabaseInterface>(listener: Listener, db: Database<D>) {
embed_assets!("assets"); #[cfg(all(feature = "embed", feature = "noembed"))]
let app = Router::new() compile_error!("You cannot have `embed` and `noembed` features enabled at the same time.");
.nest("/assets", static_router()) #[cfg(not(any(feature = "embed", feature = "noembed")))]
compile_error!("You must have `embed` or `noembed` feature enabled.");
#[cfg(feature = "embed")]
let app = {
embed_assets!("assets");
Router::new().nest("/assets", static_router())
};
#[cfg(not(feature = "embed"))]
let app = { Router::new().nest_service("/assets", ServeDir::new("assets")) };
let app = app
.route("/", get(home)) .route("/", get(home))
.route("/login", get(home)) .route("/login", get(home))
.route("/login", post(login::post_login)) .route("/login", post(login::post_login))