tfcli done (woo)
This commit is contained in:
parent
4dd84e61e0
commit
c18bcc50c4
|
@ -11,7 +11,8 @@ pub async fn account_main(command: AccountCommands, server: Url) -> Result<(), B
|
||||||
AccountCommands::MagicLink { magic_link_token } => auth_magic_link(magic_link_token, server).await,
|
AccountCommands::MagicLink { magic_link_token } => auth_magic_link(magic_link_token, server).await,
|
||||||
AccountCommands::MfaSetup {} => create_mfa_authenticator(server).await,
|
AccountCommands::MfaSetup {} => create_mfa_authenticator(server).await,
|
||||||
AccountCommands::MfaSetupFinish {code, token} => finish_mfa_authenticator(token, code, server).await,
|
AccountCommands::MfaSetupFinish {code, token} => finish_mfa_authenticator(token, code, server).await,
|
||||||
AccountCommands::Mfa {code} => mfa_auth(code, server).await
|
AccountCommands::Mfa {code} => mfa_auth(code, server).await,
|
||||||
|
AccountCommands::Login { email } => login_account(email, server).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +40,31 @@ pub async fn create_account(email: String, server: Url) -> Result<(), Box<dyn Er
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct LoginAccountBody {
|
||||||
|
pub email: String
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn login_account(email: String, server: Url) -> Result<(), Box<dyn Error>> {
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
let res = client.post(server.join("/v1/auth/magic-link")?).json(&LoginAccountBody { email }).send().await?;
|
||||||
|
|
||||||
|
if res.status().is_success() {
|
||||||
|
println!("Magic link sent, check your email.");
|
||||||
|
println!("Finish creating your account with 'tfcli account magic-link --magic-link-token [magic-link-token]'.");
|
||||||
|
} else {
|
||||||
|
|
||||||
|
let resp: APIErrorResponse = res.json().await?;
|
||||||
|
|
||||||
|
eprintln!("[error] Error logging in: {} {}", resp.errors[0].code, resp.errors[0].message);
|
||||||
|
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct MagicLinkBody {
|
pub struct MagicLinkBody {
|
||||||
#[serde(rename = "magicLinkToken")]
|
#[serde(rename = "magicLinkToken")]
|
||||||
|
|
|
@ -0,0 +1,416 @@
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fs;
|
||||||
|
use std::net::{Ipv4Addr, SocketAddrV4};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use url::{Url};
|
||||||
|
use crate::api::APIErrorResponse;
|
||||||
|
use crate::{HostCommands};
|
||||||
|
|
||||||
|
pub async fn host_main(command: HostCommands, server: Url) -> Result<(), Box<dyn Error>> {
|
||||||
|
match command {
|
||||||
|
HostCommands::List {} => list_hosts(server).await,
|
||||||
|
HostCommands::Create { name, network_id, role_id, ip_address, listen_port, lighthouse, relay, static_address } => create_host(name, network_id, role_id, ip_address, listen_port, lighthouse, relay, static_address, server).await,
|
||||||
|
HostCommands::Lookup { id } => get_host(id, server).await,
|
||||||
|
HostCommands::Delete { id } => delete_host(id, server).await,
|
||||||
|
HostCommands::Update { id, listen_port, static_address, name, ip, role } => update_host(id, listen_port, static_address, name, ip, role, server).await,
|
||||||
|
HostCommands::Block { id } => block_host(id, server).await,
|
||||||
|
HostCommands::Enroll { id } => enroll_host(id, server).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct HostListResp {
|
||||||
|
pub data: Vec<Host>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct HostMetadata {
|
||||||
|
#[serde(rename = "lastSeenAt")]
|
||||||
|
pub last_seen_at: String,
|
||||||
|
pub version: String,
|
||||||
|
pub platform: String,
|
||||||
|
#[serde(rename = "updateAvailable")]
|
||||||
|
pub update_available: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct Host {
|
||||||
|
pub id: String,
|
||||||
|
#[serde(rename = "organizationID")]
|
||||||
|
pub organization_id: String,
|
||||||
|
#[serde(rename = "networkID")]
|
||||||
|
pub network_id: String,
|
||||||
|
#[serde(rename = "roleID")]
|
||||||
|
pub role_id: String,
|
||||||
|
pub name: String,
|
||||||
|
#[serde(rename = "ipAddress")]
|
||||||
|
pub ip_address: String,
|
||||||
|
#[serde(rename = "staticAddresses")]
|
||||||
|
pub static_addresses: Vec<SocketAddrV4>,
|
||||||
|
#[serde(rename = "listenPort")]
|
||||||
|
pub listen_port: i64,
|
||||||
|
#[serde(rename = "isLighthouse")]
|
||||||
|
pub is_lighthouse: bool,
|
||||||
|
#[serde(rename = "isRelay")]
|
||||||
|
pub is_relay: bool,
|
||||||
|
#[serde(rename = "createdAt")]
|
||||||
|
pub created_at: String,
|
||||||
|
#[serde(rename = "isBlocked")]
|
||||||
|
pub is_blocked: bool,
|
||||||
|
pub metadata: HostMetadata,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn list_hosts(server: Url) -> Result<(), Box<dyn Error>> {
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
|
// load session token
|
||||||
|
let sess_token_store = dirs::config_dir().unwrap().join("tfcli-session.token");
|
||||||
|
let session_token = fs::read_to_string(&sess_token_store)?;
|
||||||
|
let auth_token_store = dirs::config_dir().unwrap().join("tfcli-auth.token");
|
||||||
|
let auth_token = fs::read_to_string(&auth_token_store)?;
|
||||||
|
|
||||||
|
let token = format!("{} {}", session_token, auth_token);
|
||||||
|
|
||||||
|
let res = client.get(server.join("/v1/hosts")?).bearer_auth(token).send().await?;
|
||||||
|
|
||||||
|
if res.status().is_success() {
|
||||||
|
let resp: HostListResp = res.json().await?;
|
||||||
|
|
||||||
|
for host in &resp.data {
|
||||||
|
println!(" Host: {} ({})", host.name, host.id);
|
||||||
|
println!(" Organization: {}", host.organization_id);
|
||||||
|
println!(" Network: {}", host.network_id);
|
||||||
|
println!(" Role: {}", host.role_id);
|
||||||
|
println!(" IP Address: {}", host.ip_address);
|
||||||
|
println!(" Static Addresses: {}", host.static_addresses.iter().map(|u| u.to_string()).collect::<Vec<_>>().join(", "));
|
||||||
|
println!(" Listen Port: {}", host.listen_port);
|
||||||
|
println!(" Type: {}", if host.is_lighthouse { "Lighthouse" } else if host.is_relay { "Relay" } else { "Host" } );
|
||||||
|
println!(" Blocked: {}", host.is_blocked);
|
||||||
|
println!(" Last Seen: {}", host.metadata.last_seen_at);
|
||||||
|
println!(" Client Version: {}", host.metadata.version);
|
||||||
|
println!(" Platform: {}", host.metadata.platform);
|
||||||
|
println!("Client Update Available: {}", host.metadata.update_available);
|
||||||
|
println!(" Created: {}", host.created_at);
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.data.is_empty() {
|
||||||
|
println!("No hosts found");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let resp: APIErrorResponse = res.json().await?;
|
||||||
|
|
||||||
|
eprintln!("[error] Error listing hosts: {} {}", resp.errors[0].code, resp.errors[0].message);
|
||||||
|
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct HostCreateBody {
|
||||||
|
pub name: String,
|
||||||
|
#[serde(rename = "networkID")]
|
||||||
|
pub network_id: String,
|
||||||
|
#[serde(rename = "roleID")]
|
||||||
|
pub role_id: String,
|
||||||
|
#[serde(rename = "ipAddress")]
|
||||||
|
pub ip_address: Ipv4Addr,
|
||||||
|
#[serde(rename = "listenPort")]
|
||||||
|
pub listen_port: u16,
|
||||||
|
#[serde(rename = "isLighthouse")]
|
||||||
|
pub is_lighthouse: bool,
|
||||||
|
#[serde(rename = "isRelay")]
|
||||||
|
pub is_relay: bool,
|
||||||
|
#[serde(rename = "staticAddresses")]
|
||||||
|
pub static_addresses: Vec<SocketAddrV4>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct HostGetMetadata {}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct HostGetResponse {
|
||||||
|
pub data: Host,
|
||||||
|
pub metadata: HostGetMetadata,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_host(name: String, network_id: String, role_id: String, ip_address: Ipv4Addr, listen_port: Option<u16>, lighthouse: bool, relay: bool, static_address: Option<SocketAddrV4>, server: Url) -> Result<(), Box<dyn Error>> {
|
||||||
|
if lighthouse && relay {
|
||||||
|
eprintln!("[error] Error creating host: a host cannot be both a lighthouse and a relay at the same time");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lighthouse || relay) && static_address.is_none() {
|
||||||
|
eprintln!("[error] Error creating host: a relay or lighthouse must have a static IP");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lighthouse || relay) && listen_port.is_none() {
|
||||||
|
eprintln!("[error] Error creating host: a relay or lighthouse must have a listen port");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
|
// load session token
|
||||||
|
let sess_token_store = dirs::config_dir().unwrap().join("tfcli-session.token");
|
||||||
|
let session_token = fs::read_to_string(&sess_token_store)?;
|
||||||
|
let auth_token_store = dirs::config_dir().unwrap().join("tfcli-auth.token");
|
||||||
|
let auth_token = fs::read_to_string(&auth_token_store)?;
|
||||||
|
|
||||||
|
let token = format!("{} {}", session_token, auth_token);
|
||||||
|
|
||||||
|
let res = client.post(server.join(&format!("/v1/hosts"))?).json(&HostCreateBody {
|
||||||
|
name,
|
||||||
|
network_id,
|
||||||
|
role_id,
|
||||||
|
ip_address,
|
||||||
|
listen_port: listen_port.unwrap_or(0),
|
||||||
|
is_lighthouse: lighthouse,
|
||||||
|
is_relay: relay,
|
||||||
|
static_addresses: static_address.map_or(vec![], |u| vec![u]),
|
||||||
|
}).bearer_auth(token).send().await?;
|
||||||
|
|
||||||
|
if res.status().is_success() {
|
||||||
|
let host: Host = res.json::<HostGetResponse>().await?.data;
|
||||||
|
|
||||||
|
println!(" Host: {} ({})", host.name, host.id);
|
||||||
|
println!(" Organization: {}", host.organization_id);
|
||||||
|
println!(" Network: {}", host.network_id);
|
||||||
|
println!(" Role: {}", host.role_id);
|
||||||
|
println!(" IP Address: {}", host.ip_address);
|
||||||
|
println!(" Static Addresses: {}", host.static_addresses.iter().map(|u| u.to_string()).collect::<Vec<_>>().join(", "));
|
||||||
|
println!(" Listen Port: {}", host.listen_port);
|
||||||
|
println!(" Type: {}", if host.is_lighthouse { "Lighthouse" } else if host.is_relay { "Relay" } else { "Host" } );
|
||||||
|
println!(" Blocked: {}", host.is_blocked);
|
||||||
|
println!(" Last Seen: {}", host.metadata.last_seen_at);
|
||||||
|
println!(" Client Version: {}", host.metadata.version);
|
||||||
|
println!(" Platform: {}", host.metadata.platform);
|
||||||
|
println!("Client Update Available: {}", host.metadata.update_available);
|
||||||
|
println!(" Created: {}", host.created_at);
|
||||||
|
println!();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
let resp: APIErrorResponse = res.json().await?;
|
||||||
|
|
||||||
|
eprintln!("[error] Error creating host: {} {}", resp.errors[0].code, resp.errors[0].message);
|
||||||
|
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_host(id: String, server: Url) -> Result<(), Box<dyn Error>> {
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
|
// load session token
|
||||||
|
let sess_token_store = dirs::config_dir().unwrap().join("tfcli-session.token");
|
||||||
|
let session_token = fs::read_to_string(&sess_token_store)?;
|
||||||
|
let auth_token_store = dirs::config_dir().unwrap().join("tfcli-auth.token");
|
||||||
|
let auth_token = fs::read_to_string(&auth_token_store)?;
|
||||||
|
|
||||||
|
let token = format!("{} {}", session_token, auth_token);
|
||||||
|
|
||||||
|
let res = client.get(server.join(&format!("/v1/hosts/{}", id))?).bearer_auth(token).send().await?;
|
||||||
|
|
||||||
|
if res.status().is_success() {
|
||||||
|
let host: Host = res.json::<HostGetResponse>().await?.data;
|
||||||
|
|
||||||
|
println!(" Host: {} ({})", host.name, host.id);
|
||||||
|
println!(" Organization: {}", host.organization_id);
|
||||||
|
println!(" Network: {}", host.network_id);
|
||||||
|
println!(" Role: {}", host.role_id);
|
||||||
|
println!(" IP Address: {}", host.ip_address);
|
||||||
|
println!(" Static Addresses: {}", host.static_addresses.iter().map(|u| u.to_string()).collect::<Vec<_>>().join(", "));
|
||||||
|
println!(" Listen Port: {}", host.listen_port);
|
||||||
|
println!(" Type: {}", if host.is_lighthouse { "Lighthouse" } else if host.is_relay { "Relay" } else { "Host" } );
|
||||||
|
println!(" Blocked: {}", host.is_blocked);
|
||||||
|
println!(" Last Seen: {}", host.metadata.last_seen_at);
|
||||||
|
println!(" Client Version: {}", host.metadata.version);
|
||||||
|
println!(" Platform: {}", host.metadata.platform);
|
||||||
|
println!("Client Update Available: {}", host.metadata.update_available);
|
||||||
|
println!(" Created: {}", host.created_at);
|
||||||
|
println!();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
let resp: APIErrorResponse = res.json().await?;
|
||||||
|
|
||||||
|
eprintln!("[error] Error listing hosts: {} {}", resp.errors[0].code, resp.errors[0].message);
|
||||||
|
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete_host(id: String, server: Url) -> Result<(), Box<dyn Error>> {
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
|
// load session token
|
||||||
|
let sess_token_store = dirs::config_dir().unwrap().join("tfcli-session.token");
|
||||||
|
let session_token = fs::read_to_string(&sess_token_store)?;
|
||||||
|
let auth_token_store = dirs::config_dir().unwrap().join("tfcli-auth.token");
|
||||||
|
let auth_token = fs::read_to_string(&auth_token_store)?;
|
||||||
|
|
||||||
|
let token = format!("{} {}", session_token, auth_token);
|
||||||
|
|
||||||
|
let res = client.delete(server.join(&format!("/v1/hosts/{}", id))?).bearer_auth(token).send().await?;
|
||||||
|
|
||||||
|
if res.status().is_success() {
|
||||||
|
println!("Host removed");
|
||||||
|
} else {
|
||||||
|
let resp: APIErrorResponse = res.json().await?;
|
||||||
|
|
||||||
|
eprintln!("[error] Error removing host: {} {}", resp.errors[0].code, resp.errors[0].message);
|
||||||
|
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct HostUpdateBody {
|
||||||
|
#[serde(rename = "listenPort")]
|
||||||
|
pub listen_port: u16,
|
||||||
|
#[serde(rename = "staticAddresses")]
|
||||||
|
pub static_addresses: Vec<SocketAddrV4>,
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub ip: Option<Ipv4Addr>,
|
||||||
|
pub role: Option<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_host(id: String, listen_port: Option<u16>, static_address: Option<SocketAddrV4>, name: Option<String>, ip: Option<Ipv4Addr>, role: Option<String>, server: Url) -> Result<(), Box<dyn Error>> {
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
|
// load session token
|
||||||
|
let sess_token_store = dirs::config_dir().unwrap().join("tfcli-session.token");
|
||||||
|
let session_token = fs::read_to_string(&sess_token_store)?;
|
||||||
|
let auth_token_store = dirs::config_dir().unwrap().join("tfcli-auth.token");
|
||||||
|
let auth_token = fs::read_to_string(&auth_token_store)?;
|
||||||
|
|
||||||
|
let token = format!("{} {}", session_token, auth_token);
|
||||||
|
|
||||||
|
let res = client.put(server.join(&format!("/v1/hosts/{}?extension=extended_hosts", id))?).json(&HostUpdateBody {
|
||||||
|
listen_port: listen_port.unwrap_or(0),
|
||||||
|
static_addresses: static_address.map_or_else(|| vec![], |u| vec![u]),
|
||||||
|
name,
|
||||||
|
ip,
|
||||||
|
role
|
||||||
|
}).bearer_auth(token).send().await?;
|
||||||
|
|
||||||
|
if res.status().is_success() {
|
||||||
|
let host: Host = res.json::<HostGetResponse>().await?.data;
|
||||||
|
|
||||||
|
println!(" Host: {} ({})", host.name, host.id);
|
||||||
|
println!(" Organization: {}", host.organization_id);
|
||||||
|
println!(" Network: {}", host.network_id);
|
||||||
|
println!(" Role: {}", host.role_id);
|
||||||
|
println!(" IP Address: {}", host.ip_address);
|
||||||
|
println!(" Static Addresses: {}", host.static_addresses.iter().map(|u| u.to_string()).collect::<Vec<_>>().join(", "));
|
||||||
|
println!(" Listen Port: {}", host.listen_port);
|
||||||
|
println!(" Type: {}", if host.is_lighthouse { "Lighthouse" } else if host.is_relay { "Relay" } else { "Host" } );
|
||||||
|
println!(" Blocked: {}", host.is_blocked);
|
||||||
|
println!(" Last Seen: {}", host.metadata.last_seen_at);
|
||||||
|
println!(" Client Version: {}", host.metadata.version);
|
||||||
|
println!(" Platform: {}", host.metadata.platform);
|
||||||
|
println!("Client Update Available: {}", host.metadata.update_available);
|
||||||
|
println!(" Created: {}", host.created_at);
|
||||||
|
println!();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
let resp: APIErrorResponse = res.json().await?;
|
||||||
|
|
||||||
|
eprintln!("[error] Error updating host: {} {}", resp.errors[0].code, resp.errors[0].message);
|
||||||
|
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct EnrollmentCodeResponseMetadata {}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct EnrollmentCode {
|
||||||
|
pub code: String,
|
||||||
|
#[serde(rename = "lifetimeSeconds")]
|
||||||
|
pub lifetime_seconds: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct EnrollmentResponseData {
|
||||||
|
#[serde(rename = "enrollmentCode")]
|
||||||
|
pub enrollment_code: EnrollmentCode,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct EnrollmentResponse {
|
||||||
|
pub data: EnrollmentResponseData,
|
||||||
|
pub metadata: EnrollmentCodeResponseMetadata,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn enroll_host(id: String, server: Url) -> Result<(), Box<dyn Error>> {
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
|
// load session token
|
||||||
|
let sess_token_store = dirs::config_dir().unwrap().join("tfcli-session.token");
|
||||||
|
let session_token = fs::read_to_string(&sess_token_store)?;
|
||||||
|
let auth_token_store = dirs::config_dir().unwrap().join("tfcli-auth.token");
|
||||||
|
let auth_token = fs::read_to_string(&auth_token_store)?;
|
||||||
|
|
||||||
|
let token = format!("{} {}", session_token, auth_token);
|
||||||
|
|
||||||
|
let res = client.post(server.join(&format!("/v1/hosts/{}/enrollment-code", id))?).bearer_auth(token).send().await?;
|
||||||
|
|
||||||
|
if res.status().is_success() {
|
||||||
|
let resp: EnrollmentResponse = res.json().await?;
|
||||||
|
|
||||||
|
println!("Enrollment code generated. Enroll the host with the following code: {}", resp.data.enrollment_code.code);
|
||||||
|
println!("This code will be valid for {} seconds, at which point you will need to generate a new code", resp.data.enrollment_code.lifetime_seconds);
|
||||||
|
println!("If this host is blocked, a successful re-enrollment will unblock it.");
|
||||||
|
} else {
|
||||||
|
let resp: APIErrorResponse = res.json().await?;
|
||||||
|
|
||||||
|
eprintln!("[error] Error blocking host: {} {}", resp.errors[0].code, resp.errors[0].message);
|
||||||
|
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn block_host(id: String, server: Url) -> Result<(), Box<dyn Error>> {
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
|
// load session token
|
||||||
|
let sess_token_store = dirs::config_dir().unwrap().join("tfcli-session.token");
|
||||||
|
let session_token = fs::read_to_string(&sess_token_store)?;
|
||||||
|
let auth_token_store = dirs::config_dir().unwrap().join("tfcli-auth.token");
|
||||||
|
let auth_token = fs::read_to_string(&auth_token_store)?;
|
||||||
|
|
||||||
|
let token = format!("{} {}", session_token, auth_token);
|
||||||
|
|
||||||
|
let res = client.post(server.join(&format!("/v1/hosts/{}/block", id))?).bearer_auth(token).send().await?;
|
||||||
|
|
||||||
|
if res.status().is_success() {
|
||||||
|
println!("Host blocked. To unblock it, re-enroll the host.");
|
||||||
|
} else {
|
||||||
|
let resp: APIErrorResponse = res.json().await?;
|
||||||
|
|
||||||
|
eprintln!("[error] Error blocking host: {} {}", resp.errors[0].code, resp.errors[0].message);
|
||||||
|
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -1,9 +1,11 @@
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::net::{Ipv4Addr, SocketAddrV4};
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use ipnet::Ipv4Net;
|
use ipnet::Ipv4Net;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use crate::account::account_main;
|
use crate::account::account_main;
|
||||||
|
use crate::host::host_main;
|
||||||
use crate::network::network_main;
|
use crate::network::network_main;
|
||||||
use crate::org::org_main;
|
use crate::org::org_main;
|
||||||
use crate::role::role_main;
|
use crate::role::role_main;
|
||||||
|
@ -13,6 +15,7 @@ pub mod api;
|
||||||
pub mod network;
|
pub mod network;
|
||||||
pub mod org;
|
pub mod org;
|
||||||
pub mod role;
|
pub mod role;
|
||||||
|
pub mod host;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(author, version, about, long_about = None)]
|
#[command(author, version, about, long_about = None)]
|
||||||
|
@ -46,6 +49,11 @@ pub enum Commands {
|
||||||
Role {
|
Role {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
command: RoleCommands
|
command: RoleCommands
|
||||||
|
},
|
||||||
|
/// Manage the hosts associated with your trifid network
|
||||||
|
Host {
|
||||||
|
#[command(subcommand)]
|
||||||
|
command: HostCommands
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,6 +64,11 @@ pub enum AccountCommands {
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
email: String
|
email: String
|
||||||
},
|
},
|
||||||
|
/// Log into an existing account on the designated server
|
||||||
|
Login {
|
||||||
|
#[clap(short, long)]
|
||||||
|
email: String
|
||||||
|
},
|
||||||
/// Log in to your account with a magic-link token acquired via email or the trifid-api logs.
|
/// Log in to your account with a magic-link token acquired via email or the trifid-api logs.
|
||||||
MagicLink {
|
MagicLink {
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
|
@ -133,6 +146,66 @@ pub enum RoleCommands {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand, Debug)]
|
||||||
|
pub enum HostCommands {
|
||||||
|
/// Create a host on your network
|
||||||
|
Create {
|
||||||
|
#[clap(short, long)]
|
||||||
|
name: String,
|
||||||
|
#[clap(short = 'N', long)]
|
||||||
|
network_id: String,
|
||||||
|
#[clap(short, long)]
|
||||||
|
role_id: String,
|
||||||
|
#[clap(short, long)]
|
||||||
|
ip_address: Ipv4Addr,
|
||||||
|
#[clap(short, long)]
|
||||||
|
listen_port: Option<u16>,
|
||||||
|
#[clap(short = 'L', long)]
|
||||||
|
lighthouse: bool,
|
||||||
|
#[clap(short = 'R', long)]
|
||||||
|
relay: bool,
|
||||||
|
#[clap(short, long)]
|
||||||
|
static_address: Option<SocketAddrV4>
|
||||||
|
},
|
||||||
|
/// List all hosts on your network
|
||||||
|
List {},
|
||||||
|
/// Lookup a specific host by it's ID
|
||||||
|
Lookup {
|
||||||
|
#[clap(short, long)]
|
||||||
|
id: String
|
||||||
|
},
|
||||||
|
/// Delete a specific host by it's ID
|
||||||
|
Delete {
|
||||||
|
#[clap(short, long)]
|
||||||
|
id: String
|
||||||
|
},
|
||||||
|
/// Update a specific host by it's ID, changing the listen port and static addresses, as well as the name, ip and role. The name, ip and role updates will only work on trifid-api compatible servers.
|
||||||
|
Update {
|
||||||
|
#[clap(short, long)]
|
||||||
|
id: String,
|
||||||
|
#[clap(short, long)]
|
||||||
|
listen_port: Option<u16>,
|
||||||
|
#[clap(short, long)]
|
||||||
|
static_address: Option<SocketAddrV4>,
|
||||||
|
#[clap(short, long)]
|
||||||
|
name: Option<String>,
|
||||||
|
#[clap(short, long)]
|
||||||
|
role: Option<String>,
|
||||||
|
#[clap(short = 'I', long)]
|
||||||
|
ip: Option<Ipv4Addr>
|
||||||
|
},
|
||||||
|
/// Blocks the specified host from the network
|
||||||
|
Block {
|
||||||
|
#[clap(short, long)]
|
||||||
|
id: String
|
||||||
|
},
|
||||||
|
/// Enroll or re-enroll the host by generating an enrollment code
|
||||||
|
Enroll {
|
||||||
|
#[clap(short, long)]
|
||||||
|
id: String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
match main2().await {
|
match main2().await {
|
||||||
|
@ -176,6 +249,7 @@ async fn main2() -> Result<(), Box<dyn Error>> {
|
||||||
Commands::Account { command } => account_main(command, server).await,
|
Commands::Account { command } => account_main(command, server).await,
|
||||||
Commands::Network { command } => network_main(command, server).await,
|
Commands::Network { command } => network_main(command, server).await,
|
||||||
Commands::Org { command } => org_main(command, server).await,
|
Commands::Org { command } => org_main(command, server).await,
|
||||||
Commands::Role { command } => role_main(command, server).await
|
Commands::Role { command } => role_main(command, server).await,
|
||||||
|
Commands::Host { command } => host_main(command, server).await
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1212,6 +1212,8 @@ pub struct EditHostRequest {
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
// t+features:extended_hosts
|
// t+features:extended_hosts
|
||||||
pub ip: Option<Ipv4Addr>,
|
pub ip: Option<Ipv4Addr>,
|
||||||
|
// t+features:extended_hosts
|
||||||
|
pub role: Option<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
@ -1461,6 +1463,10 @@ pub async fn edit_host(
|
||||||
debug!("updated host ip");
|
debug!("updated host ip");
|
||||||
host_active_model.ip = Set(new_host_ip.to_string());
|
host_active_model.ip = Set(new_host_ip.to_string());
|
||||||
}
|
}
|
||||||
|
if let Some(new_role) = req.role.clone() {
|
||||||
|
debug!("updated host role");
|
||||||
|
host_active_model.role = Set(new_role);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let host = match host_active_model.update(&db.conn).await {
|
let host = match host_active_model.update(&db.conn).await {
|
||||||
|
|
Loading…
Reference in New Issue