mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-10 06:34:20 +00:00
Merge branch 'master' into signals
This commit is contained in:
commit
0dc602eb32
23 changed files with 121 additions and 101 deletions
|
@ -97,3 +97,6 @@ plugin = ["mlua"]
|
|||
[[bin]]
|
||||
path = "src/main.rs"
|
||||
name = "dx"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.3"
|
||||
|
|
|
@ -15,6 +15,7 @@ Commands:
|
|||
serve Build, watch & serve the Rust WASM app and all of its assets
|
||||
create Init a new project for Dioxus
|
||||
clean Clean output artifacts
|
||||
bundle Bundle the Rust desktop app and all of its assets
|
||||
version Print the version of this extension
|
||||
fmt Format some rsx
|
||||
check Check the Rust files in the project for issues
|
||||
|
|
|
@ -144,7 +144,7 @@ Configeration related to static resources your application uses in development:
|
|||
Configeration related to any proxies your application requires durring development. Proxies will forward requests to a new service
|
||||
|
||||
```
|
||||
[web.proxy]
|
||||
[[web.proxy]]
|
||||
# configuration
|
||||
```
|
||||
|
||||
|
|
|
@ -236,10 +236,9 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result<BuildResult> {
|
|||
}
|
||||
}
|
||||
|
||||
let t_end = std::time::Instant::now();
|
||||
Ok(BuildResult {
|
||||
warnings: warning_messages,
|
||||
elapsed_time: (t_end - t_start).as_millis(),
|
||||
elapsed_time: t_start.elapsed().as_millis(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -360,7 +359,7 @@ pub fn build_desktop(config: &CrateConfig, _is_serve: bool) -> Result<BuildResul
|
|||
|
||||
Ok(BuildResult {
|
||||
warnings: warning_messages,
|
||||
elapsed_time: (t_start - std::time::Instant::now()).as_millis(),
|
||||
elapsed_time: t_start.elapsed().as_millis(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ use super::*;
|
|||
// For reference, the rustfmt main.rs file
|
||||
// https://github.com/rust-lang/rustfmt/blob/master/src/bin/main.rs
|
||||
|
||||
/// Build the Rust WASM app and all of its assets.
|
||||
/// Format some rsx
|
||||
#[derive(Clone, Debug, Parser)]
|
||||
pub struct Autoformat {
|
||||
/// Run in 'check' mode. Exits with 0 if input is formatted correctly. Exits
|
||||
|
|
|
@ -6,7 +6,7 @@ use tauri_bundler::{BundleSettings, PackageSettings, SettingsBuilder};
|
|||
use super::*;
|
||||
use crate::{build_desktop, cfg::ConfigOptsBundle};
|
||||
|
||||
/// Build the Rust WASM app and all of its assets.
|
||||
/// Bundle the Rust desktop app and all of its assets
|
||||
#[derive(Clone, Debug, Parser)]
|
||||
#[clap(name = "bundle")]
|
||||
pub struct Bundle {
|
||||
|
|
|
@ -101,13 +101,6 @@ pub enum Platform {
|
|||
Desktop,
|
||||
}
|
||||
|
||||
/// Ensure the given value for `--public-url` is formatted correctly.
|
||||
pub fn parse_public_url(val: &str) -> String {
|
||||
let prefix = if !val.starts_with('/') { "/" } else { "" };
|
||||
let suffix = if !val.ends_with('/') { "/" } else { "" };
|
||||
format!("{}{}{}", prefix, val, suffix)
|
||||
}
|
||||
|
||||
/// Config options for the bundling system.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Parser)]
|
||||
pub struct ConfigOptsBundle {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::*;
|
||||
|
||||
/// Build the Rust WASM app and all of its assets.
|
||||
/// Dioxus config file controls
|
||||
#[derive(Clone, Debug, Deserialize, Subcommand)]
|
||||
#[clap(name = "config")]
|
||||
pub enum Config {
|
||||
|
|
|
@ -46,15 +46,15 @@ impl Create {
|
|||
toml.as_table_mut().fmt();
|
||||
|
||||
let as_string = toml.to_string();
|
||||
let new_string = remove_tripple_newlines(&as_string);
|
||||
let new_string = remove_triple_newlines(&as_string);
|
||||
let mut file = std::fs::File::create(toml_path)?;
|
||||
file.write_all(new_string.as_bytes())?;
|
||||
}
|
||||
|
||||
// remove any tripple newlines from the readme
|
||||
// remove any triple newlines from the readme
|
||||
let readme_path = path.join("README.md");
|
||||
let readme = std::fs::read_to_string(&readme_path)?;
|
||||
let new_readme = remove_tripple_newlines(&readme);
|
||||
let new_readme = remove_triple_newlines(&readme);
|
||||
let mut file = std::fs::File::create(readme_path)?;
|
||||
file.write_all(new_readme.as_bytes())?;
|
||||
|
||||
|
@ -64,7 +64,7 @@ impl Create {
|
|||
}
|
||||
}
|
||||
|
||||
fn remove_tripple_newlines(string: &str) -> String {
|
||||
fn remove_triple_newlines(string: &str) -> String {
|
||||
let mut new_string = String::new();
|
||||
for char in string.chars() {
|
||||
if char == '\n' && new_string.ends_with("\n\n") {
|
||||
|
|
|
@ -72,6 +72,7 @@ pub enum Commands {
|
|||
#[clap(name = "fmt")]
|
||||
Autoformat(autoformat::Autoformat),
|
||||
|
||||
/// Check the Rust files in the project for issues.
|
||||
#[clap(name = "check")]
|
||||
Check(check::Check),
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use super::*;
|
||||
|
||||
/// Build the Rust WASM app and all of its assets.
|
||||
/// Manage plugins for dioxus cli
|
||||
#[derive(Clone, Debug, Deserialize, Subcommand)]
|
||||
#[clap(name = "plugin")]
|
||||
pub enum Plugin {
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
use crate::tools;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Build the Rust WASM app and all of its assets.
|
||||
#[derive(Clone, Debug, Deserialize, Subcommand)]
|
||||
#[clap(name = "tool")]
|
||||
pub enum Tool {
|
||||
/// Return all dioxus-cli support tools.
|
||||
List {},
|
||||
/// Get default app install path.
|
||||
AppPath {},
|
||||
/// Install a new tool.
|
||||
Add { name: String },
|
||||
}
|
||||
|
||||
impl Tool {
|
||||
pub async fn tool(self) -> Result<()> {
|
||||
match self {
|
||||
Tool::List {} => {
|
||||
for item in tools::tool_list() {
|
||||
if tools::Tool::from_str(item).unwrap().is_installed() {
|
||||
println!("- {item} [installed]");
|
||||
} else {
|
||||
println!("- {item}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Tool::AppPath {} => {
|
||||
if let Some(v) = tools::tools_path().to_str() {
|
||||
println!("{}", v);
|
||||
} else {
|
||||
return custom_error!("Tools path get failed.");
|
||||
}
|
||||
}
|
||||
Tool::Add { name } => {
|
||||
let tool_list = tools::tool_list();
|
||||
|
||||
if !tool_list.contains(&name.as_str()) {
|
||||
return custom_error!("Tool {name} not found.");
|
||||
}
|
||||
let target_tool = tools::Tool::from_str(&name).unwrap();
|
||||
|
||||
if target_tool.is_installed() {
|
||||
log::warn!("Tool {name} is installed.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
log::info!("Start to download tool package...");
|
||||
if let Err(e) = target_tool.download_package().await {
|
||||
return custom_error!("Tool download failed: {e}");
|
||||
}
|
||||
|
||||
log::info!("Start to install tool package...");
|
||||
if let Err(e) = target_tool.install_package().await {
|
||||
return custom_error!("Tool install failed: {e}");
|
||||
}
|
||||
|
||||
log::info!("Tool {name} installed successfully!");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ use dioxus_rsx::{BodyNode, CallBody};
|
|||
|
||||
use super::*;
|
||||
|
||||
/// Build the Rust WASM app and all of its assets.
|
||||
/// Translate some source file into Dioxus code
|
||||
#[derive(Clone, Debug, Parser)]
|
||||
#[clap(name = "translate")]
|
||||
pub struct Translate {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::*;
|
||||
|
||||
/// Build the Rust WASM app and all of its assets.
|
||||
/// Print the version of this extension
|
||||
#[derive(Clone, Debug, Parser)]
|
||||
#[clap(name = "version")]
|
||||
pub struct Version {}
|
||||
|
|
|
@ -35,6 +35,9 @@ pub enum Error {
|
|||
#[error("Invalid proxy URL: {0}")]
|
||||
InvalidProxy(#[from] hyper::http::uri::InvalidUri),
|
||||
|
||||
#[error("Failed to establish proxy: {0}")]
|
||||
ProxySetupError(String),
|
||||
|
||||
#[error("Error proxying request: {0}")]
|
||||
ProxyRequestError(hyper::Error),
|
||||
|
||||
|
|
|
@ -48,6 +48,16 @@ impl ProxyClient {
|
|||
pub fn add_proxy(mut router: Router, proxy: &WebProxyConfig) -> Result<Router> {
|
||||
let url: Uri = proxy.backend.parse()?;
|
||||
let path = url.path().to_string();
|
||||
let trimmed_path = path.trim_end_matches('/');
|
||||
|
||||
if trimmed_path.is_empty() {
|
||||
return Err(crate::Error::ProxySetupError(format!(
|
||||
"Proxy backend URL must have a non-empty path, e.g. {}/api instead of {}",
|
||||
proxy.backend.trim_end_matches('/'),
|
||||
proxy.backend
|
||||
)));
|
||||
}
|
||||
|
||||
let client = ProxyClient::new(url);
|
||||
|
||||
// We also match everything after the path using a wildcard matcher.
|
||||
|
@ -56,7 +66,7 @@ pub fn add_proxy(mut router: Router, proxy: &WebProxyConfig) -> Result<Router> {
|
|||
router = router.route(
|
||||
// Always remove trailing /'s so that the exact route
|
||||
// matches.
|
||||
path.trim_end_matches('/'),
|
||||
trimmed_path,
|
||||
any(move |req| async move {
|
||||
client
|
||||
.send(req)
|
||||
|
@ -68,7 +78,7 @@ pub fn add_proxy(mut router: Router, proxy: &WebProxyConfig) -> Result<Router> {
|
|||
// Wildcard match anything else _after_ the backend URL's path.
|
||||
// Note that we know `path` ends with a trailing `/` in this branch,
|
||||
// so `wildcard` will look like `http://localhost/api/*proxywildcard`.
|
||||
let wildcard = format!("{}/*proxywildcard", path.trim_end_matches('/'));
|
||||
let wildcard = format!("{}/*proxywildcard", trimmed_path);
|
||||
router = router.route(
|
||||
&wildcard,
|
||||
any(move |req| async move {
|
||||
|
@ -168,4 +178,21 @@ mod test {
|
|||
async fn add_proxy_trailing_slash() {
|
||||
test_proxy_requests("/api/".to_string()).await;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_proxy_empty_path() {
|
||||
let config = WebProxyConfig {
|
||||
backend: "http://localhost:8000".to_string(),
|
||||
};
|
||||
let router = super::add_proxy(Router::new(), &config);
|
||||
match router.unwrap_err() {
|
||||
crate::Error::ProxySetupError(e) => {
|
||||
assert_eq!(
|
||||
e,
|
||||
"Proxy backend URL must have a non-empty path, e.g. http://localhost:8000/api instead of http://localhost:8000"
|
||||
);
|
||||
}
|
||||
e => panic!("Unexpected error type: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -328,22 +328,79 @@ pub fn extract_zip(file: &Path, target: &Path) -> anyhow::Result<()> {
|
|||
}
|
||||
|
||||
for i in 0..zip.len() {
|
||||
let mut file = zip.by_index(i)?;
|
||||
if file.is_dir() {
|
||||
// dir
|
||||
let target = target.join(Path::new(&file.name().replace('\\', "")));
|
||||
std::fs::create_dir_all(target)?;
|
||||
let mut zip_entry = zip.by_index(i)?;
|
||||
|
||||
// check for dangerous paths
|
||||
// see https://docs.rs/zip/latest/zip/read/struct.ZipFile.html#warnings
|
||||
let Some(enclosed_name) = zip_entry.enclosed_name() else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Refusing to unpack zip entry with potentially dangerous path: zip={} entry={:?}",
|
||||
file.display(),
|
||||
zip_entry.name()
|
||||
));
|
||||
};
|
||||
|
||||
let output_path = target.join(enclosed_name);
|
||||
if zip_entry.is_dir() {
|
||||
std::fs::create_dir_all(output_path)?;
|
||||
} else {
|
||||
// file
|
||||
let file_path = target.join(Path::new(file.name()));
|
||||
let mut target_file = if !file_path.exists() {
|
||||
std::fs::File::create(file_path)?
|
||||
// create parent dirs if needed
|
||||
if let Some(parent) = output_path.parent() {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
}
|
||||
|
||||
// extract file
|
||||
let mut target_file = if !output_path.exists() {
|
||||
std::fs::File::create(output_path)?
|
||||
} else {
|
||||
std::fs::File::open(file_path)?
|
||||
std::fs::File::open(output_path)?
|
||||
};
|
||||
let _num = std::io::copy(&mut file, &mut target_file)?;
|
||||
let _num = std::io::copy(&mut zip_entry, &mut target_file)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::path::PathBuf;
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[test]
|
||||
fn test_extract_zip() -> anyhow::Result<()> {
|
||||
let path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap())
|
||||
.join("tests/fixtures/test.zip");
|
||||
let temp_dir = tempdir()?;
|
||||
let temp_path = temp_dir.path();
|
||||
|
||||
extract_zip(path.as_path(), temp_path)?;
|
||||
|
||||
let expected_files = vec!["file1.txt", "file2.txt", "dir/file3.txt"];
|
||||
for file in expected_files {
|
||||
let path = temp_path.join(file);
|
||||
assert!(path.exists(), "File not found: {:?}", path);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_zip_dangerous_path() -> anyhow::Result<()> {
|
||||
let path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap())
|
||||
.join("tests/fixtures/dangerous.zip");
|
||||
let temp_dir = tempdir()?;
|
||||
let temp_path = temp_dir.path();
|
||||
|
||||
let result = extract_zip(path.as_path(), temp_path);
|
||||
|
||||
let err = result.unwrap_err();
|
||||
assert!(err
|
||||
.to_string()
|
||||
.contains("Refusing to unpack zip entry with potentially dangerous path: zip="));
|
||||
assert!(err.to_string().contains("entry=\"/etc/passwd\""));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
BIN
packages/cli/tests/fixtures/dangerous.zip
vendored
Normal file
BIN
packages/cli/tests/fixtures/dangerous.zip
vendored
Normal file
Binary file not shown.
BIN
packages/cli/tests/fixtures/test.zip
vendored
Normal file
BIN
packages/cli/tests/fixtures/test.zip
vendored
Normal file
Binary file not shown.
|
@ -248,7 +248,7 @@ pub fn init<Ctx: HotReloadingContext + Send + 'static>(cfg: Config<Ctx>) {
|
|||
return shutdown;
|
||||
} else if log {
|
||||
println!(
|
||||
"Rebuild needed... shutting down hot reloading.\nManually rebuild the application to view futher changes."
|
||||
"Rebuild needed... shutting down hot reloading.\nManually rebuild the application to view further changes."
|
||||
);
|
||||
}
|
||||
true
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! Handles quering data from the renderer
|
||||
//! Handles querying data from the renderer
|
||||
|
||||
use euclid::Rect;
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ keywords = ["dom", "ui", "gui", "react", "wasm"]
|
|||
|
||||
[dependencies]
|
||||
dioxus-core = { workspace = true, features = ["serialize"] }
|
||||
dioxus-html = { workspace = true, features = ["wasm-bind"], default-features = false }
|
||||
dioxus-html = { workspace = true, features = ["wasm-bind"] }
|
||||
dioxus-interpreter-js = { workspace = true, features = [
|
||||
"sledgehammer",
|
||||
"minimal_bindings",
|
||||
|
|
Loading…
Reference in a new issue