Skip to content

Commit

Permalink
feat(site): properly serve fallback
Browse files Browse the repository at this point in the history
  • Loading branch information
johnbchron committed Dec 10, 2024
1 parent 52fe879 commit 5d33b32
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 93 deletions.
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions crates/site-app/src/components/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub fn Header() -> impl IntoView {
flex-row items-center rounded-b-xl border border-t-0 \
border-base-7 dark:border-basedark-7";

let auth_status = use_context::<AuthStatus>();
let auth_status = use_context::<AuthStatus>().and_then(|as_| as_.0);
let auth_status_text = format!("Status: {:?}", auth_status);

view! {
Expand All @@ -21,7 +21,7 @@ pub fn Header() -> impl IntoView {
</span>
<div class="flex-1" />
<div class="flex flex-row items-center gap-2">
{ auth_status_text }
<p>{ auth_status_text }</p>
<Button href="/sign-up" color=ButtonColor::Primary>
"Sign Up"
</Button>
Expand Down
2 changes: 1 addition & 1 deletion crates/site-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ leptos_router.workspace = true
axum.workspace = true
tokio.workspace = true
tower.workspace = true
tower-http.workspace = true
tower-http = { workspace = true, features = [ "fs" ] }
tower-sessions.workspace = true

tracing.workspace = true
Expand Down
62 changes: 62 additions & 0 deletions crates/site-server/src/file_and_error_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use axum::{
body::Body,
extract::{FromRef, Request, State},
http::{HeaderMap, HeaderValue, StatusCode, Uri},
response::{IntoResponse, Response},
};
use leptos::config::LeptosOptions;
use tower::ServiceExt;

use crate::{app_state::AppState, leptos_routes_handler, AuthSession};

pub async fn fallback_handler(
uri: Uri,
auth_session: AuthSession,
State(state): State<AppState>,
req: Request<Body>,
) -> Response<Body> {
let options = LeptosOptions::from_ref(&state);
let app_state = AppState::from_ref(&state);
let res = get_static_file(uri, &options.site_root, req.headers());
let res = res.await.unwrap();

if res.status() == StatusCode::OK {
res.into_response()
} else {
let mut res =
leptos_routes_handler(auth_session, State(app_state), req).await;
*res.status_mut() = StatusCode::NOT_FOUND;
res
}
}

async fn get_static_file(
uri: Uri,
root: &str,
headers: &HeaderMap<HeaderValue>,
) -> Result<Response<Body>, (StatusCode, String)> {
use axum::http::header::ACCEPT_ENCODING;

let req = Request::builder().uri(uri);

let req = match headers.get(ACCEPT_ENCODING) {
Some(value) => req.header(ACCEPT_ENCODING, value),
None => req,
};

let req = req.body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with
// `tower::ServiceExt::oneshot` This path is relative to the cargo root
match tower_http::services::ServeDir::new(root)
.precompressed_gzip()
.precompressed_br()
.oneshot(req)
.await
{
Ok(res) => Ok(res.into_response()),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {err}"),
)),
}
}
92 changes: 2 additions & 90 deletions crates/site-server/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod app_state;
mod file_and_error_handler;

use std::sync::Arc;

Expand All @@ -7,7 +8,6 @@ use axum::{
body::Body,
extract::{Request, State},
response::IntoResponse,
routing::get,
Router,
};
use axum_login::AuthManagerLayerBuilder;
Expand Down Expand Up @@ -82,7 +82,7 @@ async fn main() {

let app = Router::new()
.leptos_routes_with_handler(routes, leptos_routes_handler)
.fallback(leptos_axum::file_and_error_handler::<AppState, _>(shell))
.fallback(self::file_and_error_handler::fallback_handler)
.with_state(app_state)
.layer(auth_layer);

Expand All @@ -94,91 +94,3 @@ async fn main() {
.await
.unwrap();
}

mod file_and_error_handler {
use std::{future::Future, pin::Pin};

use axum::{
body::Body,
extract::{FromRef, Request, State},
http::Uri,
};
use leptos::{config::LeptosOptions, IntoView};

pub fn file_and_error_handler<S, IV>(
shell: fn(LeptosOptions) -> IV,
) -> impl Fn(
Uri,
State<S>,
Request<Body>,
) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + 'static>>
+ Clone
+ Send
+ 'static
where
IV: IntoView + 'static,
S: Send + 'static,
LeptosOptions: FromRef<S>,
{
move |uri: Uri, State(options): State<S>, req: Request<Body>| {
Box::pin(async move {
let options = LeptosOptions::from_ref(&options);
let res = get_static_file(uri, &options.site_root, req.headers());
let res = res.await.unwrap();

if res.status() == StatusCode::OK {
res.into_response()
} else {
let mut res = handle_response_inner(
|| {},
move || shell(options),
req,
|app, chunks| {
Box::pin(async move {
let app =
app.to_html_stream_in_order().collect::<String>().await;
let chunks = chunks();
Box::pin(once(async move { app }).chain(chunks))
as PinnedStream<String>
})
},
)
.await;
*res.status_mut() = StatusCode::NOT_FOUND;
res
}
})
}
}

async fn get_static_file(
uri: Uri,
root: &str,
headers: &HeaderMap<HeaderValue>,
) -> Result<Response<Body>, (StatusCode, String)> {
use axum::http::header::ACCEPT_ENCODING;

let req = Request::builder().uri(uri);

let req = match headers.get(ACCEPT_ENCODING) {
Some(value) => req.header(ACCEPT_ENCODING, value),
None => req,
};

let req = req.body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with
// `tower::ServiceExt::oneshot` This path is relative to the cargo root
match tower_http::services::ServeDir::new(root)
.precompressed_gzip()
.precompressed_br()
.oneshot(req)
.await
{
Ok(res) => Ok(res.into_response()),
Err(err) => Err((
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {err}"),
)),
}
}
}

0 comments on commit 5d33b32

Please sign in to comment.