use actix_web::{web::Bytes, *};
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();
let mut res: HttpResponseBuilder;
if accept_header == Some("application/json")
|| accept_header == Some("application/x-www-form-urlencoded")
|| accept_header == Some("application/cbor")
{
res = HttpResponse::Ok()
}
else {
let referer = req
.headers()
.get("Referer")
.and_then(|value| value.to_str().ok())
.unwrap_or("/");
res = HttpResponse::SeeOther();
res.insert_header(("Location", referer))
.content_type("application/json");
};
match serialized {
Payload::Binary(data) => {
res.content_type("application/cbor");
res.body(Bytes::from(data))
}
Payload::Url(data) => {
res.content_type("application/x-www-form-urlencoded");
res.body(data)
}
Payload::Json(data) => {
res.content_type("application/json");
res.body(data)
}
}
}
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(
options: RenderOptions,
app_fn: impl Fn(leptos::Scope) -> Element + Clone + 'static,
) -> Route {
web::get().to(move |req: HttpRequest| {
let options = options.clone();
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 pkg_path = &options.pkg_path;
let socket_ip = &options.socket_address.ip().to_string();
let reload_port = options.reload_port;
let leptos_autoreload = match options.environment {
RustEnv::DEV => format!(
r#"
<script crossorigin="">(function () {{
var ws = new WebSocket('ws://{socket_ip}:{reload_port}/autoreload');
ws.onmessage = (ev) => {{
console.log(`Reload message: `);
if (ev.data === 'reload') window.location.reload();
}};
ws.onclose = () => console.warn('Autoreload stopped. Manual reload necessary.');
}})()
</script>
"#
),
RustEnv::PROD => "".to_string(),
};
let head = format!(
r#"<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<script type="module">import init, {{ hydrate }} from '{pkg_path}.js'; init().then(hydrate);</script>
{leptos_autoreload}
"#
);
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>),
)
}
})
}