use std::sync::Arc; use axum::{extract::{Path, State}, response::{Html, Redirect}, routing::get, Extension, Router}; use serde_derive::Serialize; use tracing::{instrument, log::*}; use crate::{ post::{render_post, Post}, AppState, WebsiteError, }; pub fn router() -> Router> { Router::new() .route("/", get(index)) .route("/:slug/", get(view)) .route("/:slug/index.md", get(super::not_found)) .fallback_service(tower_http::services::ServeDir::new("./posts")) } pub fn alias_router<'a>(posts: impl IntoIterator) -> Router> { let mut router = Router::new(); for post in posts { for alias in &post.aliases { let path = post.absolute_path.to_owned(); router = router.route(alias, get(move || async { let path = path; Redirect::permanent(&path) })); } } router } #[derive(Serialize, Debug)] struct PageContext<'a> { title: &'a str, } #[instrument(skip(state))] pub async fn index(State(state): State>) -> Result, WebsiteError> { let mut posts: Vec<&Post> = state .posts .values() .filter(|p| p.is_published()) .collect(); posts.sort_by_key(|p| &p.date); posts.reverse(); let ctx = PageContext { title: "Posts", }; let mut c = tera::Context::new(); c.insert("page", &ctx); c.insert("posts", &posts); let res = state.tera .render("posts_index.html", &c)?; Ok(Html(res)) } #[instrument(skip(state))] pub async fn view( Path(slug): Path, State(state): State>, ) -> Result, WebsiteError> { let post = state.posts.get(&slug).ok_or(WebsiteError::NotFound)?; if !post.is_published() { warn!("attempted to view post before it has been published!"); return Err(WebsiteError::NotFound); } let res = render_post(&state.tera, post).await?; Ok(Html(res)) } #[cfg(test)] mod tests { use chrono::DateTime; use crate::post::Post; use super::PageContext; #[test] fn render_index() { let posts = vec![Post { title: "test".into(), slug: "test".into(), date: Some(DateTime::parse_from_rfc3339("2023-03-26T13:04:01+02:00").unwrap()), ..Default::default() }, Post { title: "test2".into(), slug: "test2".into(), date: None, ..Default::default() }]; let page = PageContext { title: "Posts", }; let mut ctx = tera::Context::new(); ctx.insert("page", &page); ctx.insert("posts", &posts); let tera = tera::Tera::new("templates/**/*").unwrap(); let _res = tera .render( "posts_index.html", &ctx, ) .unwrap(); } }