aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
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/main.rs
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/main.rs')
-rw-r--r--src/main.rs407
1 files changed, 207 insertions, 200 deletions
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"))
- .and_then(handlers::feeds::rss);
- let sitemap = warp::path("sitemap.xml")
- .and(with_state(state.clone()))
- .and_then(handlers::feeds::sitemap);
- let asset_links = warp::path!(".well-known" / "assetlinks.json")
- .and(warp::fs::file("./static/assetlinks.json"));
-
- let go_vanity_jsonfeed = warp::path("jsonfeed")
- .and(warp::any().map(move || "christine.website/jsonfeed"))
- .and(warp::any().map(move || "https://tulpa.dev/Xe/jsonfeed"))
- .and(warp::any().map(move || "master"))
- .and_then(go_vanity::gitea);
-
- let metrics_endpoint = warp::path("metrics").and(warp::path::end()).map(move || {
- 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::from(buffer))
- .unwrap()
- });
-
- let static_pages = index
- .or(feeds.or(asset_links))
- .or(resume.or(signalboost))
- .or(patrons)
- .or(jsonfeed.or(atom.or(sitemap)).or(rss))
- .or(favicon.or(robots).or(sw))
- .or(contact.or(new_post))
- .map(|reply| {
- warp::reply::with_header(
- reply,
- "Cache-Control",
- "public, max-age=86400, stale-if-error=60",
- )
- });
-
- let dynamic_pages = blog_index
- .or(series.or(series_view).or(post_view))
- .or(gallery_index.or(gallery_post_view))
- .or(talk_index.or(talk_post_view))
- .map(|reply| {
- warp::reply::with_header(
- reply,
- "Cache-Control",
- "public, max-age=600, stale-if-error=60",
- )
- });
-
- let site = static_pages
- .or(dynamic_pages)
- .or(healthcheck.or(metrics_endpoint).or(go_vanity_jsonfeed))
- .or(files.or(css))
- .map(|reply| {
- warp::reply::with_header(
- reply,
- "X-Hacker",
- "If you are reading this, check out /signalboost to find people for your team",
- )
- })
- .map(|reply| warp::reply::with_header(reply, "X-Clacks-Overhead", "GNU Ashlynn"))
- .map(|reply| {
- warp::reply::with_header(
- reply,
- "Link",
- format!(
- r#"<{}>; rel="webmention""#,
- std::env::var("WEBMENTION_URL")
- .unwrap_or("https://mi.within.website/api/webmention/accept".to_string())
- ),
- )
- })
- .with(warp::log(APPLICATION_NAME))
- .recover(handlers::rejection);
+ .route("/jsonfeed", get(go_vanity))
+ .route("/metrics", get(metrics))
+ .route(
+ "/sw.js",
+ axum::routing::get_service(ServeFile::new("./static/js/sw.js")).handle_error(
+ |err: std::io::Error| async move {
+ (
+ StatusCode::INTERNAL_SERVER_ERROR,
+ format!("unhandled internal server error: {}", err),
+ )
+ },
+ ),
+ )
+ .route(
+ "/.well-known/assetlinks.json",
+ axum::routing::get_service(ServeFile::new("./static/assetlinks.json")).handle_error(
+ |err: std::io::Error| async move {
+ (
+ StatusCode::INTERNAL_SERVER_ERROR,
+ format!("unhandled internal server error: {}", err),
+ )
+ },
+ ),
+ )
+ .route(
+ "/robots.txt",
+ axum::routing::get_service(ServeFile::new("./static/robots.txt")).handle_error(
+ |err: std::io::Error| async move {
+ (
+ StatusCode::INTERNAL_SERVER_ERROR,
+ format!("unhandled internal server error: {}", err),
+ )
+ },
+ ),
+ )
+ .route(
+ "/favicon.ico",
+ axum::routing::get_service(ServeFile::new("./static/favicon/favicon.ico"))
+ .handle_error(|err: std::io::Error| async move {
+ (
+ StatusCode::INTERNAL_SERVER_ERROR,
+ format!("unhandled internal server error: {}", err),
+ )
+ }),
+ )
+ // static pages
+ .route("/", get(handlers::index))
+ .route("/contact", get(handlers::contact))
+ .route("/feeds", get(handlers::feeds))
+ .route("/resume", get(handlers::resume))
+ .route("/patrons", get(handlers::patrons))
+ .route("/signalboost", get(handlers::signalboost))
+ // feeds
+ .route("/blog.json", get(handlers::feeds::jsonfeed))
+ .route("/blog.atom", get(handlers::feeds::atom))
+ .route("/blog.rss", get(handlers::feeds::rss))
+ // blog
+ .route("/blog", get(handlers::blog::index))
+ .route("/blog/", get(handlers::blog::index))
+ .route("/blog/:name", get(handlers::blog::post_view))
+ .route("/blog/series", get(handlers::blog::series))
+ .route("/blog/series/:series", get(handlers::blog::series_view))
+ // gallery
+ .route("/gallery", get(handlers::gallery::index))
+ .route("/gallery/", get(handlers::gallery::index))
+ .route("/gallery/:name", get(handlers::gallery::post_view))
+ // talks
+ .route("/talks", get(handlers::talks::index))
+ .route("/talks/", get(handlers::talks::index))
+ .route("/talks/:name", get(handlers::talks::post_view))
+ // junk google wants
+ .route("/sitemap.xml", get(handlers::feeds::sitemap))
+ // static files
+ .nest(
+ "/css",
+ axum::routing::get_service(ServeDir::new("./css")).handle_error(
+ |err: std::io::Error| async move {
+ (
+ StatusCode::INTERNAL_SERVER_ERROR,
+ format!("unhandled internal server error: {}", err),
+ )
+ },
+ ),
+ )
+ .nest(
+ "/static",
+ axum::routing::get_service(ServeDir::new("./static")).handle_error(
+ |err: std::io::Error| async move {
+ (
+ StatusCode::INTERNAL_SERVER_ERROR,
+ format!("unhandled internal server error: {}", err),
+ )
+ },
+ ),
+ )
+ .layer(middleware);
#[cfg(target_os = "linux")]
{
@@ -241,30 +227,51 @@ async fn main() -> Result<()> {
}
}
- let server = warp::serve(site);
-
match std::env::var("SOCKPATH") {
Ok(sockpath) => {
- let _ = std::fs::remove_file(&sockpath);
- let listener = UnixListener::bind(sockpath)?;
- let incoming = UnixListenerStream::new(listener);
- server.run_incoming(incoming).await;
-
- Ok(())
+ let uds = UnixListener::bind(&sockpath)?;
+ axum::Server::builder(ServerAccept { uds })
+ .serve(app.into_make_service_with_connect_info::<UdsConnectInfo, _>())
+ .await?;
}
Err(_) => {
- server
- .run((
- IpAddr::from_str(&std::env::var("HOST").unwrap_or("::".into()))?,
- std::env::var("PORT")
- .unwrap_or("3030".into())
- .parse::<u16>()?,
- ))
- .await;
-
- Ok(())
+ let addr: SocketAddr = (
+ IpAddr::from_str(&env::var("HOST").unwrap_or("::".into()))?,
+ env::var("PORT").unwrap_or("3030".into()).parse::<u16>()?,
+ )
+ .into();
+ info!("listening on {}", addr);
+ axum::Server::bind(&addr)
+ .serve(app.into_make_service())
+ .await?;
}
}
+
+ 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()
+}
+
+async fn go_vanity() -> Html<Vec<u8>> {
+ let mut buffer: Vec<u8> = vec![];
+ templates::gitea_html(
+ &mut buffer,
+ "christine.website/jsonfeed",
+ "https://christine.website/metrics",
+ "master",
+ )
+ .unwrap();
+ Html(buffer)
}
include!(concat!(env!("OUT_DIR"), "/templates.rs"));