use crate::base::{
println_tabled, read_input, read_input_or_default, to_auth_type, AuthType,
MmhConfig, ServiceConfig, ServiceConfigView, ServicePingView,
};
use crate::{get_config, get_config_path};
use std::io::Write;
use std::net::{TcpStream, ToSocketAddrs};
use std::os::unix::prelude::CommandExt;
use std::{fs, io};
pub fn list() {
let mut data_vec: Vec<ServiceConfigView> = vec![];
for (i, s) in get_config().servers.iter().enumerate() {
let t_s = s.clone();
data_vec.push(ServiceConfigView {
id: i,
name: t_s.name,
host: t_s.host,
port: t_s.port,
user: t_s.user,
auth_type: t_s.auth_type.as_str().to_string(),
})
}
println_tabled(data_vec);
}
pub fn add() {
let name = read_input("请输入服务器名称:");
let host = read_input("请输入服务器地址:");
let port = read_input("请输入服务器端口:").parse::<u16>().unwrap();
let user = read_input("请输入服务器用户名:");
let auth_type_str = read_input("请输入服务器认证方式(pwd,secret):")
.parse::<String>()
.unwrap();
let mut password = String::new();
let mut key_file = String::new();
let auth_type = to_auth_type(auth_type_str.as_str());
match auth_type {
AuthType::Pwd => {
password = read_input("请输入服务器密码:");
}
AuthType::Secret => {
key_file = read_input("请输入服务器密钥文件:");
}
}
let service_config = ServiceConfig {
name,
host,
port,
user,
auth_type,
password,
key_file,
};
let config_str = fs::read_to_string(get_config_path()).unwrap_or_default();
if config_str.is_empty() {
let mmh_config = MmhConfig {
servers: vec![service_config],
sync_cmd: None,
};
let toml_value = toml::to_string(&mmh_config).unwrap();
fs::File::create(get_config_path())
.expect("无法创建文件")
.write_all(toml_value.as_bytes())
.expect("无法写入文件");
} else {
let mut config = get_config();
config.servers.push(service_config);
let toml_value = toml::to_string(&config).unwrap();
fs::File::create(get_config_path())
.expect("无法创建文件")
.write_all(toml_value.as_bytes())
.expect("无法写入文件");
}
}
pub fn remove(name_option: Option<&String>) {
let config = get_config();
match name_option {
Some(name) => {
for (i, s) in config.servers.iter().enumerate() {
if s.name.eq(name) {
let mut new_config = config.clone();
new_config.servers.remove(i);
remove_save(new_config);
}
}
}
None => {
list();
let id = read_input("请输入要删除的服务器序号:")
.parse::<usize>()
.expect("请输入数字");
let mut new_config = config.clone();
new_config.servers.remove(id);
remove_save(new_config);
}
}
}
pub fn sftp(name_option: Option<&String>, remote_dir: Option<&String>) {
let config = get_config();
match name_option {
Some(name) => {
for s in config.servers {
if s.name.eq(name) {
sftp_connect(s.clone(), remote_dir)
}
}
}
None => {
list();
let id = read_input("请输入要链接的服务器序号:")
.parse::<usize>()
.expect("请输入数字");
let current_config = config.servers[id].clone();
sftp_connect(current_config.clone(), remote_dir)
}
}
}
pub fn ping() {
let config = get_config();
let mut data_vec: Vec<ServicePingView> = vec![];
for (i, s) in config.servers.iter().enumerate() {
let t_s = s.clone();
let mut flag = false;
match test_tcp_connection(s.host.as_str(), s.port) {
Ok(ping_result) => {
flag = ping_result;
}
_ => {}
}
data_vec.push(ServicePingView {
id: i,
name: t_s.name,
host: t_s.host,
port: t_s.port,
ping_result: flag,
});
}
println_tabled(data_vec)
}
pub fn connect(name_option: Option<&String>) {
let config = get_config();
match name_option {
Some(name) => {
for s in config.servers {
if s.name.eq(name) {
ssh_connect(s.clone())
}
}
}
None => {
list();
let id = read_input("请输入要登录的服务器序号:")
.parse::<usize>()
.expect("请输入数字");
let current_config = config.servers[id].clone();
ssh_connect(current_config.clone())
}
}
}
fn ssh_connect(s: ServiceConfig) {
println!("connect to {} mode!", s.name);
match s.auth_type {
AuthType::Pwd => {
let _ = std::process::Command::new("sshpass")
.arg("-p")
.arg(s.password)
.arg("ssh")
.arg("-o")
.arg("StrictHostKeyChecking=no")
.arg(format!("{}@{}", s.user, s.host))
.arg("-p")
.arg(s.port.to_string())
.exec();
}
AuthType::Secret => {
let _ = std::process::Command::new("ssh")
.arg("-i")
.arg(s.key_file)
.arg("-o")
.arg("StrictHostKeyChecking=no")
.arg(format!("{}@{}", s.user, s.host))
.arg("-p")
.arg(s.port.to_string())
.exec();
}
}
}
fn remove_save(config: MmhConfig) {
let mmh_config = MmhConfig {
servers: config.servers,
sync_cmd: config.sync_cmd,
};
let toml_value = toml::to_string(&mmh_config).unwrap();
fs::File::create(get_config_path())
.expect("无法创建文件")
.write_all(toml_value.as_bytes())
.expect("无法写入文件");
}
fn sftp_connect(s: ServiceConfig, remote_dir: Option<&String>) {
println!("connect to {} !", s.name);
match s.auth_type {
AuthType::Pwd => {
let _ = std::process::Command::new("sshpass")
.arg("-p")
.arg(s.password)
.arg("sftp")
.arg("-o")
.arg("StrictHostKeyChecking=no")
.arg("-P")
.arg(s.port.to_string())
.arg(format!(
"{}@{}:{}",
s.user,
s.host,
remote_dir.unwrap_or(&".".to_string())
))
.exec();
}
AuthType::Secret => {
let _ = std::process::Command::new("sftp")
.arg("-i")
.arg(s.key_file)
.arg("-o")
.arg("StrictHostKeyChecking=no")
.arg("-p")
.arg(s.port.to_string())
.arg(format!(
"{}@{}:{}",
s.user,
s.host,
remote_dir.unwrap_or(&".".to_string())
))
.exec();
}
}
}
fn test_tcp_connection(ip: &str, port: u16) -> Result<bool, io::Error> {
let address = format!("{}:{}", ip, port)
.to_socket_addrs()?
.next()
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Failed to parse address"))?;
match TcpStream::connect_timeout(&address, std::time::Duration::from_secs(5)) {
Ok(_) => Ok(true), Err(ref e) if e.kind() == io::ErrorKind::ConnectionRefused => Ok(false), Err(e) => Err(e), }
}
pub fn copy_server_config(name_option: Option<&String>) {
let config = get_config();
match name_option {
Some(name) => {
for s in config.servers {
if s.name.eq(name) {
copy_server(s.clone())
}
}
}
None => {
list();
let id = read_input("请输入要复制的服务器序号:")
.parse::<usize>()
.expect("请输入数字");
let current_config = config.servers[id].clone();
copy_server(current_config.clone())
}
}
}
fn copy_server(service_config: ServiceConfig) {
let name = read_input(&format!("请输入服务器新名称({}):", service_config.name));
let host = read_input(&format!("请输入服务器地址:({})", service_config.host));
let port = read_input(&format!("请输入服务器端口({}):", service_config.port))
.parse::<u16>()
.unwrap_or(service_config.port);
let user = read_input(&format!("请输入服务器用户名({}):", service_config.user));
let auth_type_str = read_input_or_default(
&format!(
"请输入服务器认证方式(pwd,secret)({}):",
service_config.auth_type.as_str()
),
service_config.auth_type.as_str(),
);
let mut password = String::new();
let mut key_file = String::new();
let auth_type = to_auth_type(auth_type_str.as_str());
match auth_type {
AuthType::Pwd => {
password = read_input(&format!("请输入服务器密码({}):", service_config.password));
}
AuthType::Secret => {
key_file = read_input(&format!(
"请输入服务器密钥文件({}):",
service_config.key_file
));
}
}
let new_name = if name.is_empty() {
service_config.name
} else {
name
};
let new_host = if host.is_empty() {
service_config.host
} else {
host
};
let new_port = if port == 0 { service_config.port } else { port };
let new_user = if user.is_empty() {
service_config.user
} else {
user
};
let new_auth_type = if auth_type_str.is_empty() {
service_config.auth_type
} else {
to_auth_type(auth_type_str.as_str())
};
let new_password = if password.is_empty() {
service_config.password
} else {
password
};
let new_key_file = if key_file.is_empty() {
service_config.key_file
} else {
key_file
};
let new_service_config = ServiceConfig {
name: new_name,
host: new_host,
port: new_port,
user: new_user,
auth_type: new_auth_type,
password: new_password,
key_file: new_key_file,
};
let mut config = get_config();
config.servers.push(new_service_config);
let toml_value = toml::to_string(&config).unwrap();
fs::File::create(get_config_path())
.expect("无法创建文件")
.write_all(toml_value.as_bytes())
.expect("无法写入文件");
}