repos

blog.bythewood.me-rust

mirror archived upstream

Single-binary self-hosted Markdown blog on Rust axum: no database, live search, Typst PDF export, and strong SEO.

axumblogdockermarkdownminijinjarustself-hostedtypstvite

1.4 KB · 54 lines · Rust Raw History
 1use axum::{
 2    extract::State,
 3    http::{HeaderMap, Uri},
 4    response::Html,
 5    routing::get,
 6    Router,
 7};
 8use minijinja::context;
 9use rand::seq::SliceRandom;
10
11use crate::app::AppState;
12use crate::error::AppError;
13use crate::posts::{self, Post};
14use crate::render::{build_request, render_html};
15
16pub fn router() -> Router<AppState> {
17    Router::new().route("/", get(index))
18}
19
20async fn index(
21    State(state): State<AppState>,
22    uri: Uri,
23    headers: HeaderMap,
24) -> Result<Html<String>, AppError> {
25    let request = build_request(&uri, &headers);
26    let published = posts::published(&state.posts);
27    let latest_post = published.first().cloned();
28    let rest: Vec<Post> = match &latest_post {
29        Some(latest) => published
30            .iter()
31            .filter(|p| p.slug != latest.slug)
32            .cloned()
33            .collect(),
34        None => Vec::new(),
35    };
36    let mut rng = rand::thread_rng();
37    let mut shuffled = rest;
38    shuffled.shuffle(&mut rng);
39    let random_blog_posts: Vec<Post> = shuffled.into_iter().take(3).collect();
40
41    let page = context! {
42        title => "Isaac Bythewood's Blog",
43        slug => "home",
44        description => "Writing about webdev, infrastructure, security, and tooling by Isaac Bythewood, a Senior Solutions Architect in Elkin, NC.",
45    };
46
47    render_html(
48        &state,
49        "home.html",
50        context! { page, latest_post, random_blog_posts },
51        &request,
52    )
53}