diff --git a/Cargo.lock b/Cargo.lock index 33af59346..96bab80ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2573,6 +2573,25 @@ dependencies = [ "miniz_oxide 0.5.4", ] +[[package]] +name = "include_dir" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "indenter" version = "0.3.3" @@ -2768,6 +2787,7 @@ dependencies = [ "http", "human-panic", "hyper", + "include_dir", "iso8601-timestamp", "kitsune-blocking", "kitsune-cache", @@ -2790,6 +2810,7 @@ dependencies = [ "mimalloc", "mime", "mime_guess", + "once_cell", "oxide-auth", "oxide-auth-async", "oxide-auth-axum", diff --git a/kitsune/Cargo.toml b/kitsune/Cargo.toml index c1fc3cb18..6424e56c7 100644 --- a/kitsune/Cargo.toml +++ b/kitsune/Cargo.toml @@ -35,6 +35,7 @@ headers = "0.3.9" http = "0.2.10" human-panic = "1.2.2" hyper = { version = "0.14.27", features = ["deprecated"] } +include_dir = "0.7.3" iso8601-timestamp = "0.2.12" kitsune-blocking = { path = "../crates/kitsune-blocking" } kitsune-cache = { path = "../crates/kitsune-cache" } @@ -55,6 +56,7 @@ mimalloc = "0.1.39" mime = "0.3.17" mime_guess = { version = "2.0.4", default-features = false } pkcs8 = "0.10.2" +once_cell = "1.18.0" oxide-auth = "0.5.4" oxide-auth-async = "0.1.1" oxide-auth-axum = "0.3.0" diff --git a/kitsune/src/http/handler/mod.rs b/kitsune/src/http/handler/mod.rs index c584339cd..c1e6269c6 100644 --- a/kitsune/src/http/handler/mod.rs +++ b/kitsune/src/http/handler/mod.rs @@ -8,5 +8,6 @@ pub mod oauth; #[cfg(feature = "oidc")] pub mod oidc; pub mod posts; +pub mod public; pub mod users; pub mod well_known; diff --git a/kitsune/src/http/handler/public.rs b/kitsune/src/http/handler/public.rs new file mode 100644 index 000000000..22169287b --- /dev/null +++ b/kitsune/src/http/handler/public.rs @@ -0,0 +1,48 @@ +use axum::{extract::Path, routing, Router, TypedHeader}; +use axum_extra::either::Either; +use headers::ContentType; +use http::StatusCode; +use include_dir::include_dir; +use mime::Mime; +use once_cell::sync::Lazy; +use std::{collections::HashMap, path::Path as FsPath, sync::RwLock}; + +static PUBLIC_DIR: include_dir::Dir<'_> = include_dir!("public"); +static PUBLIC_DIR_MIME_TYPE: Lazy>> = + Lazy::new(RwLock::default); + +#[allow(clippy::unused_async)] +async fn get( + Path(path): Path, +) -> Either<(TypedHeader, &'static [u8]), StatusCode> { + let Some(file) = PUBLIC_DIR.get_file(path) else { + return Either::E2(StatusCode::NOT_FOUND); + }; + + let mime_type = PUBLIC_DIR_MIME_TYPE + .read() + .unwrap() + .get(file.path()) + .map(Mime::clone); + + let mime_type = if let Some(mime_type) = mime_type { + mime_type + } else { + let mime_type = mime_guess::from_path(file.path()).first_or_octet_stream(); + PUBLIC_DIR_MIME_TYPE + .write() + .unwrap() + .insert(file.path(), mime_type.clone()); + + mime_type + }; + + Either::E1((TypedHeader(ContentType::from(mime_type)), file.contents())) +} + +pub fn routes() -> Router +where + T: Clone + Send + Sync + 'static, +{ + Router::new().route("/*path", routing::get(get)) +} diff --git a/kitsune/src/http/mod.rs b/kitsune/src/http/mod.rs index 4e04af1db..08648ed7b 100644 --- a/kitsune/src/http/mod.rs +++ b/kitsune/src/http/mod.rs @@ -1,5 +1,7 @@ use self::{ - handler::{confirm_account, custom_emojis, media, nodeinfo, oauth, posts, users, well_known}, + handler::{ + confirm_account, custom_emojis, media, nodeinfo, oauth, posts, public, users, well_known, + }, openapi::api_docs, }; use crate::state::Zustand; @@ -54,7 +56,7 @@ pub fn create_router( .nest("/posts", posts::routes()) .nest("/users", users::routes()) .nest("/.well-known", well_known::routes()) - .nest_service("/public", ServeDir::new("public")); + .nest("/public", public::routes()); #[cfg(feature = "oidc")] {