A little app to serve my photography from my personal website

render markdown

+118 -5
+103
Cargo.lock
··· 1111 1111 checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" 1112 1112 1113 1113 [[package]] 1114 + name = "markdown" 1115 + version = "1.0.0" 1116 + source = "registry+https://github.com/rust-lang/crates.io-index" 1117 + checksum = "a5cab8f2cadc416a82d2e783a1946388b31654d391d1c7d92cc1f03e295b1deb" 1118 + dependencies = [ 1119 + "unicode-id", 1120 + ] 1121 + 1122 + [[package]] 1123 + name = "markdown-frontmatter" 1124 + version = "0.4.0" 1125 + source = "registry+https://github.com/rust-lang/crates.io-index" 1126 + checksum = "7430fd66a1ba512543425144850284f8c821bc1e3e35cd6c13684823815d707a" 1127 + dependencies = [ 1128 + "serde", 1129 + "serde_json", 1130 + "serde_yaml", 1131 + "thiserror 2.0.17", 1132 + "toml", 1133 + ] 1134 + 1135 + [[package]] 1114 1136 name = "matchers" 1115 1137 version = "0.2.0" 1116 1138 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1440 1462 "chrono", 1441 1463 "dotenvy", 1442 1464 "init-tracing-opentelemetry", 1465 + "markdown", 1466 + "markdown-frontmatter", 1443 1467 "minijinja", 1444 1468 "minijinja-autoreload", 1445 1469 "serde", ··· 2011 2035 ] 2012 2036 2013 2037 [[package]] 2038 + name = "serde_spanned" 2039 + version = "1.0.3" 2040 + source = "registry+https://github.com/rust-lang/crates.io-index" 2041 + checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" 2042 + dependencies = [ 2043 + "serde_core", 2044 + ] 2045 + 2046 + [[package]] 2014 2047 name = "serde_urlencoded" 2015 2048 version = "0.7.1" 2016 2049 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2065 2098 dependencies = [ 2066 2099 "paste", 2067 2100 "regex", 2101 + ] 2102 + 2103 + [[package]] 2104 + name = "serde_yaml" 2105 + version = "0.9.34+deprecated" 2106 + source = "registry+https://github.com/rust-lang/crates.io-index" 2107 + checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" 2108 + dependencies = [ 2109 + "indexmap", 2110 + "itoa", 2111 + "ryu", 2112 + "serde", 2113 + "unsafe-libyaml", 2068 2114 ] 2069 2115 2070 2116 [[package]] ··· 2541 2587 ] 2542 2588 2543 2589 [[package]] 2590 + name = "toml" 2591 + version = "0.9.8" 2592 + source = "registry+https://github.com/rust-lang/crates.io-index" 2593 + checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" 2594 + dependencies = [ 2595 + "indexmap", 2596 + "serde_core", 2597 + "serde_spanned", 2598 + "toml_datetime", 2599 + "toml_parser", 2600 + "toml_writer", 2601 + "winnow", 2602 + ] 2603 + 2604 + [[package]] 2605 + name = "toml_datetime" 2606 + version = "0.7.3" 2607 + source = "registry+https://github.com/rust-lang/crates.io-index" 2608 + checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" 2609 + dependencies = [ 2610 + "serde_core", 2611 + ] 2612 + 2613 + [[package]] 2614 + name = "toml_parser" 2615 + version = "1.0.4" 2616 + source = "registry+https://github.com/rust-lang/crates.io-index" 2617 + checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" 2618 + dependencies = [ 2619 + "winnow", 2620 + ] 2621 + 2622 + [[package]] 2623 + name = "toml_writer" 2624 + version = "1.0.4" 2625 + source = "registry+https://github.com/rust-lang/crates.io-index" 2626 + checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" 2627 + 2628 + [[package]] 2544 2629 name = "tonic" 2545 2630 version = "0.14.2" 2546 2631 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2766 2851 checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" 2767 2852 2768 2853 [[package]] 2854 + name = "unicode-id" 2855 + version = "0.3.6" 2856 + source = "registry+https://github.com/rust-lang/crates.io-index" 2857 + checksum = "70ba288e709927c043cbe476718d37be306be53fb1fafecd0dbe36d072be2580" 2858 + 2859 + [[package]] 2769 2860 name = "unicode-ident" 2770 2861 version = "1.0.19" 2771 2862 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2791 2882 version = "1.12.0" 2792 2883 source = "registry+https://github.com/rust-lang/crates.io-index" 2793 2884 checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" 2885 + 2886 + [[package]] 2887 + name = "unsafe-libyaml" 2888 + version = "0.2.11" 2889 + source = "registry+https://github.com/rust-lang/crates.io-index" 2890 + checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" 2794 2891 2795 2892 [[package]] 2796 2893 name = "untrusted" ··· 3280 3377 version = "0.53.1" 3281 3378 source = "registry+https://github.com/rust-lang/crates.io-index" 3282 3379 checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" 3380 + 3381 + [[package]] 3382 + name = "winnow" 3383 + version = "0.7.14" 3384 + source = "registry+https://github.com/rust-lang/crates.io-index" 3385 + checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" 3283 3386 3284 3387 [[package]] 3285 3388 name = "wit-bindgen"
+2
Cargo.toml
··· 4 4 edition = "2024" 5 5 6 6 [dependencies] 7 + markdown = "1.0.0" 7 8 anyhow = "1.0.100" 8 9 axum = { version = "0.8.6", features = ["macros"] } 9 10 axum-extra = { version = "0.12.1", features = ["query"] } ··· 15 16 "tracing_subscriber_ext", 16 17 "metrics", 17 18 ] } 19 + markdown-frontmatter = "0.4.0" 18 20 minijinja = { version = "2.12.0", features = ["loader"] } 19 21 minijinja-autoreload = "2.12.0" 20 22 serde = "1.0.228"
+6 -1
src/app_error.rs
··· 16 16 17 17 #[error("internal server error")] 18 18 Anyhow(#[from] anyhow::Error), 19 + 20 + #[error("internal server error")] 21 + IoError(#[from] std::io::Error), 19 22 } 20 23 21 24 impl AppError { ··· 23 26 match self { 24 27 Self::NotFound => StatusCode::NOT_FOUND, 25 28 Self::ValidationError(_) => StatusCode::BAD_REQUEST, 26 - Self::DbError(_) | Self::Anyhow(_) => StatusCode::INTERNAL_SERVER_ERROR, 29 + Self::DbError(_) | Self::Anyhow(_) | Self::IoError(_) => { 30 + StatusCode::INTERNAL_SERVER_ERROR 31 + } 27 32 } 28 33 } 29 34 }
+6 -3
src/routes/blog/show.rs
··· 1 - use crate::{AppState, Response, templates::render}; 1 + use crate::{AppState, Response, app_error::AppError, templates::render}; 2 2 use axum::{ 3 3 extract::{Path, State}, 4 4 response::Html, 5 5 }; 6 6 use minijinja::context; 7 - use std::sync::Arc; 7 + use std::{fs::read_to_string, sync::Arc}; 8 8 9 9 pub async fn show(Path(slug): Path<String>, State(state): State<Arc<AppState>>) -> Response { 10 + let path = state.blog_slugs.get(&slug).ok_or(AppError::NotFound)?; 11 + let md = read_to_string(path)?; 12 + let body = markdown::to_html(&md); 10 13 let rendered = render( 11 14 &state.reloader, 12 15 "blog/show", 13 16 context! { 14 - slug 17 + body 15 18 }, 16 19 )?; 17 20
+1 -1
templates/blog/show.jinja
··· 3 3 {% block title %}Blog{% endblock %} 4 4 5 5 {% block body %} 6 - {{ slug }} 6 + {{ body }} 7 7 {% endblock %}