mirror of
https://github.com/kunkunsh/kunkun.git
synced 2025-04-20 05:29:17 +00:00

When sending to non-localhost host, will fail. grpc server somehow cannot get client ip. Modified grpc proto to send a src ip.
278 lines
8.7 KiB
Rust
278 lines
8.7 KiB
Rust
use std::collections::HashMap;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use grpc::file_transfer::file_transfer_server::FileTransfer;
|
|
use grpc::file_transfer::{FileNode, FileType, StartTransferRequest, StartTransferResponse};
|
|
use serde::{Deserialize, Serialize};
|
|
use tauri::{AppHandle, Emitter};
|
|
use tonic::{Request, Response, Status};
|
|
use uuid::Uuid;
|
|
|
|
#[derive(Debug)]
|
|
pub struct MyFileTransfer {
|
|
pub app_handle: AppHandle,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct FileTransferPayload {
|
|
pub port: String,
|
|
pub code: String,
|
|
pub total_bytes: u128,
|
|
pub total_files: usize,
|
|
pub root: FileNode,
|
|
pub ip: String,
|
|
pub ssl_cert: String,
|
|
}
|
|
|
|
#[tonic::async_trait]
|
|
impl FileTransfer for MyFileTransfer {
|
|
async fn start_transfer(
|
|
&self,
|
|
request: Request<StartTransferRequest>, // Accept request of type StartTransferRequest
|
|
) -> Result<Response<StartTransferResponse>, Status> {
|
|
let reply = StartTransferResponse {};
|
|
let payload = request.into_inner();
|
|
println!("start_transfer payload: {:?}", payload);
|
|
let src_ip = payload.ip;
|
|
println!("src_ip: {:?}", src_ip);
|
|
let root = if let Some(root) = payload.root {
|
|
root
|
|
} else {
|
|
return Err(Status::invalid_argument("root is required"));
|
|
};
|
|
|
|
let total_bytes = compute_total_size(&root);
|
|
let total_files = count_file_nodes(&root);
|
|
|
|
self.app_handle
|
|
.emit(
|
|
"file-transfer-request",
|
|
FileTransferPayload {
|
|
port: payload.port,
|
|
code: payload.code,
|
|
root,
|
|
total_bytes,
|
|
total_files,
|
|
ip: src_ip,
|
|
ssl_cert: payload.ssl_cert,
|
|
},
|
|
)
|
|
.map_err(|e| Status::internal(e.to_string()))?;
|
|
|
|
Ok(Response::new(reply))
|
|
}
|
|
}
|
|
|
|
pub fn construct_file_node(path: &Path) -> anyhow::Result<FileNode> {
|
|
if !path.exists() {
|
|
return Err(anyhow::anyhow!("path not exists"));
|
|
}
|
|
if !path.is_file() {
|
|
return Err(anyhow::anyhow!("path is not a file"));
|
|
}
|
|
Ok(FileNode {
|
|
filename: path
|
|
.file_name()
|
|
.expect("Fail to get file name")
|
|
.to_string_lossy()
|
|
.to_string(),
|
|
file_size: path.metadata()?.len(),
|
|
id: Uuid::new_v4().to_string(),
|
|
r#type: FileType::File as i32,
|
|
children: vec![],
|
|
})
|
|
}
|
|
|
|
pub fn construct_directory_node(path: &Path) -> anyhow::Result<FileNode> {
|
|
if !path.exists() {
|
|
return Err(anyhow::anyhow!("path not exists"));
|
|
}
|
|
if !path.is_dir() {
|
|
return Err(anyhow::anyhow!("path is not a directory"));
|
|
}
|
|
// construct children
|
|
let children = path
|
|
.read_dir()?
|
|
.filter_map(|entry| construct_node(&entry.ok()?.path()).ok())
|
|
.collect();
|
|
Ok(FileNode {
|
|
filename: path
|
|
.file_name()
|
|
.expect("Fail to get file name")
|
|
.to_string_lossy()
|
|
.to_string(),
|
|
file_size: path.metadata()?.len(),
|
|
id: Uuid::new_v4().to_string(),
|
|
r#type: FileType::Directory as i32,
|
|
children,
|
|
})
|
|
}
|
|
|
|
pub fn construct_node(path: &Path) -> anyhow::Result<FileNode> {
|
|
if path.is_file() {
|
|
construct_file_node(path)
|
|
} else {
|
|
construct_directory_node(path)
|
|
}
|
|
}
|
|
|
|
pub fn compute_file_node_total_size(node: &FileNode) -> u64 {
|
|
if node.r#type == FileType::File as i32 {
|
|
node.file_size
|
|
} else {
|
|
node.children
|
|
.iter()
|
|
.map(|child| compute_file_node_total_size(child))
|
|
.sum()
|
|
}
|
|
}
|
|
|
|
/// Flatten the file node tree to a vector of (id, path), path should be absolute path
|
|
/// `root_path` should be the directory containing
|
|
pub fn get_id_path_array(node: &FileNode, root_path: &Path) -> Vec<(String, PathBuf)> {
|
|
let mut vec: Vec<(String, PathBuf)> = Vec::new();
|
|
let dir_path = root_path.join(&node.filename);
|
|
if node.r#type == FileType::File as i32 {
|
|
vec.push((node.id.clone(), dir_path));
|
|
} else {
|
|
for child in node.children.iter() {
|
|
vec.extend(get_id_path_array(child, &dir_path));
|
|
}
|
|
}
|
|
vec
|
|
}
|
|
|
|
/// the returned root node will have empty filename because `files` passed in are not necessarily in the same directory
|
|
/// The root dir filename is left empty to avoid confusion, once the receiver receives the root node, it can fill in a custom directory name
|
|
pub fn build_file_node_and_id_path_map(
|
|
files: &Vec<PathBuf>,
|
|
) -> anyhow::Result<(HashMap<String, PathBuf>, FileNode)> {
|
|
let mut id_path_array: Vec<(String, PathBuf)> = Vec::new();
|
|
|
|
let mut children: Vec<FileNode> = vec![];
|
|
for file in files.iter() {
|
|
let node = construct_node(file).unwrap();
|
|
id_path_array.extend(get_id_path_array(&node, file.parent().unwrap()));
|
|
children.push(node);
|
|
}
|
|
let root = FileNode {
|
|
filename: "".to_string(),
|
|
file_size: 0,
|
|
id: Uuid::new_v4().to_string(),
|
|
r#type: FileType::Directory as i32,
|
|
children,
|
|
};
|
|
let map: HashMap<String, PathBuf> = id_path_array.into_iter().collect();
|
|
Ok((map, root))
|
|
}
|
|
|
|
pub fn count_file_nodes(node: &FileNode) -> usize {
|
|
if node.r#type == FileType::File as i32 {
|
|
1
|
|
} else {
|
|
node.children
|
|
.iter()
|
|
.map(|child| count_file_nodes(child))
|
|
.sum()
|
|
}
|
|
}
|
|
|
|
pub fn compute_total_size(node: &FileNode) -> u128 {
|
|
if node.r#type == FileType::File as i32 {
|
|
node.file_size as u128
|
|
} else {
|
|
node.children
|
|
.iter()
|
|
.map(|child| compute_total_size(child))
|
|
.sum()
|
|
}
|
|
}
|
|
|
|
mod test {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_construct_node() {
|
|
let manifest_path = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
|
let src_path = PathBuf::from(manifest_path).join("src");
|
|
// manifest_path is pointing to grpc crate
|
|
let node = construct_node(src_path.as_path()).unwrap();
|
|
println!("{:#?}", node);
|
|
// println!("total size: {}", compute_total_size(&node));
|
|
}
|
|
|
|
#[test]
|
|
fn test_build_file_node_and_id_path_map() {
|
|
let manifest_path = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
|
let src_path = PathBuf::from(manifest_path).join("src");
|
|
let (map, node) = build_file_node_and_id_path_map(&vec![src_path]).unwrap();
|
|
println!("{:#?}", node);
|
|
// check if all paths are absolute and exists
|
|
println!("{:#?}", map);
|
|
for (_, path) in map.iter() {
|
|
if !path.exists() {
|
|
panic!("path not exists: {}", path.to_string_lossy());
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_get_id_path_array() {
|
|
let manifest_path = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
|
let src_path = PathBuf::from(manifest_path)
|
|
.join("src")
|
|
.canonicalize()
|
|
.unwrap();
|
|
assert!(src_path.exists());
|
|
// let src_path = PathBuf::from("/Users/hk/Dev/kunkun/packages/grpc/src");
|
|
let node = construct_node(&src_path).unwrap();
|
|
println!("{:#?}", node);
|
|
let array = get_id_path_array(&node, src_path.parent().unwrap());
|
|
println!("{:#?}", array);
|
|
// check if all paths are absolute and exists
|
|
for (_, path) in array.iter() {
|
|
if !path.exists() {
|
|
panic!("path not exists: {}", path.to_string_lossy());
|
|
}
|
|
// assert!(path.is_absolute());
|
|
// assert!(path.exists());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_count_file_nodes() {
|
|
let manifest_path = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
|
let src_path = PathBuf::from(manifest_path).join("src");
|
|
let node = construct_node(&src_path).unwrap();
|
|
// println!("{:#?}", node);
|
|
let count = count_file_nodes(&node);
|
|
// run "find . -type f | wc -l" to get a ground truth
|
|
if cfg!(target_os = "windows") {
|
|
return;
|
|
}
|
|
let stdout = String::from_utf8(
|
|
std::process::Command::new("find")
|
|
.arg(&src_path)
|
|
.arg("-type")
|
|
.arg("f")
|
|
.output()
|
|
.unwrap()
|
|
.stdout,
|
|
)
|
|
.unwrap();
|
|
let count2 = stdout.lines().count();
|
|
assert_eq!(count, count2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_compute_total_size() {
|
|
let manifest_path = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
|
let src_path = PathBuf::from(manifest_path).join("src");
|
|
println!("src_path: {}", src_path.to_string_lossy());
|
|
let node = construct_node(&src_path).unwrap();
|
|
let size = compute_total_size(&node);
|
|
println!("size: {}", size);
|
|
}
|
|
}
|