0.3 error fixes, refmt
This commit is contained in:
parent
2a5a2bb910
commit
646340b637
|
@ -254,7 +254,10 @@ impl Client {
|
|||
|
||||
ed_privkey.verify(b64_msg_bytes, &Signature::from_slice(&signature)?)?;
|
||||
debug!("signature valid via clientside check");
|
||||
debug!("signed with key: {:x?}", ed_privkey.verifying_key().as_bytes());
|
||||
debug!(
|
||||
"signed with key: {:x?}",
|
||||
ed_privkey.verifying_key().as_bytes()
|
||||
);
|
||||
|
||||
let body = RequestV1 {
|
||||
version: 1,
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# API Support
|
||||
|
||||
This document is valid for: `trifid-api 0.3.0`.
|
||||
|
||||
This document is only useful for developers, and it lists what endpoint versions are currently supported by trifid-api. This is subject to change at any time according to SemVer constraints.
|
||||
|
||||
Endpoint types:
|
||||
- **Documented** is an endpoint available in the official documentation
|
||||
- **Reverse-engineered** is an endpoint that was reverse-engineered
|
||||
|
||||
| Endpoint Name | Version | Endpoint | Type | Added In |
|
||||
|---------------------------|---------|------------------------------------|--------------------|----------------|
|
||||
| Signup | v1 | POST /v1/signup | Reverse-engineered | 0.3.0/79b1765e |
|
||||
| Get Magic Link | v1 | POST /v1/auth/magic-link | Reverse-engineered | 0.3.0/52049947 |
|
||||
| Verify Magic Link | v1 | POST /v1/auth/verify-magic-link | Reverse-engineered | 0.3.0/51b6d3a8 |
|
||||
| Create TOTP Authenticator | v1 | POST /v1/totp-authenticators | Reverse-engineered | 0.3.0/4180bdd1 |
|
||||
| Verify TOTP Authenticator | v1 | POST /v1/verify-totp-authenticator | Reverse-engineered | 0.3.0/19332e51 |
|
||||
| Authenticate with TOTP | v1 | POST /v1/auth/totp | Reverse-engineered | 0.3.0/19332e51 |
|
|
@ -1,7 +1,7 @@
|
|||
use std::{env, process};
|
||||
use std::path::PathBuf;
|
||||
use bindgen::CargoCallbacks;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::{env, process};
|
||||
|
||||
fn get_cargo_target_dir() -> Result<std::path::PathBuf, Box<dyn std::error::Error>> {
|
||||
let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR")?);
|
||||
|
@ -20,7 +20,6 @@ fn get_cargo_target_dir() -> Result<std::path::PathBuf, Box<dyn std::error::Erro
|
|||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
// Find compiler:
|
||||
// 1. GOC
|
||||
// 2. /usr/local/go/bin/go
|
||||
|
@ -49,7 +48,14 @@ fn main() {
|
|||
let out = out_path.join(out_file);
|
||||
|
||||
let mut command = process::Command::new(compiler);
|
||||
command.args(["build", "-buildmode", link_type().as_str(), "-o", out.display().to_string().as_str(), "main.go"]);
|
||||
command.args([
|
||||
"build",
|
||||
"-buildmode",
|
||||
link_type().as_str(),
|
||||
"-o",
|
||||
out.display().to_string().as_str(),
|
||||
"main.go",
|
||||
]);
|
||||
command.env("CGO_ENABLED", "1");
|
||||
command.env("CC", c_compiler.path());
|
||||
command.env("GOARCH", goarch());
|
||||
|
@ -68,7 +74,10 @@ fn main() {
|
|||
copy_if_windows();
|
||||
|
||||
print_link();
|
||||
println!("cargo:rustc-link-search=native={}", env::var("OUT_DIR").unwrap());
|
||||
println!(
|
||||
"cargo:rustc-link-search=native={}",
|
||||
env::var("OUT_DIR").unwrap()
|
||||
);
|
||||
|
||||
//let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
|
||||
|
@ -85,7 +94,6 @@ fn main() {
|
|||
.generate()
|
||||
.expect("Error generating CFFI bindings");
|
||||
|
||||
|
||||
bindings
|
||||
.write_to_file(out_path.join("bindings.rs"))
|
||||
.expect("Couldn't write bindings!");
|
||||
|
@ -125,8 +133,9 @@ fn goarch() -> String {
|
|||
"powerpc64" => "ppc64",
|
||||
"arm" => "arm",
|
||||
"aarch64" => "arm64",
|
||||
arch => panic!("unsupported architecture {arch}")
|
||||
}.to_string()
|
||||
arch => panic!("unsupported architecture {arch}"),
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
fn goos() -> String {
|
||||
match env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() {
|
||||
|
@ -139,8 +148,9 @@ fn goos() -> String {
|
|||
"dragonfly" => "dragonfly",
|
||||
"openbsd" => "openbsd",
|
||||
"netbsd" => "netbsd",
|
||||
os => panic!("unsupported operating system {os}")
|
||||
}.to_string()
|
||||
os => panic!("unsupported operating system {os}"),
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn print_link() {
|
||||
|
@ -157,4 +167,4 @@ fn link_type() -> String {
|
|||
} else {
|
||||
"c-archive".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#![deny(clippy::missing_panics_doc)]
|
||||
#![deny(clippy::missing_safety_doc)]
|
||||
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
#[allow(non_camel_case_types)]
|
||||
#[allow(non_snake_case)]
|
||||
|
@ -36,12 +35,11 @@ pub mod generated {
|
|||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||
}
|
||||
|
||||
use generated::GoString;
|
||||
use std::error::Error;
|
||||
use std::ffi::{c_char, CString};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::path::{Path};
|
||||
use generated::GoString;
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
impl From<&str> for GoString {
|
||||
#[allow(clippy::cast_possible_wrap)]
|
||||
|
@ -51,7 +49,7 @@ impl From<&str> for GoString {
|
|||
let ptr = c_str.as_ptr();
|
||||
let go_string = GoString {
|
||||
p: ptr,
|
||||
n: c_str.as_bytes().len() as isize
|
||||
n: c_str.as_bytes().len() as isize,
|
||||
};
|
||||
go_string
|
||||
}
|
||||
|
@ -73,14 +71,18 @@ impl NebulaInstance {
|
|||
/// # Panics
|
||||
/// This function will panic if memory is corrupted while communicating with Go.
|
||||
pub fn new(config_path: &Path, config_test: bool) -> Result<Self, Box<dyn Error>> {
|
||||
let mut config_path_bytes = unsafe { config_path.display().to_string().as_bytes_mut().to_vec() };
|
||||
let mut config_path_bytes =
|
||||
unsafe { config_path.display().to_string().as_bytes_mut().to_vec() };
|
||||
config_path_bytes.push(0u8);
|
||||
let config_test_u8 = u8::from(config_test);
|
||||
|
||||
let res;
|
||||
|
||||
unsafe {
|
||||
res = generated::NebulaSetup(config_path_bytes.as_mut_ptr().cast::<c_char>(), config_test_u8);
|
||||
res = generated::NebulaSetup(
|
||||
config_path_bytes.as_mut_ptr().cast::<c_char>(),
|
||||
config_test_u8,
|
||||
);
|
||||
}
|
||||
|
||||
let res = cstring_to_string(res);
|
||||
|
@ -194,18 +196,18 @@ pub enum NebulaError {
|
|||
/// Returned by nebula when the TUN/TAP device already exists
|
||||
DeviceOrResourceBusy {
|
||||
/// The complete error string returned by the Nebula wrapper
|
||||
error_str: String
|
||||
error_str: String,
|
||||
},
|
||||
/// An unknown error that the error parser couldn't figure out how to parse.
|
||||
Unknown {
|
||||
/// The complete error string returned by the Nebula wrapper
|
||||
error_str: String
|
||||
error_str: String,
|
||||
},
|
||||
/// Occurs if you call a function before NebulaSetup has been called
|
||||
NebulaNotSetup {
|
||||
/// The complete error string returned by the Nebula wrapper
|
||||
error_str: String
|
||||
}
|
||||
error_str: String,
|
||||
},
|
||||
}
|
||||
impl Display for NebulaError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
|
@ -223,11 +225,17 @@ impl NebulaError {
|
|||
#[must_use]
|
||||
pub fn from_string(string: &str) -> Self {
|
||||
if string.starts_with("device or resource busy") {
|
||||
Self::DeviceOrResourceBusy { error_str: string.to_string() }
|
||||
Self::DeviceOrResourceBusy {
|
||||
error_str: string.to_string(),
|
||||
}
|
||||
} else if string.starts_with("NebulaSetup has not yet been called") {
|
||||
Self::NebulaNotSetup { error_str: string.to_string() }
|
||||
Self::NebulaNotSetup {
|
||||
error_str: string.to_string(),
|
||||
}
|
||||
} else {
|
||||
Self::Unknown { error_str: string.to_string() }
|
||||
Self::Unknown {
|
||||
error_str: string.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +1,48 @@
|
|||
use crate::api::APIErrorResponse;
|
||||
use crate::AccountCommands;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::error::Error;
|
||||
use std::fs;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
use crate::AccountCommands;
|
||||
use crate::api::APIErrorResponse;
|
||||
|
||||
pub async fn account_main(command: AccountCommands, server: Url) -> Result<(), Box<dyn Error>> {
|
||||
match command {
|
||||
AccountCommands::Create { email } => create_account(email, server).await,
|
||||
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::MfaSetupFinish {code, token} => finish_mfa_authenticator(token, code, server).await,
|
||||
AccountCommands::Mfa {code} => mfa_auth(code, server).await,
|
||||
AccountCommands::Login { email } => login_account(email, server).await
|
||||
AccountCommands::MfaSetupFinish { code, token } => {
|
||||
finish_mfa_authenticator(token, code, server).await
|
||||
}
|
||||
AccountCommands::Mfa { code } => mfa_auth(code, server).await,
|
||||
AccountCommands::Login { email } => login_account(email, server).await,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct CreateAccountBody {
|
||||
pub email: String
|
||||
pub email: String,
|
||||
}
|
||||
|
||||
pub async fn create_account(email: String, server: Url) -> Result<(), Box<dyn Error>> {
|
||||
let client = reqwest::Client::new();
|
||||
let res = client.post(server.join("/v1/signup")?).json(&CreateAccountBody { email }).send().await?;
|
||||
let res = client
|
||||
.post(server.join("/v1/signup")?)
|
||||
.json(&CreateAccountBody { email })
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if res.status().is_success() {
|
||||
println!("Account created successfully, 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 creating account: {} {}", resp.errors[0].code, resp.errors[0].message);
|
||||
eprintln!(
|
||||
"[error] Error creating account: {} {}",
|
||||
resp.errors[0].code, resp.errors[0].message
|
||||
);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
@ -42,21 +52,27 @@ pub async fn create_account(email: String, server: Url) -> Result<(), Box<dyn Er
|
|||
|
||||
#[derive(Serialize)]
|
||||
pub struct LoginAccountBody {
|
||||
pub email: String
|
||||
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?;
|
||||
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);
|
||||
eprintln!(
|
||||
"[error] Error logging in: {} {}",
|
||||
resp.errors[0].code, resp.errors[0].message
|
||||
);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
@ -64,26 +80,31 @@ pub async fn login_account(email: String, server: Url) -> Result<(), Box<dyn Err
|
|||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct MagicLinkBody {
|
||||
#[serde(rename = "magicLinkToken")]
|
||||
pub magic_link_token: String
|
||||
pub magic_link_token: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct MagicLinkSuccess {
|
||||
pub data: MagicLinkSuccessBody
|
||||
pub data: MagicLinkSuccessBody,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
pub struct MagicLinkSuccessBody {
|
||||
#[serde(rename = "sessionToken")]
|
||||
pub session_token: String
|
||||
pub session_token: String,
|
||||
}
|
||||
|
||||
pub async fn auth_magic_link(magic_token: String, server: Url) -> Result<(), Box<dyn Error>> {
|
||||
let client = reqwest::Client::new();
|
||||
let res = client.post(server.join("/v1/auth/verify-magic-link")?).json(&MagicLinkBody { magic_link_token: magic_token }).send().await?;
|
||||
let res = client
|
||||
.post(server.join("/v1/auth/verify-magic-link")?)
|
||||
.json(&MagicLinkBody {
|
||||
magic_link_token: magic_token,
|
||||
})
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if res.status().is_success() {
|
||||
let resp: MagicLinkSuccess = res.json().await?;
|
||||
|
@ -97,7 +118,10 @@ pub async fn auth_magic_link(magic_token: String, server: Url) -> Result<(), Box
|
|||
} else {
|
||||
let resp: APIErrorResponse = res.json().await?;
|
||||
|
||||
eprintln!("[error] Error getting session token: {} {}", resp.errors[0].code, resp.errors[0].message);
|
||||
eprintln!(
|
||||
"[error] Error getting session token: {} {}",
|
||||
resp.errors[0].code, resp.errors[0].message
|
||||
);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
@ -135,14 +159,14 @@ pub struct WhoamiResponseMetadata {}
|
|||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CreateMfaResponse {
|
||||
pub data: CreateMfaResponseData
|
||||
pub data: CreateMfaResponseData,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
pub struct CreateMfaResponseData {
|
||||
#[serde(rename = "totpToken")]
|
||||
pub totp_token: String,
|
||||
pub secret: String,
|
||||
pub url: String
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
pub async fn create_mfa_authenticator(server: Url) -> Result<(), Box<dyn Error>> {
|
||||
|
@ -153,14 +177,25 @@ pub async fn create_mfa_authenticator(server: Url) -> Result<(), Box<dyn Error>>
|
|||
let session_token = fs::read_to_string(&token_store)?;
|
||||
|
||||
// do we have mfa already?
|
||||
let whoami: WhoamiResponse = client.get(server.join("/v2/whoami")?).bearer_auth(&session_token).send().await?.json().await?;
|
||||
let whoami: WhoamiResponse = client
|
||||
.get(server.join("/v2/whoami")?)
|
||||
.bearer_auth(&session_token)
|
||||
.send()
|
||||
.await?
|
||||
.json()
|
||||
.await?;
|
||||
|
||||
if whoami.data.actor.has_totp_authenticator {
|
||||
eprintln!("[error] user already has a totp authenticator, cannot add another one");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let res = client.post(server.join("/v1/totp-authenticators")?).bearer_auth(&session_token).body("{}").send().await?;
|
||||
let res = client
|
||||
.post(server.join("/v1/totp-authenticators")?)
|
||||
.bearer_auth(&session_token)
|
||||
.body("{}")
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if res.status().is_success() {
|
||||
let resp: CreateMfaResponse = res.json().await?;
|
||||
|
@ -169,14 +204,23 @@ pub async fn create_mfa_authenticator(server: Url) -> Result<(), Box<dyn Error>>
|
|||
println!("To complete setup, you'll need a TOTP-compatible app, such as Google Authenticator or Authy.");
|
||||
println!("Scan the following code with your authenticator app:");
|
||||
qr2term::print_qr(resp.data.url)?;
|
||||
println!("Alternatively, enter the following secret into your authenticator app: '{}'", resp.data.secret);
|
||||
println!(
|
||||
"Alternatively, enter the following secret into your authenticator app: '{}'",
|
||||
resp.data.secret
|
||||
);
|
||||
println!("Once done, enable TOTP by running the following command with the code shown on your authenticator app:");
|
||||
println!("tfcli account mfa-setup-finish --token {} --code [CODE IN AUTHENTICATOR]", resp.data.totp_token);
|
||||
println!(
|
||||
"tfcli account mfa-setup-finish --token {} --code [CODE IN AUTHENTICATOR]",
|
||||
resp.data.totp_token
|
||||
);
|
||||
println!("This code will expire in 10 minutes.");
|
||||
} else {
|
||||
let resp: APIErrorResponse = res.json().await?;
|
||||
|
||||
eprintln!("[error] Error adding MFA to account: {} {}", resp.errors[0].code, resp.errors[0].message);
|
||||
eprintln!(
|
||||
"[error] Error adding MFA to account: {} {}",
|
||||
resp.errors[0].code, resp.errors[0].message
|
||||
);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
@ -188,27 +232,39 @@ pub async fn create_mfa_authenticator(server: Url) -> Result<(), Box<dyn Error>>
|
|||
pub struct MfaVerifyBody {
|
||||
#[serde(rename = "totpToken")]
|
||||
pub totp_token: String,
|
||||
pub code: String
|
||||
pub code: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct MFASuccess {
|
||||
pub data: MFASuccessBody
|
||||
pub data: MFASuccessBody,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
pub struct MFASuccessBody {
|
||||
#[serde(rename = "authToken")]
|
||||
pub auth_token: String
|
||||
pub auth_token: String,
|
||||
}
|
||||
|
||||
pub async fn finish_mfa_authenticator(token: String, code: String, server: Url) -> Result<(), Box<dyn Error>> {
|
||||
pub async fn finish_mfa_authenticator(
|
||||
token: String,
|
||||
code: String,
|
||||
server: Url,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
// load session token
|
||||
let token_store = dirs::config_dir().unwrap().join("tfcli-session.token");
|
||||
let session_token = fs::read_to_string(&token_store)?;
|
||||
|
||||
let res = client.post(server.join("/v1/verify-totp-authenticators")?).json(&MfaVerifyBody {totp_token: token, code }).bearer_auth(session_token).send().await?;
|
||||
let res = client
|
||||
.post(server.join("/v1/verify-totp-authenticators")?)
|
||||
.json(&MfaVerifyBody {
|
||||
totp_token: token,
|
||||
code,
|
||||
})
|
||||
.bearer_auth(session_token)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if res.status().is_success() {
|
||||
let resp: MFASuccess = res.json().await?;
|
||||
|
@ -222,7 +278,10 @@ pub async fn finish_mfa_authenticator(token: String, code: String, server: Url)
|
|||
} else {
|
||||
let resp: APIErrorResponse = res.json().await?;
|
||||
|
||||
eprintln!("[error] Error verifying MFA code: {} {}", resp.errors[0].code, resp.errors[0].message);
|
||||
eprintln!(
|
||||
"[error] Error verifying MFA code: {} {}",
|
||||
resp.errors[0].code, resp.errors[0].message
|
||||
);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
@ -232,7 +291,7 @@ pub async fn finish_mfa_authenticator(token: String, code: String, server: Url)
|
|||
|
||||
#[derive(Serialize)]
|
||||
pub struct MfaAuthBody {
|
||||
pub code: String
|
||||
pub code: String,
|
||||
}
|
||||
|
||||
pub async fn mfa_auth(code: String, server: Url) -> Result<(), Box<dyn Error>> {
|
||||
|
@ -242,7 +301,12 @@ pub async fn mfa_auth(code: String, server: Url) -> Result<(), Box<dyn Error>> {
|
|||
let token_store = dirs::config_dir().unwrap().join("tfcli-session.token");
|
||||
let session_token = fs::read_to_string(&token_store)?;
|
||||
|
||||
let res = client.post(server.join("/v1/auth/totp")?).json(&MfaAuthBody { code }).bearer_auth(session_token).send().await?;
|
||||
let res = client
|
||||
.post(server.join("/v1/auth/totp")?)
|
||||
.json(&MfaAuthBody { code })
|
||||
.bearer_auth(session_token)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if res.status().is_success() {
|
||||
let resp: MFASuccess = res.json().await?;
|
||||
|
@ -256,7 +320,10 @@ pub async fn mfa_auth(code: String, server: Url) -> Result<(), Box<dyn Error>> {
|
|||
} else {
|
||||
let resp: APIErrorResponse = res.json().await?;
|
||||
|
||||
eprintln!("[error] Error verifying MFA code: {} {}", resp.errors[0].code, resp.errors[0].message);
|
||||
eprintln!(
|
||||
"[error] Error verifying MFA code: {} {}",
|
||||
resp.errors[0].code, resp.errors[0].message
|
||||
);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
|
|
@ -2,11 +2,11 @@ use serde::Deserialize;
|
|||
|
||||
#[derive(Deserialize)]
|
||||
pub struct APIErrorResponse {
|
||||
pub errors: Vec<APIError>
|
||||
pub errors: Vec<APIError>,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
pub struct APIError {
|
||||
pub code: String,
|
||||
pub message: String,
|
||||
pub path: Option<String>
|
||||
}
|
||||
pub path: Option<String>,
|
||||
}
|
||||
|
|
|
@ -1,34 +1,68 @@
|
|||
use crate::api::APIErrorResponse;
|
||||
use crate::{HostCommands, HostOverrideCommands};
|
||||
use serde::{Deserialize, Serialize};
|
||||
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, HostOverrideCommands};
|
||||
use url::Url;
|
||||
|
||||
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::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::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,
|
||||
HostCommands::Overrides { command } => match command {
|
||||
HostOverrideCommands::List { id } => list_overrides(id, server).await,
|
||||
HostOverrideCommands::Set { id, key, boolean, string, numeric } => set_override(id, key, boolean, numeric, string, server).await,
|
||||
HostOverrideCommands::Unset { id, key } => unset_override(id, key, server).await
|
||||
}
|
||||
HostOverrideCommands::Set {
|
||||
id,
|
||||
key,
|
||||
boolean,
|
||||
string,
|
||||
numeric,
|
||||
} => set_override(id, key, boolean, numeric, string, server).await,
|
||||
HostOverrideCommands::Unset { id, key } => unset_override(id, key, server).await,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct HostListResp {
|
||||
pub data: Vec<Host>
|
||||
pub data: Vec<Host>,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct HostMetadata {
|
||||
#[serde(rename = "lastSeenAt")]
|
||||
|
@ -77,7 +111,11 @@ pub async fn list_hosts(server: Url) -> Result<(), Box<dyn Error>> {
|
|||
|
||||
let token = format!("{} {}", session_token, auth_token);
|
||||
|
||||
let res = client.get(server.join("/v1/hosts?pageSize=5000")?).bearer_auth(token).send().await?;
|
||||
let res = client
|
||||
.get(server.join("/v1/hosts?pageSize=5000")?)
|
||||
.bearer_auth(token)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if res.status().is_success() {
|
||||
let resp: HostListResp = res.json().await?;
|
||||
|
@ -88,14 +126,33 @@ pub async fn list_hosts(server: Url) -> Result<(), Box<dyn Error>> {
|
|||
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!(
|
||||
" 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!(
|
||||
" 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!(
|
||||
"Client Update Available: {}",
|
||||
host.metadata.update_available
|
||||
);
|
||||
println!(" Created: {}", host.created_at);
|
||||
println!();
|
||||
}
|
||||
|
@ -106,7 +163,10 @@ pub async fn list_hosts(server: Url) -> Result<(), Box<dyn Error>> {
|
|||
} else {
|
||||
let resp: APIErrorResponse = res.json().await?;
|
||||
|
||||
eprintln!("[error] Error listing hosts: {} {}", resp.errors[0].code, resp.errors[0].message);
|
||||
eprintln!(
|
||||
"[error] Error listing hosts: {} {}",
|
||||
resp.errors[0].code, resp.errors[0].message
|
||||
);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
@ -114,7 +174,6 @@ pub async fn list_hosts(server: Url) -> Result<(), Box<dyn Error>> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct HostCreateBody {
|
||||
pub name: String,
|
||||
|
@ -134,11 +193,9 @@ pub struct HostCreateBody {
|
|||
pub static_addresses: Vec<SocketAddrV4>,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct HostGetMetadata {}
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct HostGetResponse {
|
||||
pub data: Host,
|
||||
|
@ -146,7 +203,17 @@ pub struct HostGetResponse {
|
|||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
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>> {
|
||||
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);
|
||||
|
@ -172,16 +239,21 @@ pub async fn create_host(name: String, network_id: String, role_id: String, ip_a
|
|||
|
||||
let token = format!("{} {}", session_token, auth_token);
|
||||
|
||||
let res = client.post(server.join("/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?;
|
||||
let res = client
|
||||
.post(server.join("/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;
|
||||
|
@ -191,21 +263,42 @@ pub async fn create_host(name: String, network_id: String, role_id: String, ip_a
|
|||
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!(
|
||||
" 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!(
|
||||
" 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!(
|
||||
"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);
|
||||
eprintln!(
|
||||
"[error] Error creating host: {} {}",
|
||||
resp.errors[0].code, resp.errors[0].message
|
||||
);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
@ -224,7 +317,11 @@ pub async fn get_host(id: String, server: Url) -> Result<(), Box<dyn Error>> {
|
|||
|
||||
let token = format!("{} {}", session_token, auth_token);
|
||||
|
||||
let res = client.get(server.join(&format!("/v1/hosts/{}", id))?).bearer_auth(token).send().await?;
|
||||
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;
|
||||
|
@ -234,21 +331,42 @@ pub async fn get_host(id: String, server: Url) -> Result<(), Box<dyn Error>> {
|
|||
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!(
|
||||
" 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!(
|
||||
" 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!(
|
||||
"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);
|
||||
eprintln!(
|
||||
"[error] Error listing hosts: {} {}",
|
||||
resp.errors[0].code, resp.errors[0].message
|
||||
);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
@ -267,14 +385,21 @@ pub async fn delete_host(id: String, server: Url) -> Result<(), Box<dyn Error>>
|
|||
|
||||
let token = format!("{} {}", session_token, auth_token);
|
||||
|
||||
let res = client.delete(server.join(&format!("/v1/hosts/{}", id))?).bearer_auth(token).send().await?;
|
||||
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);
|
||||
eprintln!(
|
||||
"[error] Error removing host: {} {}",
|
||||
resp.errors[0].code, resp.errors[0].message
|
||||
);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
@ -290,10 +415,18 @@ pub struct HostUpdateBody {
|
|||
pub static_addresses: Vec<SocketAddrV4>,
|
||||
pub name: Option<String>,
|
||||
pub ip: Option<Ipv4Addr>,
|
||||
pub role: Option<String>
|
||||
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>> {
|
||||
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
|
||||
|
@ -304,13 +437,18 @@ pub async fn update_host(id: String, listen_port: Option<u16>, static_address: O
|
|||
|
||||
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::new, |u| vec![u]),
|
||||
name,
|
||||
ip,
|
||||
role
|
||||
}).bearer_auth(token).send().await?;
|
||||
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::new, |u| vec![u]),
|
||||
name,
|
||||
ip,
|
||||
role,
|
||||
})
|
||||
.bearer_auth(token)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if res.status().is_success() {
|
||||
let host: Host = res.json::<HostGetResponse>().await?.data;
|
||||
|
@ -320,21 +458,42 @@ pub async fn update_host(id: String, listen_port: Option<u16>, static_address: O
|
|||
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!(
|
||||
" 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!(
|
||||
" 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!(
|
||||
"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);
|
||||
eprintln!(
|
||||
"[error] Error updating host: {} {}",
|
||||
resp.errors[0].code, resp.errors[0].message
|
||||
);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
@ -342,7 +501,6 @@ pub async fn update_host(id: String, listen_port: Option<u16>, static_address: O
|
|||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct EnrollmentCodeResponseMetadata {}
|
||||
|
||||
|
@ -376,18 +534,29 @@ pub async fn enroll_host(id: String, server: Url) -> Result<(), Box<dyn Error>>
|
|||
|
||||
let token = format!("{} {}", session_token, auth_token);
|
||||
|
||||
let res = client.post(server.join(&format!("/v1/hosts/{}/enrollment-code", id))?).header("content-length", 0).bearer_auth(token).send().await?;
|
||||
let res = client
|
||||
.post(server.join(&format!("/v1/hosts/{}/enrollment-code", id))?)
|
||||
.header("content-length", 0)
|
||||
.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!(
|
||||
"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);
|
||||
eprintln!(
|
||||
"[error] Error blocking host: {} {}",
|
||||
resp.errors[0].code, resp.errors[0].message
|
||||
);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
@ -406,14 +575,22 @@ pub async fn block_host(id: String, server: Url) -> Result<(), Box<dyn Error>> {
|
|||
|
||||
let token = format!("{} {}", session_token, auth_token);
|
||||
|
||||
let res = client.post(server.join(&format!("/v1/hosts/{}/block", id))?).header("Content-Length", "0").bearer_auth(token).send().await?;
|
||||
let res = client
|
||||
.post(server.join(&format!("/v1/hosts/{}/block", id))?)
|
||||
.header("Content-Length", "0")
|
||||
.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);
|
||||
eprintln!(
|
||||
"[error] Error blocking host: {} {}",
|
||||
resp.errors[0].code, resp.errors[0].message
|
||||
);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
@ -423,18 +600,18 @@ pub async fn block_host(id: String, server: Url) -> Result<(), Box<dyn Error>> {
|
|||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct HostConfigOverrideResponse {
|
||||
pub data: HostConfigOverrideData
|
||||
pub data: HostConfigOverrideData,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct HostConfigOverrideData {
|
||||
pub overrides: Vec<HostConfigOverrideDataOverride>
|
||||
pub overrides: Vec<HostConfigOverrideDataOverride>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct HostConfigOverrideDataOverride {
|
||||
pub key: String,
|
||||
pub value: HostConfigOverrideDataOverrideValue
|
||||
pub value: HostConfigOverrideDataOverrideValue,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
|
@ -442,7 +619,7 @@ pub struct HostConfigOverrideDataOverride {
|
|||
pub enum HostConfigOverrideDataOverrideValue {
|
||||
Boolean(bool),
|
||||
Numeric(i64),
|
||||
Other(String)
|
||||
Other(String),
|
||||
}
|
||||
|
||||
pub async fn list_overrides(id: String, server: Url) -> Result<(), Box<dyn Error>> {
|
||||
|
@ -456,18 +633,25 @@ pub async fn list_overrides(id: String, server: Url) -> Result<(), Box<dyn Error
|
|||
|
||||
let token = format!("{} {}", session_token, auth_token);
|
||||
|
||||
let res = client.get(server.join(&format!("/v1/hosts/{}/config-overrides", id))?).bearer_auth(token).send().await?;
|
||||
let res = client
|
||||
.get(server.join(&format!("/v1/hosts/{}/config-overrides", id))?)
|
||||
.bearer_auth(token)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if res.status().is_success() {
|
||||
let resp: HostConfigOverrideResponse = res.json().await?;
|
||||
|
||||
for c_override in &resp.data.overrides {
|
||||
println!(" Key: {}", c_override.key);
|
||||
println!("Value: {}", match &c_override.value {
|
||||
HostConfigOverrideDataOverrideValue::Boolean(v) => format!("bool:{}", v),
|
||||
HostConfigOverrideDataOverrideValue::Numeric(v) => format!("numeric:{}", v),
|
||||
HostConfigOverrideDataOverrideValue::Other(v) => format!("string:{}", v)
|
||||
});
|
||||
println!(
|
||||
"Value: {}",
|
||||
match &c_override.value {
|
||||
HostConfigOverrideDataOverrideValue::Boolean(v) => format!("bool:{}", v),
|
||||
HostConfigOverrideDataOverrideValue::Numeric(v) => format!("numeric:{}", v),
|
||||
HostConfigOverrideDataOverrideValue::Other(v) => format!("string:{}", v),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if resp.data.overrides.is_empty() {
|
||||
|
@ -476,7 +660,10 @@ pub async fn list_overrides(id: String, server: Url) -> Result<(), Box<dyn Error
|
|||
} else {
|
||||
let resp: APIErrorResponse = res.json().await?;
|
||||
|
||||
eprintln!("[error] Error looking up config overrides: {} {}", resp.errors[0].code, resp.errors[0].message);
|
||||
eprintln!(
|
||||
"[error] Error looking up config overrides: {} {}",
|
||||
resp.errors[0].code, resp.errors[0].message
|
||||
);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
@ -486,14 +673,24 @@ pub async fn list_overrides(id: String, server: Url) -> Result<(), Box<dyn Error
|
|||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct SetOverrideRequest {
|
||||
pub overrides: Vec<HostConfigOverrideDataOverride>
|
||||
pub overrides: Vec<HostConfigOverrideDataOverride>,
|
||||
}
|
||||
|
||||
pub async fn set_override(id: String, key: String, boolean: Option<bool>, numeric: Option<i64>, other: Option<String>, server: Url) -> Result<(), Box<dyn Error>> {
|
||||
pub async fn set_override(
|
||||
id: String,
|
||||
key: String,
|
||||
boolean: Option<bool>,
|
||||
numeric: Option<i64>,
|
||||
other: Option<String>,
|
||||
server: Url,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
if boolean.is_none() && numeric.is_none() && other.is_none() {
|
||||
eprintln!("[error] no value provided: you must provide at least --boolean, --numeric, or --string");
|
||||
std::process::exit(1);
|
||||
} else if boolean.is_some() && numeric.is_some() || boolean.is_some() && other.is_some() || numeric.is_some() && other.is_some() {
|
||||
} else if boolean.is_some() && numeric.is_some()
|
||||
|| boolean.is_some() && other.is_some()
|
||||
|| numeric.is_some() && other.is_some()
|
||||
{
|
||||
eprintln!("[error] multiple values provided: you must provide only one of --boolean, --numeric, or --string");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
@ -520,7 +717,11 @@ pub async fn set_override(id: String, key: String, boolean: Option<bool>, numeri
|
|||
|
||||
let token = format!("{} {}", session_token, auth_token);
|
||||
|
||||
let res = client.get(server.join(&format!("/v1/hosts/{}/config-overrides", id))?).bearer_auth(token.clone()).send().await?;
|
||||
let res = client
|
||||
.get(server.join(&format!("/v1/hosts/{}/config-overrides", id))?)
|
||||
.bearer_auth(token.clone())
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if res.status().is_success() {
|
||||
let resp: HostConfigOverrideResponse = res.json().await?;
|
||||
|
@ -533,25 +734,28 @@ pub async fn set_override(id: String, key: String, boolean: Option<bool>, numeri
|
|||
}
|
||||
}
|
||||
|
||||
others.push(HostConfigOverrideDataOverride {
|
||||
key,
|
||||
value: val,
|
||||
});
|
||||
others.push(HostConfigOverrideDataOverride { key, value: val });
|
||||
|
||||
let res = client.put(server.join(&format!("/v1/hosts/{}/config-overrides", id))?).bearer_auth(token.clone()).json(&SetOverrideRequest {
|
||||
overrides: others,
|
||||
}).send().await?;
|
||||
let res = client
|
||||
.put(server.join(&format!("/v1/hosts/{}/config-overrides", id))?)
|
||||
.bearer_auth(token.clone())
|
||||
.json(&SetOverrideRequest { overrides: others })
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if res.status().is_success() {
|
||||
let resp: HostConfigOverrideResponse = res.json().await?;
|
||||
|
||||
for c_override in &resp.data.overrides {
|
||||
println!(" Key: {}", c_override.key);
|
||||
println!("Value: {}", match &c_override.value {
|
||||
HostConfigOverrideDataOverrideValue::Boolean(v) => format!("bool:{}", v),
|
||||
HostConfigOverrideDataOverrideValue::Numeric(v) => format!("numeric:{}", v),
|
||||
HostConfigOverrideDataOverrideValue::Other(v) => format!("string:{}", v)
|
||||
});
|
||||
println!(
|
||||
"Value: {}",
|
||||
match &c_override.value {
|
||||
HostConfigOverrideDataOverrideValue::Boolean(v) => format!("bool:{}", v),
|
||||
HostConfigOverrideDataOverrideValue::Numeric(v) => format!("numeric:{}", v),
|
||||
HostConfigOverrideDataOverrideValue::Other(v) => format!("string:{}", v),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if resp.data.overrides.is_empty() {
|
||||
|
@ -562,14 +766,20 @@ pub async fn set_override(id: String, key: String, boolean: Option<bool>, numeri
|
|||
} else {
|
||||
let resp: APIErrorResponse = res.json().await?;
|
||||
|
||||
eprintln!("[error] Error setting config overrides: {} {}", resp.errors[0].code, resp.errors[0].message);
|
||||
eprintln!(
|
||||
"[error] Error setting config overrides: {} {}",
|
||||
resp.errors[0].code, resp.errors[0].message
|
||||
);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
} else {
|
||||
let resp: APIErrorResponse = res.json().await?;
|
||||
|
||||
eprintln!("[error] Error setting config overrides: {} {}", resp.errors[0].code, resp.errors[0].message);
|
||||
eprintln!(
|
||||
"[error] Error setting config overrides: {} {}",
|
||||
resp.errors[0].code, resp.errors[0].message
|
||||
);
|
||||