NGINX-looking fallback fallback, small improvements (#519)

* small improvements

* make fallback work

* remove potential boxing, move html into own file

* move into separate closure
This commit is contained in:
Aumetra Weisman 2024-04-06 18:41:29 +02:00 committed by GitHub
parent 06a731d1c8
commit dc2101927d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 126 additions and 15 deletions

1
.gitignore vendored
View File

@ -20,6 +20,7 @@ target-analyzer
# MRF directory
/mrf-modules
/mrf-storage
# Devenv stuff
/result

11
Cargo.lock generated
View File

@ -3110,6 +3110,15 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "human-size"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9994b79e8c1a39b3166c63ae7823bb2b00831e2a96a31399c50fe69df408eaeb"
dependencies = [
"serde",
]
[[package]]
name = "humansize"
version = "2.1.3"
@ -3731,6 +3740,7 @@ name = "kitsune-config"
version = "0.0.1-pre.6"
dependencies = [
"eyre",
"human-size",
"isolang",
"serde",
"smol_str",
@ -8073,6 +8083,7 @@ dependencies = [
"tower-layer",
"tower-service",
"tracing",
"uuid",
]
[[package]]

View File

@ -139,6 +139,10 @@ type = "in-process"
[mrf]
module-dir = "mrf-modules"
[mrf.storage]
type = "fs"
path = "./mrf-storage"
# OIDC configuration
#
# Kitsune can use an OIDC service to manage logins
@ -184,8 +188,7 @@ frontend-dir = "./kitsune-fe/dist"
# Maximum upload size
#
# This is the limit of data that the HTTP server accepts before it returns an HTTP 413 error
# The unit is bytes
max-upload-size = 5242880 # 5MB
max-upload-size = "5MiB"
# Enable the media proxy
#
# The media proxy will relay all of the media streams through the backend, enabling two important properties:

View File

@ -7,6 +7,7 @@ license.workspace = true
[dependencies]
eyre = "0.6.12"
human-size = { version = "0.4.3", features = ["serde"] }
isolang = { version = "2.4.0", features = ["serde"] }
serde = { version = "1.0.197", features = ["derive"] }
smol_str = { version = "0.2.1", features = ["serde"] }

View File

@ -9,7 +9,7 @@ pub struct Configuration {
pub clacks_overhead: Vec<SmolStr>,
pub deny_brave_browsers: bool,
pub frontend_dir: SmolStr,
pub max_upload_size: usize,
pub max_upload_size: human_size::Size,
pub media_proxy_enabled: bool,
pub oidc: Option<oidc::Configuration>,
pub port: u16,

View File

@ -91,12 +91,14 @@ thiserror = "1.0.58"
time = "0.3.34"
tokio = { version = "1.37.0", features = ["full"] }
tokio-util = { version = "0.7.10", features = ["compat"] }
tower = { version = "0.4.13", features = ["util"] }
tower-stop-using-brave = { path = "../lib/tower-stop-using-brave" }
tower-x-clacks-overhead = { path = "../lib/tower-x-clacks-overhead" }
tower-http = { version = "0.5.2", features = [
"catch-panic",
"cors",
"fs",
"request-id",
"timeout",
"trace",
] }
@ -141,7 +143,6 @@ redis = { version = "0.25.2", default-features = false, features = [
"connection-manager",
"tokio-comp",
] }
tower = "0.4.13"
[features]
default = ["graphql-api", "mastodon-api"]

View File

@ -5,23 +5,37 @@ use self::{
openapi::api_docs,
};
use crate::state::Zustand;
use axum::{extract::DefaultBodyLimit, Router};
use axum::{
body::HttpBody,
extract::DefaultBodyLimit,
response::{Html, IntoResponse},
Router,
};
use bytes::Bytes;
use color_eyre::eyre::{self, Context};
use cursiv::CsrfLayer;
use http::{HeaderName, StatusCode};
use http_body_util::Either;
use kitsune_config::server;
use std::time::Duration;
use std::{convert::Infallible, time::Duration};
use tokio::net::TcpListener;
use tower::{BoxError, Service, ServiceExt};
use tower_http::{
catch_panic::CatchPanicLayer,
cors::CorsLayer,
request_id::{MakeRequestUuid, PropagateRequestIdLayer, SetRequestIdLayer},
services::{ServeDir, ServeFile},
timeout::TimeoutLayer,
trace::TraceLayer,
trace::{HttpMakeClassifier, MakeSpan, TraceLayer},
};
use tower_stop_using_brave::StopUsingBraveLayer;
use tower_x_clacks_overhead::XClacksOverheadLayer;
use utoipa_swagger_ui::SwaggerUi;
const FALLBACK_FALLBACK_INDEX: &str = include_str!("../../templates/fallback-fallback.html");
static X_REQUEST_ID: HeaderName = HeaderName::from_static("x-request-id");
#[cfg(feature = "graphql-api")]
mod graphql;
mod handler;
@ -34,10 +48,18 @@ mod util;
pub mod extractor;
pub fn create_router(
state: Zustand,
#[inline]
fn serve_frontend<B>(
server_config: &server::Configuration,
) -> eyre::Result<Router> {
) -> impl Service<
http::Request<B>,
Response = http::Response<impl HttpBody<Data = Bytes, Error = BoxError>>,
Error = Infallible,
Future = impl Send,
> + Clone
where
B: Send + 'static,
{
let frontend_dir = &server_config.frontend_dir;
let frontend_index_path = {
let mut tmp = frontend_dir.to_string();
@ -45,6 +67,41 @@ pub fn create_router(
tmp
};
let handle_response = |response: http::Response<_>| {
if response.status() == StatusCode::NOT_FOUND {
(StatusCode::NOT_FOUND, Html(FALLBACK_FALLBACK_INDEX))
.into_response()
.map(Either::Left)
} else {
response.map(Either::Right)
}
};
ServeDir::new(frontend_dir.as_str())
.fallback(ServeFile::new(frontend_index_path))
.map_future(move |result_fut| async move {
let result = result_fut.await;
result.map(handle_response)
})
}
#[inline]
fn trace_layer<B>() -> TraceLayer<HttpMakeClassifier, impl MakeSpan<B> + Clone> {
TraceLayer::new_for_http().make_span_with(|request: &http::Request<B>| {
debug_span!(
"request",
method = %request.method(),
uri = %request.uri(),
version = ?request.version(),
request_id = ?request.headers().get(&X_REQUEST_ID).unwrap(),
)
})
}
pub fn create_router(
state: Zustand,
server_config: &server::Configuration,
) -> eyre::Result<Router> {
// This warning will come up if the server is compiled without the Mastodon API compatibility
#[allow(unused_mut)]
let mut router = Router::new()
@ -78,9 +135,7 @@ pub fn create_router(
router = router
.merge(SwaggerUi::new("/swagger-ui").url("/api-doc/openapi.json", api_docs()))
.fallback_service(
ServeDir::new(frontend_dir.as_str()).fallback(ServeFile::new(frontend_index_path)),
);
.fallback_service(serve_frontend(server_config));
if !server_config.clacks_overhead.is_empty() {
let clacks_overhead_layer =
@ -98,11 +153,18 @@ pub fn create_router(
.layer(CatchPanicLayer::new())
.layer(CorsLayer::permissive())
.layer(CsrfLayer::generate()) // TODO: Make this configurable instead of random
.layer(DefaultBodyLimit::max(server_config.max_upload_size))
.layer(DefaultBodyLimit::max(
server_config.max_upload_size.to_bytes() as usize,
))
.layer(TimeoutLayer::new(Duration::from_secs(
server_config.request_timeout_secs,
)))
.layer(TraceLayer::new_for_http())
.layer(trace_layer())
.layer(PropagateRequestIdLayer::new(X_REQUEST_ID.clone()))
.layer(SetRequestIdLayer::new(
X_REQUEST_ID.clone(),
MakeRequestUuid,
))
.with_state(state))
}

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<title>Welcome to Kitsune!</title>
<style>
html {
color-scheme: light dark;
}
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to Kitsune!</h1>
<p>
If you see this page, the Kitsune fediverse server is successfully
installed and working. Further configuration is required.
</p>
<p>
For online documentation and support please refer to
<a href="http://joinkitsune.org/">joinkitsune.org</a>.<br />
Commercial support is available at
<a href="#">fuckall nowhere</a>.
</p>
<p><em>Thank you for using Kitsune.</em></p>
</body>
</html>