aboutsummaryrefslogtreecommitdiff
path: root/src/handlers
diff options
context:
space:
mode:
authorXe Iaso <me@christine.website>2022-03-21 20:14:14 -0400
committerGitHub <noreply@github.com>2022-03-21 20:14:14 -0400
commit8b747c1c40876c6668191594eddcb260199cdb7f (patch)
tree86edb9bc382751c6a32f5f2946ff235ea06674ba /src/handlers
parentf45ca40ae1052d46611ff2f27ad281695afc4f8f (diff)
downloadxesite-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.rs57
-rw-r--r--src/handlers/feeds.rs95
-rw-r--r--src/handlers/gallery.rs30
-rw-r--r--src/handlers/mod.rs169
-rw-r--r--src/handlers/talks.rs30
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))
}
}
}