cache-bust css via content-hash ?v; keycap button style

This commit is contained in:
Jean Chevronnet 2026-08-31 01:35:59 +00:00
parent c3c9e08a9e
commit 55c6685df1
No known key found for this signature in database
GPG key ID: 439666D63A9477E4
4 changed files with 47 additions and 14 deletions

View file

@ -15,8 +15,27 @@ use axum::{
Router,
};
use std::net::SocketAddr;
use std::sync::OnceLock;
use tower_http::{compression::CompressionLayer, trace::TraceLayer};
/// Short content hash of the bundled CSS, used as a `?v=` cache-buster: a changed
/// stylesheet gets a new URL (fetched fresh), an unchanged one stays cached.
fn asset_ver() -> &'static str {
static V: OnceLock<String> = OnceLock::new();
V.get_or_init(|| {
let mut h: u64 = 0xcbf29ce484222325;
for b in include_str!("../static/style.css")
.bytes()
.chain(include_str!("../static/docs.css").bytes())
{
h ^= b as u64;
h = h.wrapping_mul(0x100000001b3);
}
format!("{h:x}")
})
.as_str()
}
/// Display-ready snapshot of the live network state for templates.
struct Status {
online: bool,
@ -48,6 +67,7 @@ struct AppState {
#[template(path = "index.html")]
struct IndexTemplate {
active: &'static str,
css_v: &'static str,
status: Status,
commits: Vec<commits::Commit>,
}
@ -56,18 +76,21 @@ struct IndexTemplate {
#[template(path = "features.html")]
struct FeaturesTemplate {
active: &'static str,
css_v: &'static str,
}
#[derive(Template)]
#[template(path = "connect.html")]
struct ConnectTemplate {
active: &'static str,
css_v: &'static str,
}
#[derive(Template)]
#[template(path = "services.html")]
struct ServicesTemplate {
active: &'static str,
css_v: &'static str,
}
struct SidePage {
@ -84,6 +107,7 @@ struct SideSection {
#[template(path = "docs.html")]
struct DocsTemplate {
active: &'static str,
css_v: &'static str,
page_title: &'static str,
body: &'static str,
toc: &'static [docs::Heading],
@ -104,16 +128,16 @@ fn page<T: Template>(t: T) -> Response {
async fn index(State(st): State<AppState>) -> Response {
let commits = st.commits.read().ok().map(|g| g.clone()).unwrap_or_default();
page(IndexTemplate { active: "home", status: view(&st.stats), commits })
page(IndexTemplate { active: "home", css_v: asset_ver(), status: view(&st.stats), commits })
}
async fn features() -> Response {
page(FeaturesTemplate { active: "features" })
page(FeaturesTemplate { active: "features", css_v: asset_ver() })
}
async fn connect() -> Response {
page(ConnectTemplate { active: "connect" })
page(ConnectTemplate { active: "connect", css_v: asset_ver() })
}
async fn services() -> Response {
page(ServicesTemplate { active: "services" })
page(ServicesTemplate { active: "services", css_v: asset_ver() })
}
async fn docs_index() -> Response {
@ -137,6 +161,7 @@ async fn docs_page(Path(slug): Path<String>) -> Response {
let (prev, next) = docs::neighbours(&slug);
page(DocsTemplate {
active: "docs",
css_v: asset_ver(),
page_title: p.title,
body: &p.html,
toc: &p.toc,
@ -154,13 +179,19 @@ async fn docs_search() -> impl IntoResponse {
async fn style() -> impl IntoResponse {
(
[(header::CONTENT_TYPE, "text/css; charset=utf-8")],
[
(header::CONTENT_TYPE, "text/css; charset=utf-8"),
(header::CACHE_CONTROL, "public, max-age=31536000, immutable"),
],
include_str!("../static/style.css"),
)
}
async fn docs_css() -> impl IntoResponse {
(
[(header::CONTENT_TYPE, "text/css; charset=utf-8")],
[
(header::CONTENT_TYPE, "text/css; charset=utf-8"),
(header::CACHE_CONTROL, "public, max-age=31536000, immutable"),
],
include_str!("../static/docs.css"),
)
}