diff options
| author | Xe Iaso <me@christine.website> | 2022-03-21 20:14:14 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-03-21 20:14:14 -0400 |
| commit | 8b747c1c40876c6668191594eddcb260199cdb7f (patch) | |
| tree | 86edb9bc382751c6a32f5f2946ff235ea06674ba /src/handlers | |
| parent | f45ca40ae1052d46611ff2f27ad281695afc4f8f (diff) | |
| download | xesite-8b747c1c40876c6668191594eddcb260199cdb7f.tar.xz xesite-8b747c1c40876c6668191594eddcb260199cdb7f.zip | |
Rewrite the site routing with Axum (#441)
* broken state
Signed-off-by: Xe Iaso <me@christine.website>
* fix???
Signed-off-by: Xe Iaso <me@christine.website>
* Port everything else to axum
Signed-off-by: Xe <me@christine.website>
* headers
Signed-off-by: Xe Iaso <me@christine.website>
* site update post
Signed-off-by: Christine Dodrill <me@christine.website>
* fix headers
Signed-off-by: Xe Iaso <me@christine.website>
* remove warp example
Signed-off-by: Xe Iaso <me@christine.website>
* 80c wrap
Signed-off-by: Xe Iaso <me@christine.website>
* bump version
Signed-off-by: Xe Iaso <me@christine.website>
Diffstat (limited to 'src/handlers')
| -rw-r--r-- | src/handlers/blog.rs | 57 | ||||
| -rw-r--r-- | src/handlers/feeds.rs | 95 | ||||
| -rw-r--r-- | src/handlers/gallery.rs | 30 | ||||
| -rw-r--r-- | src/handlers/mod.rs | 169 | ||||
| -rw-r--r-- | src/handlers/talks.rs | 30 |
5 files changed, 162 insertions, 219 deletions
diff --git a/src/handlers/blog.rs b/src/handlers/blog.rs index 007b8e0..f6aae06 100644 --- a/src/handlers/blog.rs +++ b/src/handlers/blog.rs @@ -1,14 +1,13 @@ -use super::{PostNotFound, SeriesNotFound, LAST_MODIFIED}; -use crate::{ - app::State, - post::Post, - templates::{self, Html, RenderRucte}, +use super::Result; +use crate::{app::State, post::Post, templates}; +use axum::{ + extract::{Extension, Path}, + response::Html, }; use lazy_static::lazy_static; use prometheus::{opts, register_int_counter_vec, IntCounterVec}; use std::sync::Arc; use tracing::{error, instrument}; -use warp::{http::Response, Rejection, Reply}; lazy_static! { static ref HIT_COUNTER: IntCounterVec = register_int_counter_vec!( @@ -19,17 +18,18 @@ lazy_static! { } #[instrument(skip(state))] -pub async fn index(state: Arc<State>) -> Result<impl Reply, Rejection> { +pub async fn index(Extension(state): Extension<Arc<State>>) -> Result { let state = state.clone(); - Response::builder() - .header("Last-Modified", &*LAST_MODIFIED) - .html(|o| templates::blogindex_html(o, state.blog.clone())) + let mut result: Vec<u8> = vec![]; + templates::blogindex_html(&mut result, state.blog.clone())?; + Ok(Html(result)) } #[instrument(skip(state))] -pub async fn series(state: Arc<State>) -> Result<impl Reply, Rejection> { +pub async fn series(Extension(state): Extension<Arc<State>>) -> Result { let state = state.clone(); let mut series: Vec<String> = vec![]; + let mut result: Vec<u8> = vec![]; for post in &state.blog { if post.front_matter.series.is_some() { @@ -40,15 +40,18 @@ pub async fn series(state: Arc<State>) -> Result<impl Reply, Rejection> { series.sort(); series.dedup(); - Response::builder() - .header("Last-Modified", &*LAST_MODIFIED) - .html(|o| templates::series_html(o, series)) + templates::series_html(&mut result, series)?; + Ok(Html(result)) } #[instrument(skip(state))] -pub async fn series_view(series: String, state: Arc<State>) -> Result<impl Reply, Rejection> { +pub async fn series_view( + Path(series): Path<String>, + Extension(state): Extension<Arc<State>>, +) -> Result { let state = state.clone(); let mut posts: Vec<Post> = vec![]; + let mut result: Vec<u8> = vec![]; for post in &state.blog { if post.front_matter.series.is_none() { @@ -62,16 +65,18 @@ pub async fn series_view(series: String, state: Arc<State>) -> Result<impl Reply if posts.len() == 0 { error!("series not found"); - Err(SeriesNotFound(series).into()) - } else { - Response::builder() - .header("Last-Modified", &*LAST_MODIFIED) - .html(|o| templates::series_posts_html(o, series, &posts)) + return Err(super::Error::SeriesNotFound(series)); } + + templates::series_posts_html(&mut result, series, &posts).unwrap(); + Ok(Html(result)) } #[instrument(skip(state))] -pub async fn post_view(name: String, state: Arc<State>) -> Result<impl Reply, Rejection> { +pub async fn post_view( + Path(name): Path<String>, + Extension(state): Extension<Arc<State>>, +) -> Result { let mut want: Option<Post> = None; for post in &state.blog { @@ -81,15 +86,15 @@ pub async fn post_view(name: String, state: Arc<State>) -> Result<impl Reply, Re } match want { - None => Err(PostNotFound("blog".into(), name).into()), + None => Err(super::Error::PostNotFound(name)), Some(post) => { HIT_COUNTER .with_label_values(&[name.clone().as_str()]) .inc(); - let body = Html(post.body_html.clone()); - Response::builder() - .header("Last-Modified", &*LAST_MODIFIED) - .html(|o| templates::blogpost_html(o, post, body)) + let body = templates::Html(post.body_html.clone()); + let mut result: Vec<u8> = vec![]; + templates::blogpost_html(&mut result, post, body)?; + Ok(Html(result)) } } } diff --git a/src/handlers/feeds.rs b/src/handlers/feeds.rs index 3d540f6..c69e2f0 100644 --- a/src/handlers/feeds.rs +++ b/src/handlers/feeds.rs @@ -1,10 +1,14 @@ -use super::LAST_MODIFIED; -use crate::{app::State, post::Post, templates}; +use super::{Result, LAST_MODIFIED}; +use crate::{ + app::State, + post::{NewPost, Post}, + templates, +}; +use axum::{body, extract::Extension, response::Response, Json}; use lazy_static::lazy_static; use prometheus::{opts, register_int_counter_vec, IntCounterVec}; -use std::{io, sync::Arc}; +use std::sync::Arc; use tracing::instrument; -use warp::{http::Response, Rejection, Reply}; lazy_static! { pub static ref HIT_COUNTER: IntCounterVec = register_int_counter_vec!( @@ -16,103 +20,56 @@ lazy_static! { } #[instrument(skip(state))] -pub async fn jsonfeed(state: Arc<State>, since: Option<String>) -> Result<impl Reply, Rejection> { +pub async fn jsonfeed(Extension(state): Extension<Arc<State>>) -> Json<jsonfeed::Feed> { HIT_COUNTER.with_label_values(&["json"]).inc(); let state = state.clone(); - Ok(warp::reply::json(&state.jf)) + Json(state.jf.clone()) } #[instrument(skip(state))] -pub async fn new_post(state: Arc<State>) -> Result<impl Reply, Rejection> { +#[axum_macros::debug_handler] +pub async fn new_post(Extension(state): Extension<Arc<State>>) -> Result<Json<NewPost>> { let state = state.clone(); - let everything = state.everything.clone(); - let p: &Post = everything.iter().next().unwrap(); - Ok(warp::reply::json(&p.new_post)) + let p: Post = state.everything.iter().next().unwrap().clone(); + Ok(Json(p.new_post)) } -#[derive(Debug)] -pub enum RenderError { - Build(warp::http::Error), - IO(io::Error), -} - -impl warp::reject::Reject for RenderError {} - #[instrument(skip(state))] -pub async fn atom(state: Arc<State>, since: Option<String>) -> Result<impl Reply, Rejection> { - if let Some(etag) = since { - if etag == ETAG.clone() { - return Response::builder() - .status(304) - .header("Content-Type", "text/plain") - .body( - "You already have the newest version of this feed." - .to_string() - .into_bytes(), - ) - .map_err(RenderError::Build) - .map_err(warp::reject::custom); - } - } - +pub async fn atom(Extension(state): Extension<Arc<State>>) -> Result<Response> { HIT_COUNTER.with_label_values(&["atom"]).inc(); let state = state.clone(); let mut buf = Vec::new(); - templates::blog_atom_xml(&mut buf, state.everything.clone()) - .map_err(RenderError::IO) - .map_err(warp::reject::custom)?; - Response::builder() + templates::blog_atom_xml(&mut buf, state.everything.clone())?; + Ok(Response::builder() .status(200) .header("Content-Type", "application/atom+xml") .header("ETag", ETAG.clone()) .header("Last-Modified", &*LAST_MODIFIED) - .body(buf) - .map_err(RenderError::Build) - .map_err(warp::reject::custom) + .body(body::boxed(body::Full::from(buf)))?) } #[instrument(skip(state))] -pub async fn rss(state: Arc<State>, since: Option<String>) -> Result<impl Reply, Rejection> { - if let Some(etag) = since { - if etag == ETAG.clone() { - return Response::builder() - .status(304) - .header("Content-Type", "text/plain") - .body( - "You already have the newest version of this feed." - .to_string() - .into_bytes(), - ) - .map_err(RenderError::Build) - .map_err(warp::reject::custom); - } - } - +pub async fn rss(Extension(state): Extension<Arc<State>>) -> Result<Response> { HIT_COUNTER.with_label_values(&["rss"]).inc(); let state = state.clone(); let mut buf = Vec::new(); - templates::blog_rss_xml(&mut buf, state.everything.clone()) - .map_err(RenderError::IO) - .map_err(warp::reject::custom)?; - Response::builder() + templates::blog_rss_xml(&mut buf, state.everything.clone())?; + Ok(Response::builder() .status(200) .header("Content-Type", "application/rss+xml") .header("ETag", ETAG.clone()) .header("Last-Modified", &*LAST_MODIFIED) - .body(buf) - .map_err(RenderError::Build) - .map_err(warp::reject::custom) + .body(body::boxed(body::Full::from(buf)))?) } #[instrument(skip(state))] -pub async fn sitemap(state: Arc<State>) -> Result<impl Reply, Rejection> { +#[axum_macros::debug_handler] +pub async fn sitemap(Extension(state): Extension<Arc<State>>) -> Result<Response> { HIT_COUNTER.with_label_values(&["sitemap"]).inc(); let state = state.clone(); - Response::builder() + Ok(Response::builder() .status(200) .header("Content-Type", "application/xml") .header("Last-Modified", &*LAST_MODIFIED) - .body(state.sitemap.clone()) - .map_err(RenderError::Build) - .map_err(warp::reject::custom) + .body(body::boxed(body::Full::from(state.sitemap.clone())))?) } diff --git a/src/handlers/gallery.rs b/src/handlers/gallery.rs index 02bc01b..ae6c411 100644 --- a/src/handlers/gallery.rs +++ b/src/handlers/gallery.rs @@ -1,14 +1,13 @@ -use super::PostNotFound; -use crate::{ - app::State, - post::Post, - templates::{self, Html, RenderRucte}, +use super::{Error::*, Result}; +use crate::{app::State, post::Post, templates}; +use axum::{ + extract::{Extension, Path}, + response::Html, }; use lazy_static::lazy_static; use prometheus::{opts, register_int_counter_vec, IntCounterVec}; use std::sync::Arc; use tracing::instrument; -use warp::{http::Response, Rejection, Reply}; lazy_static! { static ref HIT_COUNTER: IntCounterVec = register_int_counter_vec!( @@ -19,13 +18,18 @@ lazy_static! { } #[instrument(skip(state))] -pub async fn index(state: Arc<State>) -> Result<impl Reply, Rejection> { +pub async fn index(Extension(state): Extension<Arc<State>>) -> Result { let state = state.clone(); - Response::builder().html(|o| templates::galleryindex_html(o, state.gallery.clone())) + let mut result: Vec<u8> = vec![]; + templates::galleryindex_html(&mut result, state.gallery.clone())?; + Ok(Html(result)) } #[instrument(skip(state))] -pub async fn post_view(name: String, state: Arc<State>) -> Result<impl Reply, Rejection> { +pub async fn post_view( + Path(name): Path<String>, + Extension(state): Extension<Arc<State>>, +) -> Result { let mut want: Option<Post> = None; for post in &state.gallery { @@ -35,13 +39,15 @@ pub async fn post_view(name: String, state: Arc<State>) -> Result<impl Reply, Re } match want { - None => Err(PostNotFound("gallery".into(), name).into()), + None => Err(PostNotFound(name)), Some(post) => { HIT_COUNTER .with_label_values(&[name.clone().as_str()]) .inc(); - let body = Html(post.body_html.clone()); - Response::builder().html(|o| templates::gallerypost_html(o, post, body)) + let body = templates::Html(post.body_html.clone()); + let mut result: Vec<u8> = vec![]; + templates::gallerypost_html(&mut result, post, body)?; + Ok(Html(result)) } } } diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index 798a911..07c4d52 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -1,16 +1,20 @@ -use crate::{ - app::State, - templates::{self, Html, RenderRucte}, +use crate::{app::State, templates}; +use axum::{ + body, + extract::Extension, + http::StatusCode, + response::{Html, IntoResponse, Response}, }; use chrono::{Datelike, Timelike, Utc}; use lazy_static::lazy_static; use prometheus::{opts, register_int_counter_vec, IntCounterVec}; -use std::{convert::Infallible, fmt, sync::Arc}; +use std::sync::Arc; use tracing::instrument; -use warp::{ - http::{Response, StatusCode}, - Rejection, Reply, -}; + +pub mod blog; +pub mod feeds; +pub mod gallery; +pub mod talks; lazy_static! { static ref HIT_COUNTER: IntCounterVec = @@ -32,139 +36,104 @@ lazy_static! { } #[instrument] -pub async fn index() -> Result<impl Reply, Rejection> { +pub async fn index() -> Result { HIT_COUNTER.with_label_values(&["index"]).inc(); - Response::builder() - .header("Last-Modified", &*LAST_MODIFIED) - .html(|o| templates::index_html(o)) + let mut result: Vec<u8> = vec![]; + templates::index_html(&mut result)?; + Ok(Html(result)) } #[instrument] -pub async fn contact() -> Result<impl Reply, Rejection> { +pub async fn contact() -> Result { HIT_COUNTER.with_label_values(&["contact"]).inc(); - Response::builder() - .header("Last-Modified", &*LAST_MODIFIED) - .html(|o| templates::contact_html(o)) + let mut result: Vec<u8> = vec![]; + templates::contact_html(&mut result)?; + Ok(Html(result)) } #[instrument] -pub async fn feeds() -> Result<impl Reply, Rejection> { +pub async fn feeds() -> Result { HIT_COUNTER.with_label_values(&["feeds"]).inc(); - Response::builder() - .header("Last-Modified", &*LAST_MODIFIED) - .html(|o| templates::feeds_html(o)) + let mut result: Vec<u8> = vec![]; + templates::feeds_html(&mut result)?; + Ok(Html(result)) } +#[axum_macros::debug_handler] #[instrument(skip(state))] -pub async fn resume(state: Arc<State>) -> Result<impl Reply, Rejection> { +pub async fn resume(Extension(state): Extension<Arc<State>>) -> Result { HIT_COUNTER.with_label_values(&["resume"]).inc(); let state = state.clone(); - Response::builder() - .header("Last-Modified", &*LAST_MODIFIED) - .html(|o| templates::resume_html(o, Html(state.resume.clone()))) + let mut result: Vec<u8> = vec![]; + templates::resume_html(&mut result, templates::Html(state.resume.clone()))?; + Ok(Html(result)) } #[instrument(skip(state))] -pub async fn patrons(state: Arc<State>) -> Result<impl Reply, Rejection> { +pub async fn patrons(Extension(state): Extension<Arc<State>>) -> Result { HIT_COUNTER.with_label_values(&["patrons"]).inc(); let state = state.clone(); + let mut result: Vec<u8> = vec![]; match &state.patrons { - None => Response::builder().status(500).html(|o| { - templates::error_html( - o, - "Could not load patrons, let me know the API token expired again".to_string(), - ) - }), - Some(patrons) => Response::builder() - .header("Last-Modified", &*LAST_MODIFIED) - .html(|o| templates::patrons_html(o, patrons.clone())), + None => Err(Error::NoPatrons), + Some(patrons) => { + templates::patrons_html(&mut result, patrons.clone())?; + Ok(Html(result)) + } } } +#[axum_macros::debug_handler] #[instrument(skip(state))] -pub async fn signalboost(state: Arc<State>) -> Result<impl Reply, Rejection> { +pub async fn signalboost(Extension(state): Extension<Arc<State>>) -> Result { HIT_COUNTER.with_label_values(&["signalboost"]).inc(); let state = state.clone(); - Response::builder() - .header("Last-Modified", &*LAST_MODIFIED) - .html(|o| templates::signalboost_html(o, state.signalboost.clone())) + let mut result: Vec<u8> = vec![]; + templates::signalboost_html(&mut result, state.signalboost.clone())?; + Ok(Html(result)) } #[instrument] -pub async fn not_found() -> Result<impl Reply, Rejection> { +pub async fn not_found() -> Result { HIT_COUNTER.with_label_values(&["not_found"]).inc(); - Response::builder() - .header("Last-Modified", &*LAST_MODIFIED) - .html(|o| templates::notfound_html(o, "some path".into())) + let mut result: Vec<u8> = vec![]; + templates::notfound_html(&mut result, "some path".into())?; + Ok(Html(result)) } -pub mod blog; -pub mod feeds; -pub mod gallery; -pub mod talks; - #[derive(Debug, thiserror::Error)] -struct PostNotFound(String, String); +pub enum Error { + #[error("series not found: {0}")] + SeriesNotFound(String), -impl fmt::Display for PostNotFound { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "not found: {}/{}", self.0, self.1) - } -} + #[error("post not found: {0}")] + PostNotFound(String), -impl warp::reject::Reject for PostNotFound {} + #[error("patreon key not working, poke me to get this fixed")] + NoPatrons, -#[derive(Debug, thiserror::Error)] -struct SeriesNotFound(String); + #[error("io error: {0}")] + IO(#[from] std::io::Error), -impl fmt::Display for SeriesNotFound { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } + #[error("axum http error: {0}")] + AxumHTTP(#[from] axum::http::Error), } -impl warp::reject::Reject for SeriesNotFound {} +pub type Result<T = Html<Vec<u8>>> = std::result::Result<T, Error>; -lazy_static! { - static ref REJECTION_COUNTER: IntCounterVec = register_int_counter_vec!( - opts!("rejections", "Number of rejections by kind"), - &["kind"] - ) - .unwrap(); -} +impl IntoResponse for Error { + fn into_response(self) -> Response { + let mut result: Vec<u8> = vec![]; + templates::error_html(&mut result, format!("{}", self)).unwrap(); -#[instrument] -pub async fn rejection(err: Rejection) -> Result<impl Reply, Infallible> { - let path: String; - let code; - - if err.is_not_found() { - REJECTION_COUNTER.with_label_values(&["404"]).inc(); - path = "".into(); - code = StatusCode::NOT_FOUND; - } else if let Some(SeriesNotFound(series)) = err.find() { - REJECTION_COUNTER - .with_label_values(&["SeriesNotFound"]) - .inc(); - log::error!("invalid series {}", series); - path = format!("/blog/series/{}", series); - code = StatusCode::NOT_FOUND; - } else if let Some(PostNotFound(kind, name)) = err.find() { - REJECTION_COUNTER.with_label_values(&["PostNotFound"]).inc(); - log::error!("unknown post {}/{}", kind, name); - path = format!("/{}/{}", kind, name); - code = StatusCode::NOT_FOUND; - } else { - REJECTION_COUNTER.with_label_values(&["Other"]).inc(); - log::error!("unhandled rejection: {:?}", err); - path = format!("weird rejection: {:?}", err); - code = StatusCode::INTERNAL_SERVER_ERROR; - } + let body = body::boxed(body::Full::from(result)); - Ok(warp::reply::with_status( Response::builder() - .html(|o| templates::notfound_html(o, path)) - .unwrap(), - code, - )) + .status(match self { + Error::SeriesNotFound(_) | Error::PostNotFound(_) => StatusCode::NOT_FOUND, + _ => StatusCode::INTERNAL_SERVER_ERROR, + }) + .body(body) + .unwrap() + } } diff --git a/src/handlers/talks.rs b/src/handlers/talks.rs index 8db5c9e..22ee3cf 100644 --- a/src/handlers/talks.rs +++ b/src/handlers/talks.rs @@ -1,14 +1,13 @@ -use super::PostNotFound; -use crate::{ - app::State, - post::Post, - templates::{self, Html, RenderRucte}, +use super::{Error::*, Result}; +use crate::{app::State, post::Post, templates}; +use axum::{ + extract::{Extension, Path}, + response::Html, }; use lazy_static::lazy_static; use prometheus::{opts, register_int_counter_vec, IntCounterVec}; use std::sync::Arc; use tracing::instrument; -use warp::{http::Response, Rejection, Reply}; lazy_static! { static ref HIT_COUNTER: IntCounterVec = register_int_counter_vec!( @@ -19,13 +18,18 @@ lazy_static! { } #[instrument(skip(state))] -pub async fn index(state: Arc<State>) -> Result<impl Reply, Rejection> { +pub async fn index(Extension(state): Extension<Arc<State>>) -> Result { let state = state.clone(); - Response::builder().html(|o| templates::talkindex_html(o, state.talks.clone())) + let mut result: Vec<u8> = vec![]; + templates::talkindex_html(&mut result, state.talks.clone())?; + Ok(Html(result)) } #[instrument(skip(state))] -pub async fn post_view(name: String, state: Arc<State>) -> Result<impl Reply, Rejection> { +pub async fn post_view( + Path(name): Path<String>, + Extension(state): Extension<Arc<State>>, +) -> Result { let mut want: Option<Post> = None; for post in &state.talks { @@ -35,13 +39,15 @@ pub async fn post_view(name: String, state: Arc<State>) -> Result<impl Reply, Re } match want { - None => Err(PostNotFound("talks".into(), name).into()), + None => Err(PostNotFound(name).into()), Some(post) => { HIT_COUNTER .with_label_values(&[name.clone().as_str()]) .inc(); - let body = Html(post.body_html.clone()); - Response::builder().html(|o| templates::talkpost_html(o, post, body)) + let body = templates::Html(post.body_html.clone()); + let mut result: Vec<u8> = vec![]; + templates::talkpost_html(&mut result, post, body)?; + Ok(Html(result)) } } } |
