Skip to content

Commit 587031d

Browse files
committed
feat: add native SFTP directory upload/download operations and remove unused code
1 parent 146bc6c commit 587031d

12 files changed

Lines changed: 395 additions & 2062 deletions

File tree

src/executor.rs

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -433,18 +433,58 @@ async fn upload_to_node(
433433

434434
let key_path = key_path.map(Path::new);
435435

436+
// Check if the local path is a directory
437+
if local_path.is_dir() {
438+
client
439+
.upload_dir(
440+
local_path,
441+
remote_path,
442+
key_path,
443+
Some(strict_mode),
444+
use_agent,
445+
)
446+
.await
447+
} else {
448+
client
449+
.upload_file(
450+
local_path,
451+
remote_path,
452+
key_path,
453+
Some(strict_mode),
454+
use_agent,
455+
)
456+
.await
457+
}
458+
}
459+
460+
async fn download_from_node(
461+
node: Node,
462+
remote_path: &str,
463+
local_path: &Path,
464+
key_path: Option<&str>,
465+
strict_mode: StrictHostKeyChecking,
466+
use_agent: bool,
467+
) -> Result<std::path::PathBuf> {
468+
let mut client = SshClient::new(node.host.clone(), node.port, node.username.clone());
469+
470+
let key_path = key_path.map(Path::new);
471+
472+
// This function handles both files and directories
473+
// The caller should check if it's a directory and use the appropriate method
436474
client
437-
.upload_file(
438-
local_path,
475+
.download_file(
439476
remote_path,
477+
local_path,
440478
key_path,
441479
Some(strict_mode),
442480
use_agent,
443481
)
444-
.await
482+
.await?;
483+
484+
Ok(local_path.to_path_buf())
445485
}
446486

447-
async fn download_from_node(
487+
pub async fn download_dir_from_node(
448488
node: Node,
449489
remote_path: &str,
450490
local_path: &Path,
@@ -457,7 +497,7 @@ async fn download_from_node(
457497
let key_path = key_path.map(Path::new);
458498

459499
client
460-
.download_file(
500+
.download_dir(
461501
remote_path,
462502
local_path,
463503
key_path,

src/main.rs

Lines changed: 24 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -699,77 +699,40 @@ async fn download_file(
699699
};
700700

701701
if is_directory {
702-
// Recursive directory download
702+
// Recursive directory download using SFTP
703703
println!(
704-
"Recursively downloading directory {source} from {} nodes",
704+
"Recursively downloading directory {source} from {} nodes using SFTP",
705705
params.nodes.len()
706706
);
707707

708-
// Find all files in the directory recursively
709-
let find_cmd = format!("find '{source}' -type f 2>/dev/null || find '{source}' -type f");
710-
let find_results = executor.execute(&find_cmd).await?;
711-
712708
let mut total_success = 0;
713709
let mut total_failed = 0;
714710

715-
for (node_idx, result) in find_results.iter().enumerate() {
716-
if let Ok(cmd_result) = &result.result {
717-
let stdout = String::from_utf8_lossy(&cmd_result.output);
718-
let files: Vec<String> = stdout
719-
.lines()
720-
.filter(|line| !line.is_empty())
721-
.map(|s| s.to_string())
722-
.collect();
723-
724-
if files.is_empty() {
725-
println!("No files found in directory on {}", params.nodes[node_idx]);
726-
continue;
727-
}
728-
729-
println!(
730-
"\nDownloading {} files from {}",
731-
files.len(),
732-
params.nodes[node_idx]
733-
);
734-
735-
for remote_file in files {
736-
// Calculate relative path from source directory
737-
let relative_path = remote_file
738-
.strip_prefix(source)
739-
.unwrap_or(&remote_file)
740-
.trim_start_matches('/');
741-
742-
// Create local file path preserving directory structure
743-
let local_file = destination
744-
.join(params.nodes[node_idx].to_string())
745-
.join(relative_path);
746-
747-
// Create parent directory if needed
748-
if let Some(parent) = local_file.parent() {
749-
fs::create_dir_all(parent).await?;
750-
}
711+
// Download the entire directory from each node
712+
for node in &params.nodes {
713+
let node_dir = destination.join(node.to_string());
751714

752-
// Download the file using the executor's download method
753-
let single_node = vec![params.nodes[node_idx].clone()];
754-
let single_executor = ParallelExecutor::new_with_strict_mode_and_agent(
755-
single_node,
756-
1,
757-
key_path_str.clone(),
758-
params.strict_mode,
759-
params.use_agent,
760-
);
715+
println!("\nDownloading directory from {node} to {node_dir:?}");
761716

762-
let download_results = single_executor
763-
.download_file(&remote_file, &local_file)
764-
.await?;
717+
// Use the download_dir_from_node function directly
718+
let result = bssh::executor::download_dir_from_node(
719+
node.clone(),
720+
source,
721+
&node_dir,
722+
key_path_str.as_deref(),
723+
params.strict_mode,
724+
params.use_agent,
725+
)
726+
.await;
765727

766-
if download_results.iter().any(|r| r.is_success()) {
767-
println!(" ✓ Downloaded: {remote_file} -> {local_file:?}");
768-
total_success += 1;
769-
} else {
770-
println!(" ✗ Failed: {remote_file}");
771-
total_failed += 1;
772-
}
728+
match result {
729+
Ok(_) => {
730+
println!(" ✓ Successfully downloaded directory");
731+
total_success += 1;
732+
}
733+
Err(e) => {
734+
println!(" ✗ Failed to download directory: {e}");
735+
total_failed += 1;
773736
}
774737
}
775738
}

src/sftp/auth.rs

Lines changed: 0 additions & 195 deletions
This file was deleted.

0 commit comments

Comments
 (0)