sheepd/src/sheepctl_core/cmd.rs
2025-05-02 12:53:28 +02:00

105 lines
2.7 KiB
Rust

use std::path::PathBuf;
use owl::{Deserialize, Serialize};
use sheepd::{DeviceList, LoginParam};
use super::args::{ListDevicesCommand, LoginCommand};
use crate::api::domain;
pub fn api_call<T: Serialize + for<'a> Deserialize<'a>, I: Serialize>(
server: &str,
path: &str,
data: I,
) -> crate::api::Result<T> {
let url = format!("{}/{path}", domain(server));
let mut res = ureq::post(url).send_json(data).unwrap();
let res: crate::api::Result<T> = res.body_mut().read_json().unwrap();
res
}
pub fn api_call_get<T: Serialize + for<'a> Deserialize<'a>>(
server: &str,
path: &str,
token: &str,
) -> crate::api::Result<T> {
let url = format!("{}/{path}", domain(server));
let mut res = ureq::get(url)
.header("Authorization", format!("Bearer {token}"))
.force_send_body()
.send_empty()
.unwrap();
let res: crate::api::Result<T> = res.body_mut().read_json().unwrap();
res
}
fn get_config_path() -> Option<PathBuf> {
directories::ProjectDirs::from("de", "Hydrar", "sheepd")
.map(|proj_dirs| proj_dirs.config_dir().join("config.toml"))
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CtlConfig {
pub home: String,
pub token: String,
}
impl CtlConfig {
pub fn load() -> Option<Self> {
let c = std::fs::read_to_string(get_config_path()?).ok()?;
toml::from_str(&c).ok()
}
pub fn save(&self) {
let s = toml::to_string(self).unwrap();
let config = get_config_path().unwrap();
let _ = std::fs::create_dir_all(config.parent().unwrap());
std::fs::write(get_config_path().unwrap(), s).unwrap();
}
}
pub fn list_devices(arg: ListDevicesCommand) {
let conf = CtlConfig::load().unwrap();
if let Ok(devices) = api_call_get::<DeviceList>(&conf.home, "devices", &conf.token).as_result()
{
println!("Hosts:");
for d in devices.devices {
println!(
"- {} [{}]{}",
d.hostname,
d.id,
if d.online { " [ONLINE]" } else { "" }
);
}
}
}
pub fn login(arg: LoginCommand) {
if let Some(conf) = CtlConfig::load() {
println!("You are already logged in to {}", conf.home);
std::process::exit(1);
}
let password = inquire::prompt_secret("Password: ").unwrap();
// login request
if let Result::Ok(token) = api_call::<String, _>(
&arg.home,
"login",
LoginParam {
username: arg.username,
password: password,
},
)
.as_result()
{
// save token to config
CtlConfig {
home: arg.home,
token,
}
.save();
} else {
println!("Login failed");
}
}