Merge branch 'fullstack-serve' into intigrate-collect-assets
This commit is contained in:
commit
afb8578605
24 files changed
+421
-128
No files matched your search
@@ -1,4 +1,5 @@
|
||||
use crate::{
|
||||
cfg::ConfigOptsServe,
|
||||
server::{
|
||||
output::{print_console_info, PrettierOptions},
|
||||
setup_file_watcher, setup_file_watcher_hot_reload,
|
||||
@@ -19,7 +20,16 @@ use tokio::sync::broadcast::{self};
|
||||
#[cfg(feature = "plugin")]
|
||||
use plugin::PluginManager;
|
||||
|
||||
pub async fn startup(config: CrateConfig) -> Result<()> {
|
||||
use super::Platform;
|
||||
|
||||
pub async fn startup(config: CrateConfig, serve: &ConfigOptsServe) -> Result<()> {
|
||||
startup_with_platform::<DesktopPlatform>(config, serve).await
|
||||
}
|
||||
|
||||
pub(crate) async fn startup_with_platform<P: Platform + Send + 'static>(
|
||||
config: CrateConfig,
|
||||
serve: &ConfigOptsServe,
|
||||
) -> Result<()> {
|
||||
// ctrl-c shutdown checker
|
||||
let _crate_config = config.clone();
|
||||
let _ = ctrlc::set_handler(move || {
|
||||
@@ -29,17 +39,19 @@ pub async fn startup(config: CrateConfig) -> Result<()> {
|
||||
});
|
||||
|
||||
match config.hot_reload {
|
||||
true => serve_hot_reload(config).await?,
|
||||
false => serve_default(config).await?,
|
||||
true => serve_hot_reload::<P>(config, serve).await?,
|
||||
false => serve_default::<P>(config, serve).await?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Start the server without hot reload
|
||||
pub async fn serve_default(config: CrateConfig) -> Result<()> {
|
||||
let (child, first_build_result) = start_desktop(&config)?;
|
||||
let currently_running_child: RwLock<Child> = RwLock::new(child);
|
||||
async fn serve_default<P: Platform + Send + 'static>(
|
||||
config: CrateConfig,
|
||||
serve: &ConfigOptsServe,
|
||||
) -> Result<()> {
|
||||
let platform = RwLock::new(P::start(&config, serve)?);
|
||||
|
||||
log::info!("🚀 Starting development server...");
|
||||
|
||||
@@ -49,49 +61,29 @@ pub async fn serve_default(config: CrateConfig) -> Result<()> {
|
||||
{
|
||||
let config = config.clone();
|
||||
|
||||
move || {
|
||||
let mut current_child = currently_running_child.write().unwrap();
|
||||
current_child.kill()?;
|
||||
let (child, result) = start_desktop(&config)?;
|
||||
*current_child = child;
|
||||
Ok(result)
|
||||
}
|
||||
move || platform.write().unwrap().rebuild(&config)
|
||||
},
|
||||
&config,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Print serve info
|
||||
print_console_info(
|
||||
&config,
|
||||
PrettierOptions {
|
||||
changed: vec![],
|
||||
warnings: first_build_result.warnings,
|
||||
elapsed_time: first_build_result.elapsed_time,
|
||||
},
|
||||
None,
|
||||
);
|
||||
|
||||
std::future::pending::<()>().await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Start the server without hot reload
|
||||
|
||||
/// Start dx serve with hot reload
|
||||
pub async fn serve_hot_reload(config: CrateConfig) -> Result<()> {
|
||||
let (_, first_build_result) = start_desktop(&config)?;
|
||||
|
||||
println!("🚀 Starting development server...");
|
||||
async fn serve_hot_reload<P: Platform + Send + 'static>(
|
||||
config: CrateConfig,
|
||||
serve: &ConfigOptsServe,
|
||||
) -> Result<()> {
|
||||
let platform = RwLock::new(P::start(&config, serve)?);
|
||||
|
||||
// Setup hot reload
|
||||
let FileMapBuildResult { map, errors } =
|
||||
FileMap::<HtmlCtx>::create(config.crate_dir.clone()).unwrap();
|
||||
|
||||
println!("🚀 Starting development server...");
|
||||
|
||||
for err in errors {
|
||||
log::error!("{}", err);
|
||||
}
|
||||
@@ -119,24 +111,13 @@ pub async fn serve_hot_reload(config: CrateConfig) -> Result<()> {
|
||||
for channel in &mut *channels.lock().unwrap() {
|
||||
send_msg(HotReloadMsg::Shutdown, channel);
|
||||
}
|
||||
Ok(start_desktop(&config)?.1)
|
||||
platform.write().unwrap().rebuild(&config)
|
||||
}
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Print serve info
|
||||
print_console_info(
|
||||
&config,
|
||||
PrettierOptions {
|
||||
changed: vec![],
|
||||
warnings: first_build_result.warnings,
|
||||
elapsed_time: first_build_result.elapsed_time,
|
||||
},
|
||||
None,
|
||||
);
|
||||
|
||||
clear_paths();
|
||||
|
||||
match LocalSocketListener::bind("@dioxusin") {
|
||||
@@ -228,7 +209,7 @@ fn send_msg(msg: HotReloadMsg, channel: &mut impl std::io::Write) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_desktop(config: &CrateConfig) -> Result<(Child, BuildResult)> {
|
||||
fn start_desktop(config: &CrateConfig) -> Result<(Child, BuildResult)> {
|
||||
// Run the desktop application
|
||||
let result = crate::builder::build_desktop(config, true)?;
|
||||
|
||||
@@ -249,3 +230,37 @@ pub fn start_desktop(config: &CrateConfig) -> Result<(Child, BuildResult)> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct DesktopPlatform {
|
||||
currently_running_child: Child,
|
||||
}
|
||||
|
||||
impl Platform for DesktopPlatform {
|
||||
fn start(config: &CrateConfig, _serve: &ConfigOptsServe) -> Result<Self> {
|
||||
let (child, first_build_result) = start_desktop(config)?;
|
||||
|
||||
log::info!("🚀 Starting development server...");
|
||||
|
||||
// Print serve info
|
||||
print_console_info(
|
||||
config,
|
||||
PrettierOptions {
|
||||
changed: vec![],
|
||||
warnings: first_build_result.warnings,
|
||||
elapsed_time: first_build_result.elapsed_time,
|
||||
},
|
||||
None,
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
currently_running_child: child,
|
||||
})
|
||||
}
|
||||
|
||||
fn rebuild(&mut self, config: &CrateConfig) -> Result<BuildResult> {
|
||||
self.currently_running_child.kill()?;
|
||||
let (child, result) = start_desktop(config)?;
|
||||
self.currently_running_child = child;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
use crate::{
|
||||
cfg::{ConfigOptsBuild, ConfigOptsServe},
|
||||
CrateConfig, Result,
|
||||
};
|
||||
|
||||
use super::{desktop, Platform};
|
||||
|
||||
pub async fn startup(config: CrateConfig, serve: &ConfigOptsServe) -> Result<()> {
|
||||
desktop::startup_with_platform::<FullstackPlatform>(config, serve).await
|
||||
}
|
||||
|
||||
struct FullstackPlatform {
|
||||
serve: ConfigOptsServe,
|
||||
desktop: desktop::DesktopPlatform,
|
||||
}
|
||||
|
||||
impl Platform for FullstackPlatform {
|
||||
fn start(config: &CrateConfig, serve: &ConfigOptsServe) -> Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
{
|
||||
build_web(serve.clone())?;
|
||||
}
|
||||
|
||||
let mut desktop_config = config.clone();
|
||||
let desktop_feature = serve.server_feature.clone();
|
||||
let features = &mut desktop_config.features;
|
||||
match features {
|
||||
Some(features) => {
|
||||
features.push(desktop_feature);
|
||||
}
|
||||
None => desktop_config.features = Some(vec![desktop_feature]),
|
||||
};
|
||||
let desktop = desktop::DesktopPlatform::start(&desktop_config, serve)?;
|
||||
|
||||
Ok(Self {
|
||||
desktop,
|
||||
serve: serve.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
fn rebuild(&mut self, crate_config: &CrateConfig) -> Result<crate::BuildResult> {
|
||||
build_web(self.serve.clone())?;
|
||||
{
|
||||
let mut desktop_config = crate_config.clone();
|
||||
let desktop_feature = self.serve.server_feature.clone();
|
||||
let features = &mut desktop_config.features;
|
||||
match features {
|
||||
Some(features) => {
|
||||
features.push(desktop_feature);
|
||||
}
|
||||
None => desktop_config.features = Some(vec![desktop_feature]),
|
||||
};
|
||||
self.desktop.rebuild(&desktop_config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_web(serve: ConfigOptsServe) -> Result<()> {
|
||||
let mut web_config: ConfigOptsBuild = serve.into();
|
||||
let web_feature = web_config.client_feature.clone();
|
||||
let features = &mut web_config.features;
|
||||
match features {
|
||||
Some(features) => {
|
||||
features.push(web_feature);
|
||||
}
|
||||
None => web_config.features = Some(vec![web_feature]),
|
||||
};
|
||||
web_config.platform = Some(crate::cfg::Platform::Web);
|
||||
crate::cli::build::Build { build: web_config }.build(None)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{BuildResult, CrateConfig, Result};
|
||||
use crate::{cfg::ConfigOptsServe, BuildResult, CrateConfig, Result};
|
||||
|
||||
use cargo_metadata::diagnostic::Diagnostic;
|
||||
use dioxus_core::Template;
|
||||
@@ -14,6 +14,7 @@ use tokio::sync::broadcast::Sender;
|
||||
mod output;
|
||||
use output::*;
|
||||
pub mod desktop;
|
||||
pub mod fullstack;
|
||||
pub mod web;
|
||||
|
||||
/// Sets up a file watcher
|
||||
@@ -180,3 +181,10 @@ async fn setup_file_watcher_hot_reload<F: Fn() -> Result<BuildResult> + Send + '
|
||||
|
||||
Ok(watcher)
|
||||
}
|
||||
|
||||
pub(crate) trait Platform {
|
||||
fn start(config: &CrateConfig, serve: &ConfigOptsServe) -> Result<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
fn rebuild(&mut self, config: &CrateConfig) -> Result<BuildResult>;
|
||||
}
|
||||
@@ -5,7 +5,7 @@ use crate::{
|
||||
output::{print_console_info, PrettierOptions, WebServerInfo},
|
||||
setup_file_watcher, setup_file_watcher_hot_reload,
|
||||
},
|
||||
BuildResult, CrateConfig, Result,
|
||||
BuildResult, CrateConfig, Result, WebHttpsConfig,
|
||||
};
|
||||
use axum::{
|
||||
body::{Full, HttpBody},
|
||||
@@ -67,7 +67,7 @@ pub async fn startup(port: u16, config: CrateConfig, start_browser: bool) -> Res
|
||||
}
|
||||
|
||||
/// Start the server without hot reload
|
||||
pub async fn serve_default(
|
||||
async fn serve_default(
|
||||
ip: String,
|
||||
port: u16,
|
||||
config: CrateConfig,
|
||||
@@ -128,7 +128,7 @@ pub async fn serve_default(
|
||||
}
|
||||
|
||||
/// Start dx serve with hot reload
|
||||
pub async fn serve_hot_reload(
|
||||
async fn serve_hot_reload(
|
||||
ip: String,
|
||||
port: u16,
|
||||
config: CrateConfig,
|
||||
@@ -218,67 +218,12 @@ async fn get_rustls(config: &CrateConfig) -> Result<Option<RustlsConfig>> {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let (cert_path, key_path) = match web_config.mkcert {
|
||||
let (cert_path, key_path) = if let Some(true) = web_config.mkcert {
|
||||
// mkcert, use it
|
||||
Some(true) => {
|
||||
// Get paths to store certs, otherwise use ssl/item.pem
|
||||
let key_path = web_config
|
||||
.key_path
|
||||
.clone()
|
||||
.unwrap_or(DEFAULT_KEY_PATH.to_string());
|
||||
|
||||
let cert_path = web_config
|
||||
.cert_path
|
||||
.clone()
|
||||
.unwrap_or(DEFAULT_CERT_PATH.to_string());
|
||||
|
||||
// Create ssl directory if using defaults
|
||||
if key_path == DEFAULT_KEY_PATH && cert_path == DEFAULT_CERT_PATH {
|
||||
_ = fs::create_dir("ssl");
|
||||
}
|
||||
|
||||
let cmd = Command::new("mkcert")
|
||||
.args([
|
||||
"-install",
|
||||
"-key-file",
|
||||
&key_path,
|
||||
"-cert-file",
|
||||
&cert_path,
|
||||
"localhost",
|
||||
"::1",
|
||||
"127.0.0.1",
|
||||
])
|
||||
.spawn();
|
||||
|
||||
match cmd {
|
||||
Err(e) => {
|
||||
match e.kind() {
|
||||
io::ErrorKind::NotFound => log::error!("mkcert is not installed. See https://github.com/FiloSottile/mkcert#installation for installation instructions."),
|
||||
e => log::error!("an error occured while generating mkcert certificates: {}", e.to_string()),
|
||||
};
|
||||
return Err("failed to generate mkcert certificates".into());
|
||||
}
|
||||
Ok(mut cmd) => {
|
||||
cmd.wait()?;
|
||||
}
|
||||
}
|
||||
|
||||
(cert_path, key_path)
|
||||
}
|
||||
// not mkcert
|
||||
Some(false) => {
|
||||
// get paths to cert & key
|
||||
if let (Some(key), Some(cert)) =
|
||||
(web_config.key_path.clone(), web_config.cert_path.clone())
|
||||
{
|
||||
(cert, key)
|
||||
} else {
|
||||
// missing cert or key
|
||||
return Err("https is enabled but cert or key path is missing".into());
|
||||
}
|
||||
}
|
||||
// other
|
||||
_ => return Ok(None),
|
||||
get_rustls_with_mkcert(web_config)?
|
||||
} else {
|
||||
// if mkcert not specified or false, don't use it
|
||||
get_rustls_without_mkcert(web_config)?
|
||||
};
|
||||
|
||||
Ok(Some(
|
||||
@@ -286,6 +231,62 @@ async fn get_rustls(config: &CrateConfig) -> Result<Option<RustlsConfig>> {
|
||||
))
|
||||
}
|
||||
|
||||
fn get_rustls_with_mkcert(web_config: &WebHttpsConfig) -> Result<(String, String)> {
|
||||
// Get paths to store certs, otherwise use ssl/item.pem
|
||||
let key_path = web_config
|
||||
.key_path
|
||||
.clone()
|
||||
.unwrap_or(DEFAULT_KEY_PATH.to_string());
|
||||
|
||||
let cert_path = web_config
|
||||
.cert_path
|
||||
.clone()
|
||||
.unwrap_or(DEFAULT_CERT_PATH.to_string());
|
||||
|
||||
// Create ssl directory if using defaults
|
||||
if key_path == DEFAULT_KEY_PATH && cert_path == DEFAULT_CERT_PATH {
|
||||
_ = fs::create_dir("ssl");
|
||||
}
|
||||
|
||||
let cmd = Command::new("mkcert")
|
||||
.args([
|
||||
"-install",
|
||||
"-key-file",
|
||||
&key_path,
|
||||
"-cert-file",
|
||||
&cert_path,
|
||||
"localhost",
|
||||
"::1",
|
||||
"127.0.0.1",
|
||||
])
|
||||
.spawn();
|
||||
|
||||
match cmd {
|
||||
Err(e) => {
|
||||
match e.kind() {
|
||||
io::ErrorKind::NotFound => log::error!("mkcert is not installed. See https://github.com/FiloSottile/mkcert#installation for installation instructions."),
|
||||
e => log::error!("an error occured while generating mkcert certificates: {}", e.to_string()),
|
||||
};
|
||||
return Err("failed to generate mkcert certificates".into());
|
||||
}
|
||||
Ok(mut cmd) => {
|
||||
cmd.wait()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok((cert_path, key_path))
|
||||
}
|
||||
|
||||
fn get_rustls_without_mkcert(web_config: &WebHttpsConfig) -> Result<(String, String)> {
|
||||
// get paths to cert & key
|
||||
if let (Some(key), Some(cert)) = (web_config.key_path.clone(), web_config.cert_path.clone()) {
|
||||
Ok((cert, key))
|
||||
} else {
|
||||
// missing cert or key
|
||||
Err("https is enabled but cert or key path is missing".into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets up and returns a router
|
||||
async fn setup_router(
|
||||
config: CrateConfig,
|
||||
|
||||
Reference in new issue
Block a user