use actix_web::*;
use futures::StreamExt;
use leptos::*;
use leptos_meta::*;
use leptos_router::*;
pub fn handle_server_fns() -> Route {
web::post().to(
|req: HttpRequest, params: web::Path<String>, body: web::Bytes| async move {
{
let path = params.into_inner();
let accept_header = req
.headers()
.get("Accept")
.and_then(|value| value.to_str().ok());
if let Some(server_fn) = server_fn_by_path(path.as_str()) {
let body: &[u8] = &body;
let runtime = create_runtime();
let (cx, disposer) = raw_scope_and_disposer(runtime);
provide_context(cx, req.clone());
match server_fn(cx, body).await {
Ok(serialized) => {
disposer.dispose();
runtime.dispose();
if let Some("application/json") = accept_header {
HttpResponse::Ok().body(serialized)
}
else {
let referer = req
.headers()
.get("Referer")
.and_then(|value| value.to_str().ok())
.unwrap_or("/");
HttpResponse::SeeOther()
.insert_header(("Location", referer))
.content_type("application/json")
.body(serialized)
}
}
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
} else {
HttpResponse::BadRequest()
.body(format!("Could not find a server function at that route."))
}
}
},
)
}
pub fn render_app_to_stream(
client_pkg_name: &'static str,
app_fn: impl Fn(leptos::Scope) -> Element + Clone + 'static,
) -> Route {
web::get().to(move |req: HttpRequest| {
let app_fn = app_fn.clone();
async move {
let path = req.path();
let query = req.query_string();
let path = if query.is_empty() {
"http://leptos".to_string() + path
} else {
"http://leptos".to_string() + path + "?" + query
};
let app = {
let app_fn = app_fn.clone();
move |cx| {
let integration = ServerIntegration { path: path.clone() };
provide_context(cx, RouterIntegrationContext::new(integration));
provide_context(cx, MetaContext::new());
provide_context(cx, req.clone());
(app_fn)(cx)
}
};
let head = format!(r#"<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<script type="module">import init, {{ hydrate }} from '/pkg/{client_pkg_name}.js'; init().then(hydrate);</script>"#);
let tail = "</body></html>";
HttpResponse::Ok().content_type("text/html").streaming(
futures::stream::once(async move { head.clone() })
.chain(render_to_stream(move |cx| {
let app = app(cx);
let head = use_context::<MetaContext>(cx)
.map(|meta| meta.dehydrate())
.unwrap_or_default();
format!("{head}</head><body>{app}")
}))
.chain(futures::stream::once(async { tail.to_string() }))
.map(|html| Ok(web::Bytes::from(html)) as Result<web::Bytes>),
)
}
})
}