dioxus_fullstack/server/
launch.rs

1//! A launch function that creates an axum router for the LaunchBuilder
2
3use std::any::Any;
4
5use axum::{
6    body::Body,
7    extract::{Request, State},
8    response::IntoResponse,
9};
10use dioxus_cli_config::base_path;
11use dioxus_lib::prelude::*;
12
13use crate::server::{render_handler, RenderHandleState, SSRState};
14
15/// Launch a fullstack app with the given root component, contexts, and config.
16#[allow(unused)]
17pub fn launch(
18    root: fn() -> Element,
19    contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
20    platform_config: Vec<Box<dyn Any>>,
21) -> ! {
22    use crate::{ServeConfig, ServeConfigBuilder};
23
24    #[cfg(not(target_arch = "wasm32"))]
25    tokio::runtime::Runtime::new()
26        .unwrap()
27        .block_on(async move {
28            let platform_config = platform_config
29                .into_iter()
30                .find_map(|cfg| {
31                    cfg.downcast::<ServeConfig>()
32                        .map(|cfg| Result::Ok(*cfg))
33                        .or_else(|cfg| {
34                            cfg.downcast::<ServeConfigBuilder>()
35                                .map(|builder| builder.build())
36                        })
37                        .ok()
38                })
39                .unwrap_or_else(ServeConfig::new);
40
41            // Extend the config's context providers with the context providers from the launch builder
42            let platform_config = platform_config.map(|mut cfg| {
43                let mut contexts = contexts;
44                let cfg_context_providers = cfg.context_providers.clone();
45                for i in 0..cfg_context_providers.len() {
46                    contexts.push(Box::new({
47                        let cfg_context_providers = cfg_context_providers.clone();
48                        move || (cfg_context_providers[i])()
49                    }));
50                }
51                cfg.context_providers = std::sync::Arc::new(contexts);
52                cfg
53            });
54
55            // Get the address the server should run on. If the CLI is running, the CLI proxies fullstack into the main address
56            // and we use the generated address the CLI gives us
57            let address = dioxus_cli_config::fullstack_address_or_localhost();
58
59            use crate::server::DioxusRouterExt;
60
61            struct TryIntoResult(Result<ServeConfig, crate::UnableToLoadIndex>);
62
63            impl TryInto<ServeConfig> for TryIntoResult {
64                type Error = crate::UnableToLoadIndex;
65
66                fn try_into(self) -> Result<ServeConfig, Self::Error> {
67                    self.0
68                }
69            }
70
71            let mut base_path = base_path();
72            let config = platform_config.as_ref().ok().cloned();
73            let dioxus_router =
74                axum::Router::new().serve_dioxus_application(TryIntoResult(platform_config), root);
75            let mut router;
76            match base_path.as_deref() {
77                Some(base_path) => {
78                    let base_path = base_path.trim_matches('/');
79                    // If there is a base path, nest the router under it and serve the root route manually
80                    // Nesting a route in axum only serves /base_path or /base_path/ not both
81                    router = axum::Router::new().nest(&format!("/{base_path}/"), dioxus_router);
82                    async fn root_render_handler(
83                        state: State<RenderHandleState>,
84                        mut request: Request<Body>,
85                    ) -> impl IntoResponse {
86                        // The root of the base path always looks like the root from dioxus fullstack
87                        *request.uri_mut() = "/".parse().unwrap();
88                        render_handler(state, request).await
89                    }
90                    if let Some(cfg) = config {
91                        let ssr_state = SSRState::new(&cfg);
92                        router = router.route(
93                            &format!("/{base_path}"),
94                            axum::routing::method_routing::get(root_render_handler).with_state(
95                                RenderHandleState::new(cfg, root).with_ssr_state(ssr_state),
96                            ),
97                        )
98                    }
99                }
100                None => router = dioxus_router,
101            }
102
103            let router = router.into_make_service();
104            let listener = tokio::net::TcpListener::bind(address).await.unwrap();
105
106            axum::serve(listener, router).await.unwrap();
107        });
108
109    unreachable!("Launching a fullstack app should never return")
110}