From 305ae590861a40a9d204e0d6a4018f353b0a52ce Mon Sep 17 00:00:00 2001 From: Valerio Ageno <51341197+Valerioageno@users.noreply.github.com> Date: Thu, 23 Jan 2025 18:43:17 +0100 Subject: [PATCH] feat: pass config from server to client (#407) --- crates/tuono_internal/src/config.rs | 6 +-- crates/tuono_lib/Cargo.toml | 1 + crates/tuono_lib/src/config.rs | 4 ++ crates/tuono_lib/src/lib.rs | 1 + crates/tuono_lib/src/payload.rs | 20 +++++++- crates/tuono_lib/src/server.rs | 23 ++++++--- crates/tuono_lib/src/vite_reverse_proxy.rs | 15 ++++-- crates/tuono_lib/src/vite_websocket_proxy.rs | 14 ++++- packages/tuono-router/src/types.ts | 4 ++ packages/tuono/src/shared/DevResources.tsx | 54 +++++++++++--------- 10 files changed, 102 insertions(+), 40 deletions(-) create mode 100644 crates/tuono_lib/src/config.rs diff --git a/crates/tuono_internal/src/config.rs b/crates/tuono_internal/src/config.rs index ccebcad6..9e2229ff 100644 --- a/crates/tuono_internal/src/config.rs +++ b/crates/tuono_internal/src/config.rs @@ -1,15 +1,15 @@ -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use std::fs::read_to_string; use std::io; use std::path::PathBuf; -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Serialize, Debug, Clone)] pub struct ServerConfig { pub host: String, pub port: u16, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Serialize, Debug, Clone)] pub struct Config { pub server: ServerConfig, } diff --git a/crates/tuono_lib/Cargo.toml b/crates/tuono_lib/Cargo.toml index 5fb0dc5f..766d8891 100644 --- a/crates/tuono_lib/Cargo.toml +++ b/crates/tuono_lib/Cargo.toml @@ -33,6 +33,7 @@ tower-http = {version = "0.6.0", features = ["fs"]} colored = "2.1.0" tuono_lib_macros = {path = "../tuono_lib_macros", version = "0.17.3"} +tuono_internal = {path = "../tuono_internal", version = "0.17.3"} # Match the same version used by axum tokio-tungstenite = "0.24.0" futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] } diff --git a/crates/tuono_lib/src/config.rs b/crates/tuono_lib/src/config.rs new file mode 100644 index 00000000..1ea6138d --- /dev/null +++ b/crates/tuono_lib/src/config.rs @@ -0,0 +1,4 @@ +use once_cell::sync::OnceCell; +use tuono_internal::config::Config; + +pub static GLOBAL_CONFIG: OnceCell = OnceCell::new(); diff --git a/crates/tuono_lib/src/lib.rs b/crates/tuono_lib/src/lib.rs index 2be702c4..7da4b60e 100644 --- a/crates/tuono_lib/src/lib.rs +++ b/crates/tuono_lib/src/lib.rs @@ -4,6 +4,7 @@ //! You can find the full documentation at [tuono.dev](https://tuono.dev/) mod catch_all; +mod config; mod logger; mod manifest; mod mode; diff --git a/crates/tuono_lib/src/payload.rs b/crates/tuono_lib/src/payload.rs index 22efacea..3e04e5cd 100644 --- a/crates/tuono_lib/src/payload.rs +++ b/crates/tuono_lib/src/payload.rs @@ -1,8 +1,10 @@ +use crate::config::GLOBAL_CONFIG; use crate::manifest::MANIFEST; use crate::mode::{Mode, GLOBAL_MODE}; use erased_serde::Serialize; use regex::Regex; use serde::Serialize as SerdeSerialize; +use tuono_internal::config::ServerConfig; use crate::request::{Location, Request}; @@ -21,16 +23,31 @@ pub struct Payload<'a> { js_bundles: Option>, #[serde(rename(serialize = "cssBundles"))] css_bundles: Option>, + #[serde(rename(serialize = "devServerConfig"))] + dev_server_config: Option<&'a ServerConfig>, } impl<'a> Payload<'a> { pub fn new(req: &'a Request, props: &'a dyn Serialize) -> Payload<'a> { + let config = GLOBAL_CONFIG + .get() + .expect("Failed to load the current config"); + + let mode = *GLOBAL_MODE.get().expect("Failed to load the current mode"); + + let dev_server_config = if mode == Mode::Dev { + Some(&config.server) + } else { + None + }; + Payload { router: req.location(), props, - mode: *GLOBAL_MODE.get().expect("Failed to load the current mode"), + mode, js_bundles: None, css_bundles: None, + dev_server_config, } } @@ -221,6 +238,7 @@ mod tests { mode, js_bundles: None, css_bundles: None, + dev_server_config: None, } } diff --git a/crates/tuono_lib/src/server.rs b/crates/tuono_lib/src/server.rs index e7181132..e6a918af 100644 --- a/crates/tuono_lib/src/server.rs +++ b/crates/tuono_lib/src/server.rs @@ -1,9 +1,11 @@ +use crate::config::GLOBAL_CONFIG; use crate::manifest::load_manifest; use crate::mode::{Mode, GLOBAL_MODE}; use axum::routing::{get, Router}; use colored::Colorize; use ssr_rs::Ssr; use tower_http::services::ServeDir; +use tuono_internal::config::Config; use crate::{ catch_all::catch_all, logger::LoggerLayer, vite_reverse_proxy::vite_reverse_proxy, @@ -23,6 +25,9 @@ impl Server { Ssr::create_platform(); GLOBAL_MODE.set(mode).unwrap(); + GLOBAL_CONFIG + .set(Config::get().expect("[SERVER] Failed to load config")) + .unwrap(); if mode == Mode::Prod { load_manifest() @@ -32,10 +37,19 @@ impl Server { } pub async fn start(&self) { - let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + let config = GLOBAL_CONFIG + .get() + .expect("Failed to get the internal config"); + + let server_http_address = format!("{}:{}", config.server.host, config.server.port); + let listener = tokio::net::TcpListener::bind(&server_http_address) + .await + .unwrap(); + + let server_url = format!("http://{}", &server_http_address); if self.mode == Mode::Dev { - println!(" Ready at: {}\n", "http://localhost:3000".blue().bold()); + println!(" Ready at: {}\n", &server_url.blue().bold()); let router = self .router .to_owned() @@ -51,10 +65,7 @@ impl Server { .await .expect("Failed to serve development server"); } else { - println!( - " Production server at: {}\n", - "http://localhost:3000".blue().bold() - ); + println!(" Production server at: {}\n", &server_url.blue().bold()); let router = self .router .to_owned() diff --git a/crates/tuono_lib/src/vite_reverse_proxy.rs b/crates/tuono_lib/src/vite_reverse_proxy.rs index ef56952c..e0e58074 100644 --- a/crates/tuono_lib/src/vite_reverse_proxy.rs +++ b/crates/tuono_lib/src/vite_reverse_proxy.rs @@ -1,3 +1,4 @@ +use crate::config::GLOBAL_CONFIG; use axum::body::Body; use axum::extract::Path; @@ -5,12 +6,20 @@ use axum::http::{HeaderName, HeaderValue}; use axum::response::{IntoResponse, Response}; use reqwest::Client; -const VITE_URL: &str = "http://localhost:3001/vite-server"; - pub async fn vite_reverse_proxy(Path(path): Path) -> impl IntoResponse { let client = Client::new(); - match client.get(format!("{VITE_URL}/{path}")).send().await { + let config = GLOBAL_CONFIG + .get() + .expect("Failed to get the internal config"); + + let vite_url = format!( + "http://{}:{}/vite-server", + config.server.host, + config.server.port + 1 + ); + + match client.get(format!("{vite_url}/{path}")).send().await { Ok(res) => { let mut response_builder = Response::builder().status(res.status().as_u16()); diff --git a/crates/tuono_lib/src/vite_websocket_proxy.rs b/crates/tuono_lib/src/vite_websocket_proxy.rs index 0dce599f..cf61cbfc 100644 --- a/crates/tuono_lib/src/vite_websocket_proxy.rs +++ b/crates/tuono_lib/src/vite_websocket_proxy.rs @@ -1,3 +1,4 @@ +use crate::config::GLOBAL_CONFIG; use axum::extract::ws::{self, WebSocket, WebSocketUpgrade}; use axum::response::IntoResponse; use futures_util::{SinkExt, StreamExt}; @@ -6,7 +7,6 @@ use tokio_tungstenite::tungstenite::{Error, Message}; use tungstenite::client::IntoClientRequest; use tungstenite::ClientRequestBuilder; -const VITE_WS: &str = "ws://localhost:3001/vite-server/"; const VITE_WS_PROTOCOL: &str = "vite-hmr"; /// This is the entry point to proxy the vite's WebSocket. @@ -28,7 +28,17 @@ async fn handle_socket(mut tuono_socket: WebSocket) { return; } - let vite_ws_request = ClientRequestBuilder::new(VITE_WS.parse().unwrap()) + let config = GLOBAL_CONFIG + .get() + .expect("Failed to get the internal config"); + + let vite_ws = format!( + "ws://{}:{}/vite-server/", + config.server.host, + config.server.port + 1 + ); + + let vite_ws_request = ClientRequestBuilder::new(vite_ws.parse().unwrap()) .with_sub_protocol(VITE_WS_PROTOCOL) .into_client_request() .expect("Failed to create vite WS request"); diff --git a/packages/tuono-router/src/types.ts b/packages/tuono-router/src/types.ts index ad1c5667..593790f3 100644 --- a/packages/tuono-router/src/types.ts +++ b/packages/tuono-router/src/types.ts @@ -17,6 +17,10 @@ export interface ServerProps { jsBundles: Array cssBundles: Array mode: 'Dev' | 'Prod' + devServerConfig: { + port: number + host: string + } } export interface RouteProps { diff --git a/packages/tuono/src/shared/DevResources.tsx b/packages/tuono/src/shared/DevResources.tsx index 327cd65f..595954ca 100644 --- a/packages/tuono/src/shared/DevResources.tsx +++ b/packages/tuono/src/shared/DevResources.tsx @@ -1,29 +1,33 @@ import type { JSX } from 'react' +import { useRouterContext } from 'tuono-router' -const TUONO_DEV_SERVER_PORT = 3000 const VITE_PROXY_PATH = '/vite-server' -const SCRIPT_BASE_URL = `http://localhost:${TUONO_DEV_SERVER_PORT}${VITE_PROXY_PATH}` +const DEFAULT_SERVER_CONFIG = { host: 'localhost', port: 3000 } -export const DevResources = (): JSX.Element => ( - <> - - - - -) +export const DevResources = (): JSX.Element => { + const { serverSideProps } = useRouterContext() + const { host, port } = + serverSideProps?.devServerConfig ?? DEFAULT_SERVER_CONFIG + + const viteBaseUrl = `http://${host}:${port}${VITE_PROXY_PATH}` + + return ( + <> + + + + + ) +}