1use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13use std::path::PathBuf;
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct RemoteCred {
17 pub host: String,
18 pub user: String,
19 #[serde(default)]
20 pub password: Option<String>,
21 #[serde(default = "default_port")]
22 pub port: u64,
23 #[serde(default)]
25 pub key: Option<String>,
26}
27
28fn default_port() -> u64 {
29 22
30}
31
32fn store_path() -> PathBuf {
33 aegis_types::paths::config_dir().join("remotes.json")
34}
35
36pub fn load_all() -> HashMap<String, RemoteCred> {
38 std::fs::read_to_string(store_path())
39 .ok()
40 .and_then(|c| serde_json::from_str(&c).ok())
41 .unwrap_or_default()
42}
43
44pub fn get(name: &str) -> Option<RemoteCred> {
46 load_all().get(name).cloned()
47}
48
49pub fn list_names() -> Vec<String> {
51 let mut v: Vec<String> = load_all().into_keys().collect();
52 v.sort();
53 v
54}
55
56pub fn save(name: &str, cred: RemoteCred) -> anyhow::Result<()> {
58 let mut all = load_all();
59 all.insert(name.to_string(), cred);
60 let path = store_path();
61 if let Some(p) = path.parent() {
62 std::fs::create_dir_all(p)?;
63 }
64 std::fs::write(&path, serde_json::to_string_pretty(&all)?)?;
65 #[cfg(unix)]
66 {
67 use std::os::unix::fs::PermissionsExt;
68 let _ = std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o600));
69 }
70 Ok(())
71}
72
73pub fn remove(name: &str) -> anyhow::Result<bool> {
75 let mut all = load_all();
76 let existed = all.remove(name).is_some();
77 if existed {
78 std::fs::write(store_path(), serde_json::to_string_pretty(&all)?)?;
79 }
80 Ok(existed)
81}
82
83pub fn all_passwords() -> Vec<String> {
85 load_all()
86 .into_values()
87 .filter_map(|c| c.password)
88 .filter(|p| p.len() >= 4)
89 .collect()
90}