1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
use mini_static::{Server, SlideOptions, SpaTransition};
use std::env;
use std::path::Path;
use std::time::Duration;
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let port = env::var("PORT")
.unwrap_or_else(|_| "8080".to_string())
.parse::<u16>()?;
let root = env::var("ROOT").unwrap_or_else(|_| "./public".to_string());
let root_path = Path::new(&root);
// `with_immutable_assets` opts fingerprinted filenames (`vendor.a1b2c3.js`) into a
// year-long, cacheable-forever `Cache-Control`, since a content change would produce
// a new filename rather than mutating this one. Everything else keeps the default
// `no-cache` (see `Server::with_immutable_assets`).
//
// `with_spa_root("#app")` demos spa-mode navigation (see `public/spa-demo/`) —
// unlike the CSS/JS bundling below, this has no external-tool dependency to gate
// on, so it's simply always on here. `SpaTransition::Slide` demos the built-in
// slide animation (no site CSS needed) instead of the default cross-fade.
let server = Server::new(root_path)?
.with_immutable_assets(|path| {
path.file_name()
.and_then(|name| name.to_str())
.is_some_and(|name| name.contains(".a1b2c3."))
})
.with_spa_root("#app")
.with_spa_transition(SpaTransition::Slide(SlideOptions::default()));
// CSS/JS bundling moved out of this crate in 0.29.0 — see `mini-build`, which
// produces the directory this server serves. Run it before (or alongside) this
// example rather than expecting the server to build anything itself.
#[cfg_attr(not(debug_assertions), allow(unused_mut))]
let mut server = server;
// Live-reload (background file watcher, SSE stream, injected reload script — see
// `Server::with_live_reload`) is only enabled in debug builds, matching the
// convention `mini-unified`'s `add_reload_route` uses: a release build never pays
// for the watcher or ships the injected script.
#[cfg(debug_assertions)]
{
server = server.with_live_reload();
}
// Bind to all interfaces (0.0.0.0) so the server is accessible from outside
// (e.g., from the host when running in Docker)
let (_port, handle) = server.run_all(port, Duration::from_secs(30)).await?;
println!("mini-static listening on 0.0.0.0:{}", port);
println!("serving files from: {}", root);
println!("immutable caching enabled for *.a1b2c3.* (see vendor.a1b2c3.js)");
println!("precompressed sidecar demo: bundle.js / bundle.js.gz");
println!("spa-mode demo (root #app): http://127.0.0.1:{port}/spa-demo/");
#[cfg(debug_assertions)]
println!("live-reload enabled at {}", mini_static::LIVE_RELOAD_PATH);
// Keep the server running until interrupted
tokio::signal::ctrl_c().await?;
println!("shutting down...");
handle.shutdown().await;
Ok(())
}