use std::{path::PathBuf, sync::Arc}; use axum::{routing::{get, get_service}, Extension, Router, handler::Handler, http::StatusCode}; use color_eyre::eyre::Result; use glob::glob; use serde_derive::Serialize; use tera::Tera; use tower_http::trace::TraceLayer; use tracing::log::*; mod handlers; pub struct State { posts: Vec, tera: Tera, } #[derive(Serialize)] pub struct Post { pub slug: String, pub content: String, } #[tokio::main] async fn main() -> Result<()> { color_eyre::install()?; tracing_subscriber::fmt::init(); info!("Starting server..."); let tera = Tera::new("templates/**/*")?; let posts = glob("posts/**/*.md")? .map(|p| { let path = p.unwrap(); let filename = path .file_name() .unwrap() .to_string_lossy(); let (filename, _) = filename.rsplit_once('.') .unwrap(); let slug = if filename.eq_ignore_ascii_case("index") { path.parent().unwrap().file_name().unwrap().to_string_lossy().into_owned() } else { filename.to_owned() }; Post { slug, content: std::fs::read_to_string(&path).unwrap(), } }) .collect(); let state = Arc::new(State { tera, posts }); let middleware = tower::ServiceBuilder::new() .layer(TraceLayer::new_for_http()) .layer(Extension(state.clone())); let app = Router::new() .route("/", get(handlers::index)) .route("/posts/", get(handlers::posts::index)) .route("/posts/:slug/", get(handlers::posts::view)) .nest("/static", get_service(tower_http::services::ServeDir::new("./static")).handle_error(|e| async { (StatusCode::NOT_FOUND, "not found") })) .fallback((|| async { (StatusCode::NOT_FOUND, "not found") }).into_service()) .layer(middleware); info!("Now listening at http://localhost:8180"); axum::Server::bind(&"0.0.0.0:8180".parse().unwrap()) .serve(app.into_make_service()) .await?; Ok(()) } async fn healthcheck() -> &'static str { "OK" }