use std::{collections::HashMap, sync::Arc}; use axum::{body, response::Response, routing::get, Extension, Router}; use color_eyre::eyre::{Error, Result}; use hyper::header::CONTENT_TYPE; use post::Post; use prometheus::{Encoder, TextEncoder}; use tera::Tera; use tower_http::{compression::CompressionLayer, trace::TraceLayer}; use tracing::{instrument, log::*}; mod handlers; mod post; pub struct State { posts: HashMap, tera: Tera, } #[tokio::main] async fn main() -> Result<()> { color_eyre::install()?; tracing_subscriber::fmt::init(); info!("Starting server..."); let app = init_app().await?; info!("Now listening at http://localhost:8180"); axum::Server::bind(&"0.0.0.0:8180".parse().unwrap()) .serve(app.into_make_service()) .await?; Ok(()) } #[instrument] pub async fn init_app() -> Result { let tera = Tera::new("templates/**/*")?; let posts = post::load_all().await?; let state = Arc::new(State { tera, posts }); let middleware = tower::ServiceBuilder::new() .layer(TraceLayer::new_for_http()) .layer(Extension(state)) .layer(CompressionLayer::new()); let app = Router::new() .route("/", get(handlers::index)) .nest("/posts", handlers::posts::router()) .nest_service("/static", tower_http::services::ServeDir::new("./static")) .route("/healthcheck", get(healthcheck)) .route("/metrics", get(metrics)) .layer(middleware); Ok(app) } async fn healthcheck() -> &'static str { "OK" } async fn metrics() -> Response { let encoder = TextEncoder::new(); let metric_families = prometheus::gather(); let mut buffer = vec![]; encoder.encode(&metric_families, &mut buffer).unwrap(); Response::builder() .status(200) .header(CONTENT_TYPE, encoder.format_type()) .body(body::boxed(body::Full::from(buffer))) .unwrap() } #[derive(Debug)] pub enum WebsiteError { NotFound, InternalError(Error), } impl From for WebsiteError where E: Into, { fn from(value: E) -> Self { WebsiteError::InternalError(value.into()) } }