use crate::commands::utils::{export_path, show_profile};
use crate::{ProfileCommand, ProfileOpts};
use eyre::eyre;
pub fn run(opts: ProfileOpts) {
match opts.command {
ProfileCommand::List => {
let serve = volli_server::list_profiles().unwrap_or_default();
let agent = volli_agent::list_profiles().unwrap_or_default();
println!("serve profiles:");
for p in serve {
println!(" {p}");
}
println!("agent profiles:");
for p in agent {
println!(" {p}");
}
}
ProfileCommand::Show(show) => {
show_profile(&show.profile, show.coord, show.agent);
}
ProfileCommand::Delete(del) => {
let mut confirmed = false;
if !del.agent && !del.serve {
println!(
"Delete both agent and serve profile '{}' ? [y/N]",
del.profile
);
let mut c = String::new();
std::io::stdin().read_line(&mut c).ok();
confirmed = c.trim() == "y";
}
if del.agent || confirmed && !del.serve {
volli_agent::delete_profile(&del.profile).ok();
}
if del.agent || !del.serve && confirmed {
volli_server::delete_profile(&del.profile).ok();
}
}
ProfileCommand::Rename(rn) => {
let agent_exists = volli_agent::profile_exists(&rn.old);
let coord_exists = volli_server::profile_exists(&rn.old);
let which = if rn.coord {
Some("coord")
} else if rn.agent {
Some("agent")
} else {
None
};
let res = match which {
Some("coord") => volli_server::rename_profile(&rn.old, &rn.new),
Some("agent") => volli_agent::rename_profile(&rn.old, &rn.new),
None => {
if agent_exists && coord_exists {
Err(eyre!("profile name conflict; specify --coord or --agent"))
} else if agent_exists {
volli_agent::rename_profile(&rn.old, &rn.new)
} else if coord_exists {
volli_server::rename_profile(&rn.old, &rn.new)
} else {
Err(eyre!("profile not found"))
}
}
_ => unreachable!(),
};
if let Err(e) = res {
eprintln!("rename error: {e}");
}
}
ProfileCommand::Update(upd) => {
if let Some(token) = upd.add_join_token {
volli_server::add_join_host_from_token(&upd.profile, &token).ok();
}
if let Some(host) = upd.add_join_host {
let entry = volli_server::JoinHostEntry {
coord_id: None,
host,
tcp_port: upd.join_tcp_port,
quic_port: upd.join_quic_port,
token: None,
cert: None,
fingerprint: None,
last_ok: None,
last_fail: None,
};
volli_server::add_join_host(&upd.profile, entry).ok();
}
if let Some(idx) = upd.remove_join_index {
volli_server::remove_join_host_index(&upd.profile, idx).ok();
}
if let Some(host) = upd.bind_host {
volli_server::save_bind_host(&upd.profile, &host).ok();
}
if let Some(p) = upd.tcp_port {
volli_server::save_tcp_port(&upd.profile, p).ok();
}
if let Some(p) = upd.quic_port {
volli_server::save_quic_port(&upd.profile, p).ok();
}
if let Some(host) = upd.advertise_host {
volli_server::save_profile_host(&upd.profile, &host).ok();
}
if let Some(list) = upd.agent_whitelist {
let vals: Vec<String> = list
.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect();
volli_server::save_agent_whitelist(&upd.profile, &vals).ok();
}
if let Some(list) = upd.coord_whitelist {
let vals: Vec<String> = list
.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect();
volli_server::save_coord_whitelist(&upd.profile, &vals).ok();
}
if let Some(secret) = upd.agent_secret {
volli_agent::save_state(&upd.profile, &secret).ok();
}
}
ProfileCommand::Export(exp) => {
let agent_exists = volli_agent::profile_exists(&exp.profile);
let coord_exists = volli_server::profile_exists(&exp.profile);
let which = if exp.coord {
Some("coord")
} else if exp.agent {
Some("agent")
} else {
None
};
let yaml = match which {
Some("coord") => volli_server::export_profile(&exp.profile).ok(),
Some("agent") => volli_agent::export_profile(&exp.profile).ok(),
None => {
if agent_exists && coord_exists {
eprintln!("profile name conflict; specify --coord or --agent");
None
} else if agent_exists {
volli_agent::export_profile(&exp.profile).ok()
} else if coord_exists {
volli_server::export_profile(&exp.profile).ok()
} else {
eprintln!("profile not found");
None
}
}
_ => None,
};
if let Some(data) = yaml {
if exp.stdout {
print!("{data}");
} else {
let file = export_path(&exp.profile, exp.output.as_deref());
std::fs::write(&file, data).ok();
println!("written {file}");
}
}
}
ProfileCommand::Import(imp) => {
let data = std::fs::read_to_string(&imp.file).expect("read file");
let kind: Result<volli_server::CoordProfileExport, _> = serde_yaml::from_str(&data);
if kind.is_ok() {
match volli_server::import_profile(&data, imp.name.as_deref(), imp.force) {
Ok(name) => println!("imported coordinator profile '{name}'"),
Err(e) => {
if e.to_string().contains("profile exists") && !imp.force {
println!("Profile exists. Overwrite? [y/N]");
let mut c = String::new();
std::io::stdin().read_line(&mut c).ok();
if c.trim().eq_ignore_ascii_case("y") {
match volli_server::import_profile(&data, imp.name.as_deref(), true)
{
Ok(name) => println!("imported coordinator profile '{name}'"),
Err(e) => eprintln!("import error: {e}"),
}
} else {
println!("aborted");
}
} else {
eprintln!("import error: {e}");
}
}
}
} else {
match volli_agent::import_profile(&data, imp.name.as_deref(), imp.force) {
Ok(name) => println!("imported agent profile '{name}'"),
Err(e) => {
if e.to_string().contains("profile exists") && !imp.force {
println!("Profile exists. Overwrite? [y/N]");
let mut c = String::new();
std::io::stdin().read_line(&mut c).ok();
if c.trim().eq_ignore_ascii_case("y") {
match volli_agent::import_profile(&data, imp.name.as_deref(), true)
{
Ok(name) => println!("imported agent profile '{name}'"),
Err(e) => eprintln!("import error: {e}"),
}
} else {
println!("aborted");
}
} else {
eprintln!("import error: {e}");
}
}
}
}
}
ProfileCommand::Edit(edit) => {
let agent_exists = volli_agent::profile_exists(&edit.profile);
let coord_exists = volli_server::profile_exists(&edit.profile);
let which = if edit.coord {
Some("coord")
} else if edit.agent {
Some("agent")
} else {
None
};
let yaml_opt = match which {
Some("coord") => volli_server::export_profile(&edit.profile).ok(),
Some("agent") => volli_agent::export_profile(&edit.profile).ok(),
None => {
if agent_exists && coord_exists {
eprintln!("profile name conflict; specify --coord or --agent");
None
} else if agent_exists {
volli_agent::export_profile(&edit.profile).ok()
} else if coord_exists {
volli_server::export_profile(&edit.profile).ok()
} else {
eprintln!("profile not found");
None
}
}
_ => None,
};
if let Some(mut data) = yaml_opt {
let editor = std::env::var("EDITOR").unwrap_or_else(|_| "vi".into());
let tmp = tempfile::NamedTempFile::new().expect("tmpfile");
std::fs::write(tmp.path(), &data).ok();
if editor.split_whitespace().count() > 1 {
let cmd = format!("{} {}", editor, tmp.path().display());
std::process::Command::new("sh")
.arg("-c")
.arg(cmd)
.status()
.ok();
} else {
std::process::Command::new(&editor)
.arg(tmp.path())
.status()
.ok();
}
data = std::fs::read_to_string(tmp.path()).unwrap_or(data);
let valid = match which {
Some("coord") => {
serde_yaml::from_str::<volli_server::CoordProfileExport>(&data).is_ok()
}
Some("agent") => {
serde_yaml::from_str::<volli_agent::AgentProfileExport>(&data).is_ok()
}
None => {
if coord_exists {
serde_yaml::from_str::<volli_server::CoordProfileExport>(&data).is_ok()
} else {
serde_yaml::from_str::<volli_agent::AgentProfileExport>(&data).is_ok()
}
}
_ => false,
};
if valid {
if which == Some("coord") || (which.is_none() && coord_exists && !agent_exists)
{
if volli_server::import_profile(&data, Some(&edit.profile), true).is_ok() {
println!("updated coordinator profile '{}'", edit.profile);
}
} else if volli_agent::import_profile(&data, Some(&edit.profile), true).is_ok()
{
println!("updated agent profile '{}'", edit.profile);
}
} else {
eprintln!("syntax error - not saving");
}
}
}
}
}