//! Launch helper macros for fullstack apps #![allow(unused)] use crate::prelude::*; use dioxus::prelude::*; #[cfg(feature = "router")] use dioxus_router::prelude::*; /// A builder for a fullstack app. pub struct LaunchBuilder { component: Component, #[cfg(not(feature = "ssr"))] props: Props, #[cfg(feature = "ssr")] server_fn_route: &'static str, #[cfg(feature = "ssr")] server_cfg: ServeConfigBuilder, #[cfg(feature = "ssr")] addr: std::net::SocketAddr, #[cfg(feature = "web")] web_cfg: dioxus_web::Config, #[cfg(feature = "desktop")] desktop_cfg: dioxus_desktop::Config, } impl LaunchBuilder { /// Create a new builder for a fullstack app. pub fn new(component: Component) -> Self where Props: Default, { Self::new_with_props(component, Default::default()) } /// Create a new builder for a fullstack app with props. pub fn new_with_props(component: Component, props: Props) -> Self where Props: Default, { Self { component, #[cfg(not(feature = "ssr"))] props, #[cfg(feature = "ssr")] server_fn_route: "", #[cfg(feature = "ssr")] addr: std::net::SocketAddr::from(([127, 0, 0, 1], 8080)), #[cfg(feature = "ssr")] server_cfg: ServeConfigBuilder::new(component, props), #[cfg(feature = "web")] web_cfg: dioxus_web::Config::default(), #[cfg(feature = "desktop")] desktop_cfg: dioxus_desktop::Config::default(), } } /// Set the address to serve the app on. #[cfg(feature = "ssr")] pub fn addr(self, addr: impl Into) -> Self { let addr = addr.into(); Self { addr, ..self } } /// Set the route to the server functions. #[cfg(feature = "ssr")] pub fn server_fn_route(self, server_fn_route: &'static str) -> Self { Self { server_fn_route, ..self } } /// Set the incremental renderer config. #[cfg(feature = "ssr")] pub fn incremental(self, cfg: IncrementalRendererConfig) -> Self { Self { server_cfg: self.server_cfg.incremental(cfg), ..self } } /// Set the server config. #[cfg(feature = "ssr")] pub fn server_cfg(self, server_cfg: ServeConfigBuilder) -> Self { Self { server_cfg, ..self } } /// Set the web config. #[cfg(feature = "web")] pub fn web_cfg(self, web_cfg: dioxus_web::Config) -> Self { Self { web_cfg, ..self } } /// Set the desktop config. #[cfg(feature = "desktop")] pub fn desktop_cfg(self, desktop_cfg: dioxus_desktop::Config) -> Self { Self { desktop_cfg, ..self } } /// Launch the app. pub fn launch(self) { #[cfg(feature = "ssr")] tokio::runtime::Runtime::new() .unwrap() .block_on(async move { self.launch_server().await; }); #[cfg(not(feature = "ssr"))] { #[cfg(feature = "web")] self.launch_web(); #[cfg(feature = "desktop")] self.launch_desktop(); } } #[cfg(feature = "web")] fn launch_web(self) { let cfg = self.web_cfg.hydrate(true); dioxus_web::launch_with_props(self.component, get_root_props_from_document().unwrap(), cfg); } #[cfg(feature = "desktop")] fn launch_desktop(self) { let cfg = self.desktop_cfg; dioxus_desktop::launch_with_props(self.component, self.props, cfg); } /// Launch a server with the given configuration /// This will use the routing integration of the currently enabled integration feature #[cfg(feature = "ssr")] async fn launch_server(self) { let addr = self.addr; println!("Listening on {}", addr); let cfg = self.server_cfg.build(); let server_fn_route = self.server_fn_route; #[cfg(all(feature = "axum", not(feature = "warp"), not(feature = "salvo")))] { use crate::adapters::axum_adapter::{render_handler, DioxusRouterExt}; use axum::routing::get; use tower::ServiceBuilder; let ssr_state = SSRState::new(&cfg); let router = axum::Router::new().register_server_fns(server_fn_route); #[cfg(not(feature = "desktop"))] let router = router .serve_static_assets(cfg.assets_path) .connect_hot_reload() .fallback(get(render_handler).with_state((cfg, ssr_state))); let router = router .layer( ServiceBuilder::new() .layer(tower_http::compression::CompressionLayer::new().gzip(true)), ) .into_make_service(); axum::Server::bind(&addr).serve(router).await.unwrap(); } #[cfg(all(feature = "warp", not(feature = "axum"), not(feature = "salvo")))] { use warp::Filter; // First register the server functions let router = register_server_fns(server_fn_route); #[cfg(not(feature = "desktop"))] let router = { // Serve the dist folder and the index.html file let serve_dir = warp::fs::dir(cfg.assets_path); router .or(connect_hot_reload()) // Then the index route .or(warp::path::end().and(render_ssr(cfg.clone()))) // Then the static assets .or(serve_dir) // Then all other routes .or(render_ssr(cfg)) }; warp::serve(router.boxed().with(warp::filters::compression::gzip())) .run(addr) .await; } #[cfg(all(feature = "salvo", not(feature = "axum"), not(feature = "warp")))] { use crate::adapters::salvo_adapter::{DioxusRouterExt, SSRHandler}; use salvo::conn::Listener; let router = salvo::Router::new().register_server_fns(server_fn_route); #[cfg(not(feature = "desktop"))] let router = router .serve_static_assets(cfg.assets_path) .connect_hot_reload() .push(salvo::Router::with_path("/<**any_path>").get(SSRHandler::new(cfg))); let router = router.hoop( salvo::compression::Compression::new() .enable_gzip(salvo::prelude::CompressionLevel::Default), ); salvo::Server::new(salvo::conn::tcp::TcpListener::new(addr).bind().await) .serve(router) .await; } } } #[cfg(feature = "router")] impl LaunchBuilder> where ::Err: std::fmt::Display, R: Clone + serde::Serialize + serde::de::DeserializeOwned + Send + Sync + 'static, { /// Create a new launch builder for the given router. pub fn router() -> Self { let component = crate::router::RouteWithCfg::; let props = crate::router::FullstackRouterConfig::default(); Self::new_with_props(component, props) } }