diff --git a/Cargo.lock b/Cargo.lock index a7b404a..94d8eb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -168,6 +168,7 @@ dependencies = [ "libshire", "maud", "miette", + "mime", "notify", "pulldown-cmark", "rss", diff --git a/blog_server/Cargo.toml b/blog_server/Cargo.toml index 227a341..95d7444 100644 --- a/blog_server/Cargo.toml +++ b/blog_server/Cargo.toml @@ -13,6 +13,8 @@ axum = "0.5" # Middleware for the web server tower = { version = "0.4", features = ["limit"] } tower-http = { version = "0.3", features = ["fs", "trace"] } +# MIME type implementation +mime = "0.3" # Compile-time HTTP templating maud = "0.23" # Serialisation for RSS and Atom diff --git a/blog_server/src/main.rs b/blog_server/src/main.rs index 942f429..120facb 100644 --- a/blog_server/src/main.rs +++ b/blog_server/src/main.rs @@ -27,6 +27,8 @@ pub struct Config { #[knuffel(child, unwrap(argument))] static_dir: PathBuf, #[knuffel(child, unwrap(argument))] + favicon_dir: PathBuf, + #[knuffel(child, unwrap(argument))] posts_dir: PathBuf, #[knuffel(child, unwrap(argument))] post_media_dir: PathBuf, diff --git a/blog_server/src/service/response.rs b/blog_server/src/service/response.rs index a204d80..379f4a3 100644 --- a/blog_server/src/service/response.rs +++ b/blog_server/src/service/response.rs @@ -131,9 +131,17 @@ impl IntoResponse for Html { html { head { meta charset="utf-8"; + meta name="robots" content=(self.crawler_hints); meta name="viewport" content="width=device-width, initial-scale=1"; + title { (self.title) } + + link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"; + link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"; + link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"; + link rel="manifest" href="/site.webmanifest"; + @if let Some(head) = self.head { (head) } diff --git a/blog_server/src/service/site.rs b/blog_server/src/service/site.rs index d102de7..591a9cc 100644 --- a/blog_server/src/service/site.rs +++ b/blog_server/src/service/site.rs @@ -33,13 +33,20 @@ pub fn service( { Router::new() .route("/", get(index::handle)) - .route("/rss.xml", get(rss::handle)) - .route("/atom.xml", get(atom::handle)) .route("/contact", get(contact::handle)) .route("/articles", get(posts_list::handle)) + .route("/rss.xml", get(rss::handle)) + .route("/atom.xml", get(atom::handle)) .route("/articles/:post_id", get(post::handle)) - .nest("/static", static_content::service(&config.static_dir)) - .nest("/article_media", static_content::service(&config.post_media_dir)) + .route("/favicon.ico", static_content::file_service(&config.favicon_dir.join("favicon.ico"), None)) + .route("/favicon-16x16.png", static_content::file_service(&config.favicon_dir.join("favicon-16x16.png"), None)) + .route("/favicon-32x32.png", static_content::file_service(&config.favicon_dir.join("favicon-32x32.png"), None)) + .route("/apple-touch-icon.png", static_content::file_service(&config.favicon_dir.join("apple-touch-icon.png"), None)) + .route("/android-chrome-192x192.png", static_content::file_service(&config.favicon_dir.join("android-chrome-192x192.png"), None)) + .route("/android-chrome-512x512.png", static_content::file_service(&config.favicon_dir.join("android-chrome-512x512.png"), None)) + .route("/site.webmanifest", static_content::file_service(&config.favicon_dir.join("site.webmanifest"), None)) + .nest("/static", static_content::dir_service(&config.static_dir)) + .nest("/article_media", static_content::dir_service(&config.post_media_dir)) .fallback(handle_fallback.into_service()) .layer(ConcurrencyLimitLayer::new(config.concurrency_limit)) .layer(TraceLayer::new_for_http()) diff --git a/blog_server/src/service/static_content.rs b/blog_server/src/service/static_content.rs index b037049..b1b4fa7 100644 --- a/blog_server/src/service/static_content.rs +++ b/blog_server/src/service/static_content.rs @@ -11,18 +11,29 @@ use axum::{ routing::{get_service, MethodRouter}, }; use libshire::convert::Empty; +use mime::Mime; use tower::ServiceExt; -use tower_http::services::ServeDir; +use tower_http::services::{ServeDir, ServeFile}; use tracing::{info, error}; use super::response::Error; -pub fn service(static_dir: &Path) -> MethodRouter
{ +pub fn file_service(file_path: &Path, mime: Option<&Mime>) -> MethodRouter { + let serve_file = match mime { + Some(mime) => ServeFile::new_with_mime(file_path, mime), + None => ServeFile::new(file_path), + }; + + get_service(serve_file) + .handle_error(handle_error) +} + +pub fn dir_service(dir_path: &Path) -> MethodRouter { let fallback_service = handle_fallback .into_service() .map_err(Empty::elim::