use axum::{
body::Body,
extract::Request,
http,
middleware::{self, Next},
response::Response,
Router,
};
use crate::core::InjectedStates;
pub trait HydratedRouterExt {
fn hydrated(self) -> Self;
}
impl<S> HydratedRouterExt for Router<S>
where
S: Clone + Send + Sync + 'static,
{
fn hydrated(self) -> Self {
self.layer(middleware::from_fn(inject_logic))
}
}
#[derive(Clone)]
pub(crate) struct HydrationMiddlewareMarker;
async fn inject_logic(req: Request, next: Next) -> Response {
use crate::helpers::HydrationStore;
let (mut parts, body) = req.into_parts();
let states = InjectedStates::default();
let store = HydrationStore::new_from_parts(&parts);
parts.extensions.insert(states.clone());
parts.extensions.insert(store.clone());
parts.extensions.insert(HydrationMiddlewareMarker);
let req = Request::from_parts(parts, body);
let res = next.run(req).await;
let injected = states.0.lock().unwrap().clone();
if injected.is_empty() {
return res;
}
let (mut parts, body) = res.into_parts();
let is_html = parts
.headers
.get(http::header::CONTENT_TYPE)
.map(|v| v.to_str().unwrap_or("").contains("text/html"))
.unwrap_or(false);
if !is_html {
return Response::from_parts(parts, body);
}
let json_array = format!("[{}]", injected.join(","));
let script = format!(
"<script>window.__lh_data = {};</script>",
json_array
);
use futures_util::StreamExt;
let stream = body.into_data_stream().chain(futures_util::stream::once(async move {
Ok::<_, axum::Error>(axum::body::Bytes::from(script))
}));
parts.headers.remove(http::header::CONTENT_LENGTH);
Response::from_parts(parts, Body::from_stream(stream))
}