aboutsummaryrefslogtreecommitdiff
path: root/src
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
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')
-rw-r--r--src/domainsocket.rs94
-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
-rw-r--r--src/main.rs407
-rw-r--r--src/post/mod.rs6
8 files changed, 466 insertions, 422 deletions
diff --git a/src/domainsocket.rs b/src/domainsocket.rs
new file mode 100644
index 0000000..ef731f5
--- /dev/null
+++ b/src/domainsocket.rs
@@ -0,0 +1,94 @@
+use axum::extract::connect_info;
+use futures::ready;
+use hyper::{
+ client::connect::{Connected, Connection},
+ server::accept::Accept,
+};
+use std::{
+ io,
+ pin::Pin,
+ sync::Arc,
+ task::{Context, Poll},
+};
+use tokio::{
+ io::{AsyncRead, AsyncWrite},
+ net::{unix::UCred, UnixListener, UnixStream},
+};
+use tower::BoxError;
+
+pub struct ServerAccept {
+ pub uds: UnixListener,
+}
+
+impl Accept for ServerAccept {
+ type Conn = UnixStream;
+ type Error = BoxError;
+
+ fn poll_accept(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ ) -> Poll<Option<Result<Self::Conn, Self::Error>>> {
+ let (stream, _addr) = ready!(self.uds.poll_accept(cx))?;
+ Poll::Ready(Some(Ok(stream)))
+ }
+}
+
+pub struct ClientConnection {
+ pub stream: UnixStream,
+}
+
+impl AsyncWrite for ClientConnection {
+ fn poll_write(
+ mut self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ buf: &[u8],
+ ) -> Poll<Result<usize, io::Error>> {
+ Pin::new(&mut self.stream).poll_write(cx, buf)
+ }
+
+ fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
+ Pin::new(&mut self.stream).poll_flush(cx)
+ }
+
+ fn poll_shutdown(
+ mut self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ ) -> Poll<Result<(), io::Error>> {
+ Pin::new(&mut self.stream).poll_shutdown(cx)
+ }
+}
+
+impl AsyncRead for ClientConnection {
+ fn poll_read(
+ mut self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ buf: &mut tokio::io::ReadBuf<'_>,
+ ) -> Poll<io::Result<()>> {
+ Pin::new(&mut self.stream).poll_read(cx, buf)
+ }
+}
+
+impl Connection for ClientConnection {
+ fn connected(&self) -> Connected {
+ Connected::new()
+ }
+}
+
+#[derive(Clone, Debug)]
+#[allow(dead_code)]
+pub struct UdsConnectInfo {
+ pub peer_addr: Arc<tokio::net::unix::SocketAddr>,
+ pub peer_cred: UCred,
+}
+
+impl connect_info::Connected<&UnixStream> for UdsConnectInfo {
+ fn connect_info(target: &UnixStream) -> Self {
+ let peer_addr = target.peer_addr().unwrap();
+ let peer_cred = target.peer_cred().unwrap();
+
+ Self {
+ peer_addr: Arc::new(peer_addr),
+ peer_cred,
+ }
+ }
+}
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))
}
}
}
diff --git a/src/main.rs b/src/main.rs
index bbe5658..c6dc33f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,29 +1,64 @@
#[macro_use]
extern crate tracing;
+use axum::{
+ body,
+ extract::Extension,
+ http::header::{self, HeaderValue, CACHE_CONTROL, CONTENT_TYPE},
+ response::{Html, Response},
+ routing::get,
+ Router,
+};
use color_eyre::eyre::Result;
-use hyper::{header::CONTENT_TYPE, Body, Response};
+use hyper::StatusCode;
use prometheus::{Encoder, TextEncoder};
-use std::net::IpAddr;
-use std::str::FromStr;
-use std::sync::Arc;
+use std::{
+ env,
+ net::{IpAddr, SocketAddr},
+ str::FromStr,
+ sync::Arc,
+};
use tokio::net::UnixListener;
-use tokio_stream::wrappers::UnixListenerStream;
-use warp::{path, Filter};
+use tower_http::{
+ services::{ServeDir, ServeFile},
+ set_header::SetResponseHeaderLayer,
+ trace::TraceLayer,
+};
pub mod app;
pub mod handlers;
pub mod post;
pub mod signalboost;
-use app::State;
+mod domainsocket;
+use domainsocket::*;
const APPLICATION_NAME: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
-fn with_state(
- state: Arc<State>,
-) -> impl Filter<Extract = (Arc<State>,), Error = std::convert::Infallible> + Clone {
- warp::any().map(move || state.clone())
+async fn healthcheck() -> &'static str {
+ "OK"
+}
+
+fn cache_header(_: &Response) -> Option<header::HeaderValue> {
+ Some(header::HeaderValue::from_static(
+ "public, max-age=3600, stale-if-error=60",
+ ))
+}
+
+fn webmention_header(_: &Response) -> Option<HeaderValue> {
+ Some(header::HeaderValue::from_static(
+ r#"<https://mi.within.website/api/webmention/accept>; rel="webmention""#,
+ ))
+}
+
+fn clacks_header(_: &Response) -> Option<HeaderValue> {
+ Some(HeaderValue::from_static("Ashlynn"))
+}
+
+fn hacker_header(_: &Response) -> Option<HeaderValue> {
+ Some(header::HeaderValue::from_static(
+ "If you are reading this, check out /signalboost to find people for your team",
+ ))
}
#[tokio::main]
@@ -35,7 +70,7 @@ async fn main() -> Result<()> {
let state = Arc::new(
app::init(
- std::env::var("CONFIG_FNAME")
+ env::var("CONFIG_FNAME")
.unwrap_or("./config.dhall".into())
.as_str()
.into(),
@@ -43,178 +78,129 @@ async fn main() -> Result<()> {
.await?,
);
- let healthcheck = warp::get().and(warp::path(".within").and(warp::path("health")).map(|| "OK"));
- let new_post = warp::path!(".within" / "website.within.xesite" / "new_post")
- .and(with_state(state.clone()))
- .and_then(handlers::feeds::new_post);
-
- let base = warp::path!("blog" / ..);
- let blog_index = base
- .and(warp::path::end())
- .and(with_state(state.clone()))
- .and_then(handlers::blog::index);
- let series = base
- .and(warp::path!("series").and(with_state(state.clone()).and_then(handlers::blog::series)));
- let series_view = base.and(
- warp::path!("series" / String)
- .and(with_state(state.clone()))
- .and(warp::get())
- .and_then(handlers::blog::series_view),
- );
- let post_view = base.and(
- warp::path!(String)
- .and(with_state(state.clone()))
- .and(warp::get())
- .and_then(handlers::blog::post_view),
- );
-
- let gallery_base = warp::path!("gallery" / ..);
- let gallery_index = gallery_base
- .and(warp::path::end())
- .and(with_state(state.clone()))
- .and_then(handlers::gallery::index);
- let gallery_post_view = gallery_base.and(
- warp::path!(String)
- .and(with_state(state.clone()))
- .and(warp::get())
- .and_then(handlers::gallery::post_view),
- );
-
- let talk_base = warp::path!("talks" / ..);
- let talk_index = talk_base
- .and(warp::path::end())
- .and(with_state(state.clone()))
- .and_then(handlers::talks::index);
- let talk_post_view = talk_base.and(
- warp::path!(String)
- .and(with_state(state.clone()))
- .and(warp::get())
- .and_then(handlers::talks::post_view),
- );
-
- let index = warp::get().and(path::end().and_then(handlers::index));
- let contact = warp::path!("contact").and_then(handlers::contact);
- let feeds = warp::path!("feeds").and_then(handlers::feeds);
- let resume = warp::path!("resume")
- .and(with_state(state.clone()))
- .and_then(handlers::resume);
- let signalboost = warp::path!("signalboost")
- .and(with_state(state.clone()))
- .and_then(handlers::signalboost);
- let patrons = warp::path!("patrons")
- .and(with_state(state.clone()))
- .and_then(handlers::patrons);
+ let middleware = tower::ServiceBuilder::new()
+ .layer(TraceLayer::new_for_http())
+ .layer(Extension(state.clone()))
+ .layer(SetResponseHeaderLayer::overriding(
+ header::CACHE_CONTROL,
+ cache_header,
+ ))
+ .layer(SetResponseHeaderLayer::appending(
+ header::LINK,
+ webmention_header,
+ ))
+ .layer(SetResponseHeaderLayer::appending(
+ header::HeaderName::from_static("x-clacks-overhead"),
+ clacks_header,
+ ))
+ .layer(SetResponseHeaderLayer::overriding(
+ header::HeaderName::from_static("x-hacker"),
+ hacker_header,
+ ));
- let files = warp::path("static")
- .and(warp::fs::dir("./static"))
- .map(|reply| {
- warp::reply::with_header(
- reply,
- "Cache-Control",
- "public, max-age=86400, stale-if-error=60",
- )
- });
-
- let css = warp::path("css").and(warp::fs::dir("./css")).map(|reply| {
- warp::reply::with_header(
- reply,
- "Cache-Control",
- "public, max-age=86400, stale-if-error=60",
+ let app = Router::new()
+ // meta
+ .route("/.within/health", get(healthcheck))
+ .route(
+ "/.within/website.within.xesite/new_post",
+ get(handlers::feeds::new_post),
)
- });
-
- let sw = warp::path("sw.js").and(warp::fs::file("./static/js/sw.js"));
- let robots = warp::path("robots.txt").and(warp::fs::file("./static/robots.txt"));
- let favicon = warp::path("favicon.ico").and(warp::fs::file("./static/favicon/favicon.ico"));
-
- let jsonfeed = warp::path("blog.json")
- .and(with_state(state.clone()))
- .and(warp::header::optional("if-none-match"))
- .and_then(handlers::feeds::jsonfeed);
- let atom = warp::path("blog.atom")
- .and(with_state(state.clone()))
- .and(warp::header::optional("if-none-match"))
- .and_then(handlers::feeds::atom);
- let rss = warp::path("blog.rss")
- .and(with_state(state.clone()))
- .and(warp::header::optional("if-none-match