Per-site favicon via site.yaml

A content repo can ship a favicon (svg/png/ico) named in site.yaml;
portal serves it at /site/<path> and injects a <link rel=icon> after
the static defaults so it wins. Also serves .ico assets.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y42TyF8Zu7NGRR2893vNcZ
This commit is contained in:
Bendik Aagaard Lynghaug
2026-09-01 22:12:20 +02:00
co-authored by Claude Fable 5
parent b25a5839b0
commit 01504fb120
3 changed files with 24 additions and 2 deletions
+13
View File
@@ -611,6 +611,10 @@ pub struct SiteConfig {
/// same-origin at `/site/<path>`; plain path only.
#[serde(default)]
pub stylesheet: Option<String>,
/// A content-repo-relative favicon (svg/png/ico), served at
/// `/site/<path>` and used in place of the built-in one.
#[serde(default)]
pub favicon: Option<String>,
#[serde(default)]
pub hero: HeroConfig,
}
@@ -673,6 +677,14 @@ impl SiteConfig {
);
}
}
if let Some(icon) = &self.favicon {
let ok = [".svg", ".png", ".ico"].iter().any(|e| icon.ends_with(e));
if !is_safe_site_path(icon) || !ok {
anyhow::bail!(
"site.yaml: favicon {icon:?} must be a plain repo-relative .svg/.png/.ico path"
);
}
}
Ok(())
}
}
@@ -1537,6 +1549,7 @@ pub async fn site_asset_handler(
Some("png") => "image/png",
Some("webp") => "image/webp",
Some("avif") => "image/avif",
Some("ico") => "image/x-icon",
Some("woff2") => "font/woff2",
_ => "application/octet-stream",
};