integrated hetzner client in repo + showcase of using scope for 'cleaner' scripts
This commit is contained in:
parent
eed6dbf8dc
commit
9fdb8d8845
10
Cargo.toml
10
Cargo.toml
@ -26,6 +26,7 @@ members = [
|
|||||||
"packages/system/virt",
|
"packages/system/virt",
|
||||||
"rhai",
|
"rhai",
|
||||||
"herodo",
|
"herodo",
|
||||||
|
"packages/clients/hetznerclient",
|
||||||
]
|
]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
@ -47,7 +48,7 @@ log = "0.4"
|
|||||||
once_cell = "1.18.0"
|
once_cell = "1.18.0"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
regex = "1.8.1"
|
regex = "1.8.1"
|
||||||
reqwest = { version = "0.12.15", features = ["json"] }
|
reqwest = { version = "0.12.15", features = ["json", "blocking"] }
|
||||||
rhai = { version = "1.12.0", features = ["sync"] }
|
rhai = { version = "1.12.0", features = ["sync"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
@ -102,6 +103,7 @@ sal-git = { path = "packages/system/git" }
|
|||||||
sal-kubernetes = { path = "packages/system/kubernetes" }
|
sal-kubernetes = { path = "packages/system/kubernetes" }
|
||||||
sal-redisclient = { path = "packages/clients/redisclient" }
|
sal-redisclient = { path = "packages/clients/redisclient" }
|
||||||
sal-mycelium = { path = "packages/clients/myceliumclient" }
|
sal-mycelium = { path = "packages/clients/myceliumclient" }
|
||||||
|
sal-hetzner = { path = "packages/clients/hetznerclient" }
|
||||||
sal-text = { path = "packages/core/text" }
|
sal-text = { path = "packages/core/text" }
|
||||||
sal-os = { path = "packages/system/os" }
|
sal-os = { path = "packages/system/os" }
|
||||||
sal-net = { path = "packages/core/net" }
|
sal-net = { path = "packages/core/net" }
|
||||||
@ -122,6 +124,7 @@ sal-git = { workspace = true, optional = true }
|
|||||||
sal-kubernetes = { workspace = true, optional = true }
|
sal-kubernetes = { workspace = true, optional = true }
|
||||||
sal-redisclient = { workspace = true, optional = true }
|
sal-redisclient = { workspace = true, optional = true }
|
||||||
sal-mycelium = { workspace = true, optional = true }
|
sal-mycelium = { workspace = true, optional = true }
|
||||||
|
sal-hetzner = { workspace = true, optional = true }
|
||||||
sal-text = { workspace = true, optional = true }
|
sal-text = { workspace = true, optional = true }
|
||||||
sal-os = { workspace = true, optional = true }
|
sal-os = { workspace = true, optional = true }
|
||||||
sal-net = { workspace = true, optional = true }
|
sal-net = { workspace = true, optional = true }
|
||||||
@ -141,6 +144,7 @@ git = ["dep:sal-git"]
|
|||||||
kubernetes = ["dep:sal-kubernetes"]
|
kubernetes = ["dep:sal-kubernetes"]
|
||||||
redisclient = ["dep:sal-redisclient"]
|
redisclient = ["dep:sal-redisclient"]
|
||||||
mycelium = ["dep:sal-mycelium"]
|
mycelium = ["dep:sal-mycelium"]
|
||||||
|
hetzner = ["dep:sal-hetzner"]
|
||||||
text = ["dep:sal-text"]
|
text = ["dep:sal-text"]
|
||||||
os = ["dep:sal-os"]
|
os = ["dep:sal-os"]
|
||||||
net = ["dep:sal-net"]
|
net = ["dep:sal-net"]
|
||||||
@ -154,7 +158,7 @@ rhai = ["dep:sal-rhai"]
|
|||||||
|
|
||||||
# Convenience feature groups
|
# Convenience feature groups
|
||||||
core = ["os", "process", "text", "net"]
|
core = ["os", "process", "text", "net"]
|
||||||
clients = ["redisclient", "postgresclient", "zinit_client", "mycelium"]
|
clients = ["redisclient", "postgresclient", "zinit_client", "mycelium", "hetzner"]
|
||||||
infrastructure = ["git", "vault", "kubernetes", "virt"]
|
infrastructure = ["git", "vault", "kubernetes", "virt"]
|
||||||
scripting = ["rhai"]
|
scripting = ["rhai"]
|
||||||
all = [
|
all = [
|
||||||
@ -162,6 +166,7 @@ all = [
|
|||||||
"kubernetes",
|
"kubernetes",
|
||||||
"redisclient",
|
"redisclient",
|
||||||
"mycelium",
|
"mycelium",
|
||||||
|
"hetzner",
|
||||||
"text",
|
"text",
|
||||||
"os",
|
"os",
|
||||||
"net",
|
"net",
|
||||||
@ -188,4 +193,3 @@ required-features = ["kubernetes"]
|
|||||||
name = "generic_cluster"
|
name = "generic_cluster"
|
||||||
path = "examples/kubernetes/clusters/generic.rs"
|
path = "examples/kubernetes/clusters/generic.rs"
|
||||||
required-features = ["kubernetes"]
|
required-features = ["kubernetes"]
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
//! This library loads the Rhai engine, registers all SAL modules,
|
//! This library loads the Rhai engine, registers all SAL modules,
|
||||||
//! and executes Rhai scripts from a specified directory in sorted order.
|
//! and executes Rhai scripts from a specified directory in sorted order.
|
||||||
|
|
||||||
use rhai::Engine;
|
use rhai::{Engine, Scope};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
@ -29,6 +29,17 @@ pub fn run(script_path: &str) -> Result<(), Box<dyn Error>> {
|
|||||||
|
|
||||||
// Create a new Rhai engine
|
// Create a new Rhai engine
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
// TODO: if we create a scope here we could clean up all the different functionsand types regsitered wit the engine
|
||||||
|
// We should generalize the way we add things to the scope for each module sepeartely
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
// TODO: this should be done for the other clients as well (but not here of course, in each module)
|
||||||
|
let hetzner_client = sal::hetzner::api::Client::new(sal::hetzner::config::Config::from_env().unwrap());
|
||||||
|
scope.push("hetzner", hetzner_client);
|
||||||
|
// This makes it easy to call e.g. `hetzner.get_server()` or `mycelium.get_connected_peers()`
|
||||||
|
// --> without the need of manually created a client for each one first
|
||||||
|
// --> could be conditionally compiled to only use those who we need (we only push the things to the scope that we actually need to run the script)
|
||||||
|
|
||||||
|
|
||||||
// Register println function for output
|
// Register println function for output
|
||||||
engine.register_fn("println", |s: &str| println!("{}", s));
|
engine.register_fn("println", |s: &str| println!("{}", s));
|
||||||
@ -78,19 +89,20 @@ pub fn run(script_path: &str) -> Result<(), Box<dyn Error>> {
|
|||||||
let script = fs::read_to_string(&script_file)?;
|
let script = fs::read_to_string(&script_file)?;
|
||||||
|
|
||||||
// Execute the script
|
// Execute the script
|
||||||
match engine.eval::<rhai::Dynamic>(&script) {
|
// match engine.eval::<rhai::Dynamic>(&script) {
|
||||||
Ok(result) => {
|
// Ok(result) => {
|
||||||
println!("Script executed successfully");
|
// println!("Script executed successfully");
|
||||||
if !result.is_unit() {
|
// if !result.is_unit() {
|
||||||
println!("Result: {}", result);
|
// println!("Result: {}", result);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
Err(err) => {
|
// Err(err) => {
|
||||||
eprintln!("Error executing script: {}", err);
|
// eprintln!("Error executing script: {}", err);
|
||||||
// Exit with error code when a script fails
|
// // Exit with error code when a script fails
|
||||||
process::exit(1);
|
// process::exit(1);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
engine.run_with_scope(&mut scope, &script)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("\nAll scripts executed successfully!");
|
println!("\nAll scripts executed successfully!");
|
||||||
|
12
packages/clients/hetznerclient/Cargo.toml
Normal file
12
packages/clients/hetznerclient/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "sal-hetzner"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
prettytable = "0.10.0"
|
||||||
|
reqwest.workspace = true
|
||||||
|
rhai = { workspace = true, features = ["serde"] }
|
||||||
|
serde = { workspace = true, features = ["derive"] }
|
||||||
|
serde_json.workspace = true
|
||||||
|
thiserror.workspace = true
|
54
packages/clients/hetznerclient/src/api/error.rs
Normal file
54
packages/clients/hetznerclient/src/api/error.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum AppError {
|
||||||
|
#[error("Request failed: {0}")]
|
||||||
|
RequestError(#[from] reqwest::Error),
|
||||||
|
#[error("API error: {0}")]
|
||||||
|
ApiError(ApiError),
|
||||||
|
#[error("Deserialization Error: {0:?}")]
|
||||||
|
SerdeJsonError(#[from] serde_json::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct ApiError {
|
||||||
|
pub status: u16,
|
||||||
|
pub message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<reqwest::blocking::Response> for ApiError {
|
||||||
|
fn from(value: reqwest::blocking::Response) -> Self {
|
||||||
|
ApiError {
|
||||||
|
status: value.status().into(),
|
||||||
|
message: value.text().unwrap_or("The API call returned an error.".to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ApiError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct HetznerApiError {
|
||||||
|
code: String,
|
||||||
|
message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct HetznerApiErrorWrapper {
|
||||||
|
error: HetznerApiError,
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(wrapper) = serde_json::from_str::<HetznerApiErrorWrapper>(&self.message) {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Status: {}, Code: {}, Message: {}",
|
||||||
|
self.status, wrapper.error.code, wrapper.error.message
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
write!(f, "Status: {}: {}", self.status, self.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
513
packages/clients/hetznerclient/src/api/mod.rs
Normal file
513
packages/clients/hetznerclient/src/api/mod.rs
Normal file
@ -0,0 +1,513 @@
|
|||||||
|
pub mod error;
|
||||||
|
pub mod models;
|
||||||
|
|
||||||
|
use self::models::{
|
||||||
|
Boot, Rescue, Server, SshKey, ServerAddonProduct, ServerAddonProductWrapper,
|
||||||
|
AuctionServerProduct, AuctionServerProductWrapper, AuctionTransaction,
|
||||||
|
AuctionTransactionWrapper, BootWrapper, Cancellation, CancellationWrapper,
|
||||||
|
OrderServerBuilder, OrderServerProduct, OrderServerProductWrapper, RescueWrapped,
|
||||||
|
ServerWrapper, SshKeyWrapper, Transaction, TransactionWrapper,
|
||||||
|
ServerAddonTransaction, ServerAddonTransactionWrapper,
|
||||||
|
OrderServerAddonBuilder,
|
||||||
|
};
|
||||||
|
use crate::api::error::ApiError;
|
||||||
|
use crate::config::Config;
|
||||||
|
use error::AppError;
|
||||||
|
use reqwest::blocking::Client as HttpClient;
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Client {
|
||||||
|
http_client: HttpClient,
|
||||||
|
config: Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
pub fn new(config: Config) -> Self {
|
||||||
|
Self {
|
||||||
|
http_client: HttpClient::new(),
|
||||||
|
config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_response<T>(&self, response: reqwest::blocking::Response) -> Result<T, AppError>
|
||||||
|
where
|
||||||
|
T: serde::de::DeserializeOwned,
|
||||||
|
{
|
||||||
|
let status = response.status();
|
||||||
|
let body = response.text()?;
|
||||||
|
|
||||||
|
if status.is_success() {
|
||||||
|
serde_json::from_str::<T>(&body).map_err(Into::into)
|
||||||
|
} else {
|
||||||
|
Err(AppError::ApiError(ApiError {
|
||||||
|
status: status.as_u16(),
|
||||||
|
message: body,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_server(&self, server_number: i32) -> Result<Server, AppError> {
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.get(format!("{}/server/{}", self.config.api_url, server_number))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: ServerWrapper = self.handle_response(response)?;
|
||||||
|
Ok(wrapped.server)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_servers(&self) -> Result<Vec<Server>, AppError> {
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.get(format!("{}/server", self.config.api_url))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: Vec<ServerWrapper> = self.handle_response(response)?;
|
||||||
|
let servers = wrapped.into_iter().map(|sw| sw.server).collect();
|
||||||
|
Ok(servers)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_server_name(&self, server_number: i32, name: &str) -> Result<Server, AppError> {
|
||||||
|
let params = [("server_name", name)];
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.post(format!("{}/server/{}", self.config.api_url, server_number))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.form(¶ms)
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: ServerWrapper = self.handle_response(response)?;
|
||||||
|
Ok(wrapped.server)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_cancellation_data(&self, server_number: i32) -> Result<Cancellation, AppError> {
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.get(format!(
|
||||||
|
"{}/server/{}/cancellation",
|
||||||
|
self.config.api_url, server_number
|
||||||
|
))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: CancellationWrapper = self.handle_response(response)?;
|
||||||
|
Ok(wrapped.cancellation)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cancel_server(
|
||||||
|
&self,
|
||||||
|
server_number: i32,
|
||||||
|
cancellation_date: &str,
|
||||||
|
) -> Result<Cancellation, AppError> {
|
||||||
|
let params = [("cancellation_date", cancellation_date)];
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.post(format!(
|
||||||
|
"{}/server/{}/cancellation",
|
||||||
|
self.config.api_url, server_number
|
||||||
|
))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.form(¶ms)
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: CancellationWrapper = self.handle_response(response)?;
|
||||||
|
Ok(wrapped.cancellation)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn withdraw_cancellation(&self, server_number: i32) -> Result<(), AppError> {
|
||||||
|
self.http_client
|
||||||
|
.delete(format!(
|
||||||
|
"{}/server/{}/cancellation",
|
||||||
|
self.config.api_url, server_number
|
||||||
|
))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_ssh_keys(&self) -> Result<Vec<SshKey>, AppError> {
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.get(format!("{}/key", self.config.api_url))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: Vec<SshKeyWrapper> = self.handle_response(response)?;
|
||||||
|
let keys = wrapped.into_iter().map(|sk| sk.key).collect();
|
||||||
|
Ok(keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_ssh_key(&self, fingerprint: &str) -> Result<SshKey, AppError> {
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.get(format!("{}/key/{}", self.config.api_url, fingerprint))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: SshKeyWrapper = self.handle_response(response)?;
|
||||||
|
Ok(wrapped.key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_ssh_key(&self, name: &str, data: &str) -> Result<SshKey, AppError> {
|
||||||
|
let params = [("name", name), ("data", data)];
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.post(format!("{}/key", self.config.api_url))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.form(¶ms)
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: SshKeyWrapper = self.handle_response(response)?;
|
||||||
|
Ok(wrapped.key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_ssh_key_name(&self, fingerprint: &str, name: &str) -> Result<SshKey, AppError> {
|
||||||
|
let params = [("name", name)];
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.post(format!("{}/key/{}", self.config.api_url, fingerprint))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.form(¶ms)
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: SshKeyWrapper = self.handle_response(response)?;
|
||||||
|
Ok(wrapped.key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_ssh_key(&self, fingerprint: &str) -> Result<(), AppError> {
|
||||||
|
self.http_client
|
||||||
|
.delete(format!("{}/key/{}", self.config.api_url, fingerprint))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn get_boot_configuration(&self, server_number: i32) -> Result<Boot, AppError> {
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.get(format!("{}/boot/{}", self.config.api_url, server_number))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: BootWrapper = self.handle_response(response)?;
|
||||||
|
Ok(wrapped.boot)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_rescue_boot_configuration(&self, server_number: i32) -> Result<Rescue, AppError> {
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.get(format!(
|
||||||
|
"{}/boot/{}/rescue",
|
||||||
|
self.config.api_url, server_number
|
||||||
|
))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: RescueWrapped = self.handle_response(response)?;
|
||||||
|
Ok(wrapped.rescue)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enable_rescue_mode(
|
||||||
|
&self,
|
||||||
|
server_number: i32,
|
||||||
|
os: &str,
|
||||||
|
authorized_keys: Option<&[String]>,
|
||||||
|
) -> Result<Rescue, AppError> {
|
||||||
|
let mut params = vec![("os", os)];
|
||||||
|
if let Some(keys) = authorized_keys {
|
||||||
|
for key in keys {
|
||||||
|
params.push(("authorized_key[]", key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.post(format!(
|
||||||
|
"{}/boot/{}/rescue",
|
||||||
|
self.config.api_url, server_number
|
||||||
|
))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.form(¶ms)
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: RescueWrapped = self.handle_response(response)?;
|
||||||
|
Ok(wrapped.rescue)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable_rescue_mode(&self, server_number: i32) -> Result<Rescue, AppError> {
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.delete(format!(
|
||||||
|
"{}/boot/{}/rescue",
|
||||||
|
self.config.api_url, server_number
|
||||||
|
))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: RescueWrapped = self.handle_response(response)?;
|
||||||
|
Ok(wrapped.rescue)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_server_products(
|
||||||
|
&self,
|
||||||
|
) -> Result<Vec<OrderServerProduct>, AppError> {
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.get(format!("{}/order/server/product", &self.config.api_url))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: Vec<OrderServerProductWrapper> = self.handle_response(response)?;
|
||||||
|
let products = wrapped.into_iter().map(|sop| sop.product).collect();
|
||||||
|
Ok(products)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_server_product_by_id(
|
||||||
|
&self,
|
||||||
|
product_id: &str,
|
||||||
|
) -> Result<OrderServerProduct, AppError> {
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.get(format!(
|
||||||
|
"{}/order/server/product/{}",
|
||||||
|
&self.config.api_url, product_id
|
||||||
|
))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: OrderServerProductWrapper = self.handle_response(response)?;
|
||||||
|
Ok(wrapped.product)
|
||||||
|
}
|
||||||
|
pub fn order_server(&self, order: OrderServerBuilder) -> Result<Transaction, AppError> {
|
||||||
|
let mut params = json!({
|
||||||
|
"product_id": order.product_id,
|
||||||
|
"dist": order.dist,
|
||||||
|
"location": order.location,
|
||||||
|
"authorized_key": order.authorized_keys.unwrap_or_default(),
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(addons) = order.addons {
|
||||||
|
params["addon"] = json!(addons);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(test) = order.test {
|
||||||
|
if test {
|
||||||
|
params["test"] = json!(test);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.post(format!("{}/order/server/transaction", &self.config.api_url))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.json(¶ms)
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: TransactionWrapper = self.handle_response(response)?;
|
||||||
|
Ok(wrapped.transaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_transaction_by_id(&self, transaction_id: &str) -> Result<Transaction, AppError> {
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.get(format!(
|
||||||
|
"{}/order/server/transaction/{}",
|
||||||
|
&self.config.api_url, transaction_id
|
||||||
|
))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: TransactionWrapper = self.handle_response(response)?;
|
||||||
|
Ok(wrapped.transaction)
|
||||||
|
}
|
||||||
|
pub fn get_transactions(&self) -> Result<Vec<Transaction>, AppError> {
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.get(format!("{}/order/server/transaction", &self.config.api_url))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: Vec<TransactionWrapper> = self.handle_response(response)?;
|
||||||
|
let transactions = wrapped.into_iter().map(|t| t.transaction).collect();
|
||||||
|
Ok(transactions)
|
||||||
|
}
|
||||||
|
pub fn get_auction_server_products(&self) -> Result<Vec<AuctionServerProduct>, AppError> {
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.get(format!(
|
||||||
|
"{}/order/server_market/product",
|
||||||
|
&self.config.api_url
|
||||||
|
))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: Vec<AuctionServerProductWrapper> = self.handle_response(response)?;
|
||||||
|
let products = wrapped.into_iter().map(|asp| asp.product).collect();
|
||||||
|
Ok(products)
|
||||||
|
}
|
||||||
|
pub fn get_auction_server_product_by_id(&self, product_id: &str) -> Result<AuctionServerProduct, AppError> {
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.get(format!("{}/order/server_market/product/{}", &self.config.api_url, product_id))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: AuctionServerProductWrapper = self.handle_response(response)?;
|
||||||
|
Ok(wrapped.product)
|
||||||
|
}
|
||||||
|
pub fn get_auction_transactions(&self) -> Result<Vec<AuctionTransaction>, AppError> {
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.get(format!("{}/order/server_market/transaction", &self.config.api_url))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: Vec<AuctionTransactionWrapper> = self.handle_response(response)?;
|
||||||
|
let transactions = wrapped.into_iter().map(|t| t.transaction).collect();
|
||||||
|
Ok(transactions)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_auction_transaction_by_id(&self, transaction_id: &str) -> Result<AuctionTransaction, AppError> {
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.get(format!("{}/order/server_market/transaction/{}", &self.config.api_url, transaction_id))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: AuctionTransactionWrapper = self.handle_response(response)?;
|
||||||
|
Ok(wrapped.transaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_server_addon_products(
|
||||||
|
&self,
|
||||||
|
server_number: i64,
|
||||||
|
) -> Result<Vec<ServerAddonProduct>, AppError> {
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.get(format!(
|
||||||
|
"{}/order/server_addon/{}/product",
|
||||||
|
&self.config.api_url, server_number
|
||||||
|
))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: Vec<ServerAddonProductWrapper> = self.handle_response(response)?;
|
||||||
|
let products = wrapped.into_iter().map(|sap| sap.product).collect();
|
||||||
|
Ok(products)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn order_auction_server(
|
||||||
|
&self,
|
||||||
|
product_id: i64,
|
||||||
|
authorized_keys: Vec<String>,
|
||||||
|
dist: Option<String>,
|
||||||
|
arch: Option<String>,
|
||||||
|
lang: Option<String>,
|
||||||
|
comment: Option<String>,
|
||||||
|
addons: Option<Vec<String>>,
|
||||||
|
test: Option<bool>,
|
||||||
|
) -> Result<AuctionTransaction, AppError> {
|
||||||
|
let mut params: Vec<(&str, String)> = Vec::new();
|
||||||
|
|
||||||
|
params.push(("product_id", product_id.to_string()));
|
||||||
|
|
||||||
|
for key in &authorized_keys {
|
||||||
|
params.push(("authorized_key[]", key.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(dist) = dist {
|
||||||
|
params.push(("dist", dist));
|
||||||
|
}
|
||||||
|
if let Some(arch) = arch {
|
||||||
|
params.push(("@deprecated arch", arch));
|
||||||
|
}
|
||||||
|
if let Some(lang) = lang {
|
||||||
|
params.push(("lang", lang));
|
||||||
|
}
|
||||||
|
if let Some(comment) = comment {
|
||||||
|
params.push(("comment", comment));
|
||||||
|
}
|
||||||
|
if let Some(addons) = addons {
|
||||||
|
for addon in addons {
|
||||||
|
params.push(("addon[]", addon));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(test) = test {
|
||||||
|
params.push(("test", test.to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.post(format!("{}/order/server_market/transaction", &self.config.api_url))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.form(¶ms)
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: AuctionTransactionWrapper = self.handle_response(response)?;
|
||||||
|
Ok(wrapped.transaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_server_addon_transactions(&self) -> Result<Vec<ServerAddonTransaction>, AppError> {
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.get(format!("{}/order/server_addon/transaction", &self.config.api_url))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: Vec<ServerAddonTransactionWrapper> = self.handle_response(response)?;
|
||||||
|
let transactions = wrapped.into_iter().map(|satw| satw.transaction).collect();
|
||||||
|
Ok(transactions)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_server_addon_transaction_by_id(
|
||||||
|
&self,
|
||||||
|
transaction_id: &str,
|
||||||
|
) -> Result<ServerAddonTransaction, AppError> {
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.get(format!(
|
||||||
|
"{}/order/server_addon/transaction/{}",
|
||||||
|
&self.config.api_url, transaction_id
|
||||||
|
))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: ServerAddonTransactionWrapper = self.handle_response(response)?;
|
||||||
|
Ok(wrapped.transaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn order_server_addon(
|
||||||
|
&self,
|
||||||
|
order: OrderServerAddonBuilder,
|
||||||
|
) -> Result<ServerAddonTransaction, AppError> {
|
||||||
|
let mut params = json!({
|
||||||
|
"server_number": order.server_number,
|
||||||
|
"product_id": order.product_id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(reason) = order.reason {
|
||||||
|
params["reason"] = json!(reason);
|
||||||
|
}
|
||||||
|
if let Some(gateway) = order.gateway {
|
||||||
|
params["gateway"] = json!(gateway);
|
||||||
|
}
|
||||||
|
if let Some(test) = order.test {
|
||||||
|
if test {
|
||||||
|
params["test"] = json!(test);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.post(format!("{}/order/server_addon/transaction", &self.config.api_url))
|
||||||
|
.basic_auth(&self.config.username, Some(&self.config.password))
|
||||||
|
.form(¶ms)
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let wrapped: ServerAddonTransactionWrapper = self.handle_response(response)?;
|
||||||
|
Ok(wrapped.transaction)
|
||||||
|
}
|
||||||
|
}
|
1894
packages/clients/hetznerclient/src/api/models.rs
Normal file
1894
packages/clients/hetznerclient/src/api/models.rs
Normal file
File diff suppressed because it is too large
Load Diff
25
packages/clients/hetznerclient/src/config.rs
Normal file
25
packages/clients/hetznerclient/src/config.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
use std::env;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Config {
|
||||||
|
pub username: String,
|
||||||
|
pub password: String,
|
||||||
|
pub api_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn from_env() -> Result<Self, String> {
|
||||||
|
let username = env::var("HETZNER_USERNAME")
|
||||||
|
.map_err(|_| "HETZNER_USERNAME environment variable not set".to_string())?;
|
||||||
|
let password = env::var("HETZNER_PASSWORD")
|
||||||
|
.map_err(|_| "HETZNER_PASSWORD environment variable not set".to_string())?;
|
||||||
|
let api_url = env::var("HETZNER_API_URL")
|
||||||
|
.unwrap_or_else(|_| "https://robot-ws.your-server.de".to_string());
|
||||||
|
|
||||||
|
Ok(Config {
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
api_url,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
3
packages/clients/hetznerclient/src/lib.rs
Normal file
3
packages/clients/hetznerclient/src/lib.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub mod api;
|
||||||
|
pub mod config;
|
||||||
|
pub mod rhai;
|
63
packages/clients/hetznerclient/src/rhai/boot.rs
Normal file
63
packages/clients/hetznerclient/src/rhai/boot.rs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
use crate::api::{
|
||||||
|
models::{Boot, Rescue},
|
||||||
|
Client,
|
||||||
|
};
|
||||||
|
use rhai::{plugin::*, Engine};
|
||||||
|
|
||||||
|
pub fn register(engine: &mut Engine) {
|
||||||
|
let boot_module = exported_module!(boot_api);
|
||||||
|
engine.register_global_module(boot_module.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export_module]
|
||||||
|
pub mod boot_api {
|
||||||
|
use super::*;
|
||||||
|
use rhai::EvalAltResult;
|
||||||
|
|
||||||
|
#[rhai_fn(name = "get_boot_configuration", return_raw)]
|
||||||
|
pub fn get_boot_configuration(
|
||||||
|
client: &mut Client,
|
||||||
|
server_number: i64,
|
||||||
|
) -> Result<Boot, Box<EvalAltResult>> {
|
||||||
|
client
|
||||||
|
.get_boot_configuration(server_number as i32)
|
||||||
|
.map_err(|e| e.to_string().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "get_rescue_boot_configuration", return_raw)]
|
||||||
|
pub fn get_rescue_boot_configuration(
|
||||||
|
client: &mut Client,
|
||||||
|
server_number: i64,
|
||||||
|
) -> Result<Rescue, Box<EvalAltResult>> {
|
||||||
|
client
|
||||||
|
.get_rescue_boot_configuration(server_number as i32)
|
||||||
|
.map_err(|e| e.to_string().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "enable_rescue_mode", return_raw)]
|
||||||
|
pub fn enable_rescue_mode(
|
||||||
|
client: &mut Client,
|
||||||
|
server_number: i64,
|
||||||
|
os: &str,
|
||||||
|
authorized_keys: rhai::Array,
|
||||||
|
) -> Result<Rescue, Box<EvalAltResult>> {
|
||||||
|
let keys: Vec<String> = authorized_keys
|
||||||
|
.into_iter()
|
||||||
|
.map(|k| k.into_string().unwrap())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
client
|
||||||
|
.enable_rescue_mode(server_number as i32, os, Some(&keys))
|
||||||
|
.map_err(|e| e.to_string().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "disable_rescue_mode", return_raw)]
|
||||||
|
pub fn disable_rescue_mode(
|
||||||
|
client: &mut Client,
|
||||||
|
server_number: i64,
|
||||||
|
) -> Result<Rescue, Box<EvalAltResult>> {
|
||||||
|
client
|
||||||
|
.disable_rescue_mode(server_number as i32)
|
||||||
|
.map_err(|e| e.to_string().into())
|
||||||
|
}
|
||||||
|
}
|
54
packages/clients/hetznerclient/src/rhai/mod.rs
Normal file
54
packages/clients/hetznerclient/src/rhai/mod.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
|
use crate::api::models::{
|
||||||
|
AuctionServerProduct, AuctionTransaction, AuctionTransactionProduct, AuthorizedKey, Boot,
|
||||||
|
Cancellation, Cpanel, HostKey, Linux, OrderAuctionServerBuilder, OrderServerAddonBuilder,
|
||||||
|
OrderServerBuilder, OrderServerProduct, Plesk, Rescue, Server, ServerAddonProduct,
|
||||||
|
ServerAddonResource, ServerAddonTransaction, SshKey, Transaction, TransactionProduct, Vnc,
|
||||||
|
Windows,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub mod boot;
|
||||||
|
pub mod printing;
|
||||||
|
pub mod server;
|
||||||
|
pub mod server_ordering;
|
||||||
|
pub mod ssh_keys;
|
||||||
|
|
||||||
|
// here just register the hetzner module
|
||||||
|
pub fn register_hetzner_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
|
||||||
|
// TODO:register types
|
||||||
|
engine.build_type::<Server>();
|
||||||
|
engine.build_type::<SshKey>();
|
||||||
|
engine.build_type::<Boot>();
|
||||||
|
engine.build_type::<Rescue>();
|
||||||
|
engine.build_type::<Linux>();
|
||||||
|
engine.build_type::<Vnc>();
|
||||||
|
engine.build_type::<Windows>();
|
||||||
|
engine.build_type::<Plesk>();
|
||||||
|
engine.build_type::<Cpanel>();
|
||||||
|
engine.build_type::<Cancellation>();
|
||||||
|
engine.build_type::<OrderServerProduct>();
|
||||||
|
engine.build_type::<Transaction>();
|
||||||
|
engine.build_type::<AuthorizedKey>();
|
||||||
|
engine.build_type::<TransactionProduct>();
|
||||||
|
engine.build_type::<HostKey>();
|
||||||
|
engine.build_type::<AuctionServerProduct>();
|
||||||
|
engine.build_type::<AuctionTransaction>();
|
||||||
|
engine.build_type::<AuctionTransactionProduct>();
|
||||||
|
engine.build_type::<OrderAuctionServerBuilder>();
|
||||||
|
engine.build_type::<OrderServerBuilder>();
|
||||||
|
engine.build_type::<ServerAddonProduct>();
|
||||||
|
engine.build_type::<ServerAddonTransaction>();
|
||||||
|
engine.build_type::<ServerAddonResource>();
|
||||||
|
engine.build_type::<OrderServerAddonBuilder>();
|
||||||
|
|
||||||
|
server::register(engine);
|
||||||
|
ssh_keys::register(engine);
|
||||||
|
boot::register(engine);
|
||||||
|
server_ordering::register(engine);
|
||||||
|
|
||||||
|
// TODO: push hetzner to scope as value client:
|
||||||
|
// scope.push("hetzner", client);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
43
packages/clients/hetznerclient/src/rhai/printing/mod.rs
Normal file
43
packages/clients/hetznerclient/src/rhai/printing/mod.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
use rhai::{Array, Engine};
|
||||||
|
use crate::{api::models::{OrderServerProduct, AuctionServerProduct, AuctionTransaction, ServerAddonProduct, ServerAddonTransaction, Server, SshKey}};
|
||||||
|
|
||||||
|
mod servers_table;
|
||||||
|
mod ssh_keys_table;
|
||||||
|
mod server_ordering_table;
|
||||||
|
|
||||||
|
// This will be called when we print(...) or pretty_print() an Array (with Dynamic values)
|
||||||
|
pub fn pretty_print_dispatch(array: Array) {
|
||||||
|
if array.is_empty() {
|
||||||
|
println!("<empty table>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let first = &array[0];
|
||||||
|
|
||||||
|
if first.is::<Server>() {
|
||||||
|
println!("Yeah first is server!");
|
||||||
|
servers_table::pretty_print_servers(array);
|
||||||
|
} else if first.is::<SshKey>() {
|
||||||
|
ssh_keys_table::pretty_print_ssh_keys(array);
|
||||||
|
}
|
||||||
|
else if first.is::<OrderServerProduct>() {
|
||||||
|
server_ordering_table::pretty_print_server_products(array);
|
||||||
|
} else if first.is::<AuctionServerProduct>() {
|
||||||
|
server_ordering_table::pretty_print_auction_server_products(array);
|
||||||
|
} else if first.is::<AuctionTransaction>() {
|
||||||
|
server_ordering_table::pretty_print_auction_transactions(array);
|
||||||
|
} else if first.is::<ServerAddonProduct>() {
|
||||||
|
server_ordering_table::pretty_print_server_addon_products(array);
|
||||||
|
} else if first.is::<ServerAddonTransaction>() {
|
||||||
|
server_ordering_table::pretty_print_server_addon_transactions(array);
|
||||||
|
} else {
|
||||||
|
// Generic fallback for other types
|
||||||
|
for item in array {
|
||||||
|
println!("{}", item.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register(engine: &mut Engine) {
|
||||||
|
engine.register_fn("pretty_print", pretty_print_dispatch);
|
||||||
|
}
|
@ -0,0 +1,293 @@
|
|||||||
|
use prettytable::{row, Table};
|
||||||
|
use crate::api::models::{OrderServerProduct, ServerAddonProduct, ServerAddonTransaction, ServerAddonResource};
|
||||||
|
|
||||||
|
pub fn pretty_print_server_products(products: rhai::Array) {
|
||||||
|
let mut table = Table::new();
|
||||||
|
table.add_row(row![b =>
|
||||||
|
"ID",
|
||||||
|
"Name",
|
||||||
|
"Description",
|
||||||
|
"Traffic",
|
||||||
|
"Location",
|
||||||
|
"Price (Net)",
|
||||||
|
"Price (Gross)",
|
||||||
|
]);
|
||||||
|
|
||||||
|
for product_dyn in products {
|
||||||
|
if let Some(product) = product_dyn.try_cast::<OrderServerProduct>() {
|
||||||
|
let mut price_net = "N/A".to_string();
|
||||||
|
let mut price_gross = "N/A".to_string();
|
||||||
|
|
||||||
|
if let Some(first_price) = product.prices.first() {
|
||||||
|
price_net = first_price.price.net.clone();
|
||||||
|
price_gross = first_price.price.gross.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
table.add_row(row![
|
||||||
|
product.id,
|
||||||
|
product.name,
|
||||||
|
product.description.join(", "),
|
||||||
|
product.traffic,
|
||||||
|
product.location.join(", "),
|
||||||
|
price_net,
|
||||||
|
price_gross,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table.printstd();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pretty_print_auction_server_products(products: rhai::Array) {
|
||||||
|
let mut table = Table::new();
|
||||||
|
table.add_row(row![b =>
|
||||||
|
"ID",
|
||||||
|
"Name",
|
||||||
|
"Description",
|
||||||
|
"Traffic",
|
||||||
|
"Distributions",
|
||||||
|
"Architectures",
|
||||||
|
"Languages",
|
||||||
|
"CPU",
|
||||||
|
"CPU Benchmark",
|
||||||
|
"Memory Size (GB)",
|
||||||
|
"HDD Size (GB)",
|
||||||
|
"HDD Text",
|
||||||
|
"HDD Count",
|
||||||
|
"Datacenter",
|
||||||
|
"Network Speed",
|
||||||
|
"Price (Net)",
|
||||||
|
"Price (Hourly Net)",
|
||||||
|
"Price (Setup Net)",
|
||||||
|
"Price (VAT)",
|
||||||
|
"Price (Hourly VAT)",
|
||||||
|
"Price (Setup VAT)",
|
||||||
|
"Fixed Price",
|
||||||
|
"Next Reduce (seconds)",
|
||||||
|
"Next Reduce Date",
|
||||||
|
"Orderable Addons",
|
||||||
|
]);
|
||||||
|
|
||||||
|
for product_dyn in products {
|
||||||
|
if let Some(product) = product_dyn.try_cast::<crate::api::models::AuctionServerProduct>() {
|
||||||
|
let mut addons_table = Table::new();
|
||||||
|
addons_table.add_row(row![b => "ID", "Name", "Min", "Max", "Prices"]);
|
||||||
|
for addon in &product.orderable_addons {
|
||||||
|
let mut addon_prices_table = Table::new();
|
||||||
|
addon_prices_table.add_row(row![b => "Location", "Net", "Gross", "Hourly Net", "Hourly Gross", "Setup Net", "Setup Gross"]);
|
||||||
|
for price in &addon.prices {
|
||||||
|
addon_prices_table.add_row(row![
|
||||||
|
price.location,
|
||||||
|
price.price.net,
|
||||||
|
price.price.gross,
|
||||||
|
price.price.hourly_net,
|
||||||
|
price.price.hourly_gross,
|
||||||
|
price.price_setup.net,
|
||||||
|
price.price_setup.gross
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
addons_table.add_row(row![
|
||||||
|
addon.id,
|
||||||
|
addon.name,
|
||||||
|
addon.min,
|
||||||
|
addon.max,
|
||||||
|
addon_prices_table
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
table.add_row(row![
|
||||||
|
product.id,
|
||||||
|
product.name,
|
||||||
|
product.description.join(", "),
|
||||||
|
product.traffic,
|
||||||
|
product.dist.join(", "),
|
||||||
|
product.arch.as_deref().unwrap_or_default().join(", "),
|
||||||
|
product.lang.join(", "),
|
||||||
|
product.cpu,
|
||||||
|
product.cpu_benchmark,
|
||||||
|
product.memory_size,
|
||||||
|
product.hdd_size,
|
||||||
|
product.hdd_text,
|
||||||
|
product.hdd_count,
|
||||||
|
product.datacenter,
|
||||||
|
product.network_speed,
|
||||||
|
product.price,
|
||||||
|
product.price_hourly.as_deref().unwrap_or("N/A"),
|
||||||
|
product.price_setup,
|
||||||
|
product.price_with_vat,
|
||||||
|
product.price_hourly_with_vat.as_deref().unwrap_or("N/A"),
|
||||||
|
product.price_setup_with_vat,
|
||||||
|
product.fixed_price,
|
||||||
|
product.next_reduce,
|
||||||
|
product.next_reduce_date,
|
||||||
|
addons_table,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table.printstd();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pretty_print_server_addon_products(products: rhai::Array) {
|
||||||
|
let mut table = Table::new();
|
||||||
|
table.add_row(row![b =>
|
||||||
|
"ID",
|
||||||
|
"Name",
|
||||||
|
"Type",
|
||||||
|
"Location",
|
||||||
|
"Price (Net)",
|
||||||
|
"Price (Gross)",
|
||||||
|
"Hourly Net",
|
||||||
|
"Hourly Gross",
|
||||||
|
"Setup Net",
|
||||||
|
"Setup Gross",
|
||||||
|
]);
|
||||||
|
|
||||||
|
for product_dyn in products {
|
||||||
|
if let Some(product) = product_dyn.try_cast::<ServerAddonProduct>() {
|
||||||
|
table.add_row(row![
|
||||||
|
product.id,
|
||||||
|
product.name,
|
||||||
|
product.product_type,
|
||||||
|
product.price.location,
|
||||||
|
product.price.price.net,
|
||||||
|
product.price.price.gross,
|
||||||
|
product.price.price.hourly_net,
|
||||||
|
product.price.price.hourly_gross,
|
||||||
|
product.price.price_setup.net,
|
||||||
|
product.price.price_setup.gross,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table.printstd();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pretty_print_auction_transactions(transactions: rhai::Array) {
|
||||||
|
let mut table = Table::new();
|
||||||
|
table.add_row(row![b =>
|
||||||
|
"ID",
|
||||||
|
"Date",
|
||||||
|
"Status",
|
||||||
|
"Server Number",
|
||||||
|
"Server IP",
|
||||||
|
"Comment",
|
||||||
|
"Product ID",
|
||||||
|
"Product Name",
|
||||||
|
"Product Traffic",
|
||||||
|
"Product Distributions",
|
||||||
|
"Product Architectures",
|
||||||
|
"Product Languages",
|
||||||
|
"Product CPU",
|
||||||
|
"Product CPU Benchmark",
|
||||||
|
"Product Memory Size (GB)",
|
||||||
|
"Product HDD Size (GB)",
|
||||||
|
"Product HDD Text",
|
||||||
|
"Product HDD Count",
|
||||||
|
"Product Datacenter",
|
||||||
|
"Product Network Speed",
|
||||||
|
"Product Fixed Price",
|
||||||
|
"Product Next Reduce (seconds)",
|
||||||
|
"Product Next Reduce Date",
|
||||||
|
"Addons",
|
||||||
|
]);
|
||||||
|
|
||||||
|
for transaction_dyn in transactions {
|
||||||
|
if let Some(transaction) = transaction_dyn.try_cast::<crate::api::models::AuctionTransaction>() {
|
||||||
|
let _authorized_keys_table = {
|
||||||
|
let mut table = Table::new();
|
||||||
|
table.add_row(row![b => "Name", "Fingerprint", "Type", "Size"]);
|
||||||
|
for key in &transaction.authorized_key {
|
||||||
|
table.add_row(row![
|
||||||
|
key.key.name.as_deref().unwrap_or("N/A"),
|
||||||
|
key.key.fingerprint.as_deref().unwrap_or("N/A"),
|
||||||
|
key.key.key_type.as_deref().unwrap_or("N/A"),
|
||||||
|
key.key.size.map_or("N/A".to_string(), |s| s.to_string())
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
table
|
||||||
|
};
|
||||||
|
|
||||||
|
let _host_keys_table = {
|
||||||
|
let mut table = Table::new();
|
||||||
|
table.add_row(row![b => "Fingerprint", "Type", "Size"]);
|
||||||
|
for key in &transaction.host_key {
|
||||||
|
table.add_row(row![
|
||||||
|
key.key.fingerprint.as_deref().unwrap_or("N/A"),
|
||||||
|
key.key.key_type.as_deref().unwrap_or("N/A"),
|
||||||
|
key.key.size.map_or("N/A".to_string(), |s| s.to_string())
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
table
|
||||||
|
};
|
||||||
|
|
||||||
|
table.add_row(row![
|
||||||
|
transaction.id,
|
||||||
|
transaction.date,
|
||||||
|
transaction.status,
|
||||||
|
transaction.server_number.map_or("N/A".to_string(), |id| id.to_string()),
|
||||||
|
transaction.server_ip.as_deref().unwrap_or("N/A"),
|
||||||
|
transaction.comment.as_deref().unwrap_or("N/A"),
|
||||||
|
transaction.product.id,
|
||||||
|
transaction.product.name,
|
||||||
|
transaction.product.traffic,
|
||||||
|
transaction.product.dist,
|
||||||
|
transaction.product.arch.as_deref().unwrap_or("N/A"),
|
||||||
|
transaction.product.lang,
|
||||||
|
transaction.product.cpu,
|
||||||
|
transaction.product.cpu_benchmark,
|
||||||
|
transaction.product.memory_size,
|
||||||
|
transaction.product.hdd_size,
|
||||||
|
transaction.product.hdd_text,
|
||||||
|
transaction.product.hdd_count,
|
||||||
|
transaction.product.datacenter,
|
||||||
|
transaction.product.network_speed,
|
||||||
|
transaction.product.fixed_price.unwrap_or_default().to_string(),
|
||||||
|
transaction
|
||||||
|
.product
|
||||||
|
.next_reduce
|
||||||
|
.map_or("N/A".to_string(), |r| r.to_string()),
|
||||||
|
transaction
|
||||||
|
.product
|
||||||
|
.next_reduce_date
|
||||||
|
.as_deref()
|
||||||
|
.unwrap_or("N/A"),
|
||||||
|
transaction.addons.join(", "),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table.printstd();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pretty_print_server_addon_transactions(transactions: rhai::Array) {
|
||||||
|
let mut table = Table::new();
|
||||||
|
table.add_row(row![b =>
|
||||||
|
"ID",
|
||||||
|
"Date",
|
||||||
|
"Status",
|
||||||
|
"Server Number",
|
||||||
|
"Product ID",
|
||||||
|
"Product Name",
|
||||||
|
"Product Price",
|
||||||
|
"Resources",
|
||||||
|
]);
|
||||||
|
|
||||||
|
for transaction_dyn in transactions {
|
||||||
|
if let Some(transaction) = transaction_dyn.try_cast::<ServerAddonTransaction>() {
|
||||||
|
let mut resources_table = Table::new();
|
||||||
|
resources_table.add_row(row![b => "Type", "ID"]);
|
||||||
|
for resource in &transaction.resources {
|
||||||
|
resources_table.add_row(row![resource.resource_type, resource.id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
table.add_row(row![
|
||||||
|
transaction.id,
|
||||||
|
transaction.date,
|
||||||
|
transaction.status,
|
||||||
|
transaction.server_number,
|
||||||
|
transaction.product.id,
|
||||||
|
transaction.product.name,
|
||||||
|
transaction.product.price.to_string(),
|
||||||
|
resources_table,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table.printstd();
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
use prettytable::{row, Table};
|
||||||
|
use rhai::Array;
|
||||||
|
|
||||||
|
use super::Server;
|
||||||
|
|
||||||
|
pub fn pretty_print_servers(servers: Array) {
|
||||||
|
let mut table = Table::new();
|
||||||
|
table.add_row(row![b =>
|
||||||
|
"Number",
|
||||||
|
"Name",
|
||||||
|
"IP",
|
||||||
|
"Product",
|
||||||
|
"DC",
|
||||||
|
"Status"
|
||||||
|
]);
|
||||||
|
|
||||||
|
for server_dyn in servers {
|
||||||
|
if let Some(server) = server_dyn.try_cast::<Server>() {
|
||||||
|
table.add_row(row![
|
||||||
|
server.server_number.to_string(),
|
||||||
|
server.server_name,
|
||||||
|
server.server_ip.unwrap_or("N/A".to_string()),
|
||||||
|
server.product,
|
||||||
|
server.dc,
|
||||||
|
server.status
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table.printstd();
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
use prettytable::{row, Table};
|
||||||
|
use super::SshKey;
|
||||||
|
|
||||||
|
pub fn pretty_print_ssh_keys(keys: rhai::Array) {
|
||||||
|
let mut table = Table::new();
|
||||||
|
table.add_row(row![b =>
|
||||||
|
"Name",
|
||||||
|
"Fingerprint",
|
||||||
|
"Type",
|
||||||
|
"Size",
|
||||||
|
"Created At"
|
||||||
|
]);
|
||||||
|
|
||||||
|
for key_dyn in keys {
|
||||||
|
if let Some(key) = key_dyn.try_cast::<SshKey>() {
|
||||||
|
table.add_row(row![
|
||||||
|
key.name,
|
||||||
|
key.fingerprint,
|
||||||
|
key.key_type,
|
||||||
|
key.size.to_string(),
|
||||||
|
key.created_at
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table.printstd();
|
||||||
|
}
|
76
packages/clients/hetznerclient/src/rhai/server.rs
Normal file
76
packages/clients/hetznerclient/src/rhai/server.rs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
use crate::api::{Client, models::Server};
|
||||||
|
use rhai::{Array, Dynamic, plugin::*};
|
||||||
|
|
||||||
|
pub fn register(engine: &mut Engine) {
|
||||||
|
let server_module = exported_module!(server_api);
|
||||||
|
engine.register_global_module(server_module.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export_module]
|
||||||
|
pub mod server_api {
|
||||||
|
use crate::api::models::Cancellation;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use rhai::EvalAltResult;
|
||||||
|
|
||||||
|
#[rhai_fn(name = "get_server", return_raw)]
|
||||||
|
pub fn get_server(
|
||||||
|
client: &mut Client,
|
||||||
|
server_number: i64,
|
||||||
|
) -> Result<Server, Box<EvalAltResult>> {
|
||||||
|
client
|
||||||
|
.get_server(server_number as i32)
|
||||||
|
.map_err(|e| e.to_string().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "get_servers", return_raw)]
|
||||||
|
pub fn get_servers(client: &mut Client) -> Result<Array, Box<EvalAltResult>> {
|
||||||
|
let servers = client
|
||||||
|
.get_servers()
|
||||||
|
.map_err(|e| Into::<Box<EvalAltResult>>::into(e.to_string()))?;
|
||||||
|
println!("number of SERVERS we got: {:#?}", servers.len());
|
||||||
|
Ok(servers.into_iter().map(Dynamic::from).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "update_server_name", return_raw)]
|
||||||
|
pub fn update_server_name(
|
||||||
|
client: &mut Client,
|
||||||
|
server_number: i64,
|
||||||
|
name: &str,
|
||||||
|
) -> Result<Server, Box<EvalAltResult>> {
|
||||||
|
client
|
||||||
|
.update_server_name(server_number as i32, name)
|
||||||
|
.map_err(|e| e.to_string().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "get_cancellation_data", return_raw)]
|
||||||
|
pub fn get_cancellation_data(
|
||||||
|
client: &mut Client,
|
||||||
|
server_number: i64,
|
||||||
|
) -> Result<Cancellation, Box<EvalAltResult>> {
|
||||||
|
client
|
||||||
|
.get_cancellation_data(server_number as i32)
|
||||||
|
.map_err(|e| e.to_string().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "cancel_server", return_raw)]
|
||||||
|
pub fn cancel_server(
|
||||||
|
client: &mut Client,
|
||||||
|
server_number: i64,
|
||||||
|
cancellation_date: &str,
|
||||||
|
) -> Result<Cancellation, Box<EvalAltResult>> {
|
||||||
|
client
|
||||||
|
.cancel_server(server_number as i32, cancellation_date)
|
||||||
|
.map_err(|e| e.to_string().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "withdraw_cancellation", return_raw)]
|
||||||
|
pub fn withdraw_cancellation(
|
||||||
|
client: &mut Client,
|
||||||
|
server_number: i64,
|
||||||
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
|
client
|
||||||
|
.withdraw_cancellation(server_number as i32)
|
||||||
|
.map_err(|e| e.to_string().into())
|
||||||
|
}
|
||||||
|
}
|
170
packages/clients/hetznerclient/src/rhai/server_ordering.rs
Normal file
170
packages/clients/hetznerclient/src/rhai/server_ordering.rs
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
use crate::api::{
|
||||||
|
Client,
|
||||||
|
models::{
|
||||||
|
AuctionServerProduct, AuctionTransaction, OrderAuctionServerBuilder, OrderServerBuilder,
|
||||||
|
OrderServerProduct, ServerAddonProduct, ServerAddonTransaction, Transaction,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use rhai::{Array, Dynamic, plugin::*};
|
||||||
|
|
||||||
|
pub fn register(engine: &mut Engine) {
|
||||||
|
let server_order_module = exported_module!(server_order_api);
|
||||||
|
engine.register_global_module(server_order_module.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export_module]
|
||||||
|
pub mod server_order_api {
|
||||||
|
use crate::api::models::OrderServerAddonBuilder;
|
||||||
|
|
||||||
|
#[rhai_fn(name = "get_server_products", return_raw)]
|
||||||
|
pub fn get_server_ordering_product_overview(
|
||||||
|
client: &mut Client,
|
||||||
|
) -> Result<Array, Box<EvalAltResult>> {
|
||||||
|
let overview_servers = client
|
||||||
|
.get_server_products()
|
||||||
|
.map_err(|e| Into::<Box<EvalAltResult>>::into(e.to_string()))?;
|
||||||
|
Ok(overview_servers.into_iter().map(Dynamic::from).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "get_server_product_by_id", return_raw)]
|
||||||
|
pub fn get_server_ordering_product_by_id(
|
||||||
|
client: &mut Client,
|
||||||
|
product_id: &str,
|
||||||
|
) -> Result<OrderServerProduct, Box<EvalAltResult>> {
|
||||||
|
let product = client
|
||||||
|
.get_server_product_by_id(product_id)
|
||||||
|
.map_err(|e| Into::<Box<EvalAltResult>>::into(e.to_string()))?;
|
||||||
|
Ok(product)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "order_server", return_raw)]
|
||||||
|
pub fn order_server(
|
||||||
|
client: &mut Client,
|
||||||
|
order: OrderServerBuilder,
|
||||||
|
) -> Result<Transaction, Box<EvalAltResult>> {
|
||||||
|
let transaction = client
|
||||||
|
.order_server(order)
|
||||||
|
.map_err(|e| Into::<Box<EvalAltResult>>::into(e.to_string()))?;
|
||||||
|
Ok(transaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "get_transaction_by_id", return_raw)]
|
||||||
|
pub fn get_transaction_by_id(
|
||||||
|
client: &mut Client,
|
||||||
|
transaction_id: &str,
|
||||||
|
) -> Result<Transaction, Box<EvalAltResult>> {
|
||||||
|
let transaction = client
|
||||||
|
.get_transaction_by_id(transaction_id)
|
||||||
|
.map_err(|e| Into::<Box<EvalAltResult>>::into(e.to_string()))?;
|
||||||
|
Ok(transaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "get_transactions", return_raw)]
|
||||||
|
pub fn get_transactions(client: &mut Client) -> Result<Array, Box<EvalAltResult>> {
|
||||||
|
let transactions = client
|
||||||
|
.get_transactions()
|
||||||
|
.map_err(|e| Into::<Box<EvalAltResult>>::into(e.to_string()))?;
|
||||||
|
Ok(transactions.into_iter().map(Dynamic::from).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "get_auction_server_products", return_raw)]
|
||||||
|
pub fn get_auction_server_products(client: &mut Client) -> Result<Array, Box<EvalAltResult>> {
|
||||||
|
let products = client
|
||||||
|
.get_auction_server_products()
|
||||||
|
.map_err(|e| Into::<Box<EvalAltResult>>::into(e.to_string()))?;
|
||||||
|
Ok(products.into_iter().map(Dynamic::from).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "get_auction_server_product_by_id", return_raw)]
|
||||||
|
pub fn get_auction_server_product_by_id(
|
||||||
|
client: &mut Client,
|
||||||
|
product_id: &str,
|
||||||
|
) -> Result<AuctionServerProduct, Box<EvalAltResult>> {
|
||||||
|
let product = client
|
||||||
|
.get_auction_server_product_by_id(product_id)
|
||||||
|
.map_err(|e| Into::<Box<EvalAltResult>>::into(e.to_string()))?;
|
||||||
|
Ok(product)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "get_auction_transactions", return_raw)]
|
||||||
|
pub fn get_auction_transactions(client: &mut Client) -> Result<Array, Box<EvalAltResult>> {
|
||||||
|
let transactions = client
|
||||||
|
.get_auction_transactions()
|
||||||
|
.map_err(|e| Into::<Box<EvalAltResult>>::into(e.to_string()))?;
|
||||||
|
Ok(transactions.into_iter().map(Dynamic::from).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "get_auction_transaction_by_id", return_raw)]
|
||||||
|
pub fn get_auction_transaction_by_id(
|
||||||
|
client: &mut Client,
|
||||||
|
transaction_id: &str,
|
||||||
|
) -> Result<AuctionTransaction, Box<EvalAltResult>> {
|
||||||
|
let transaction = client
|
||||||
|
.get_auction_transaction_by_id(transaction_id)
|
||||||
|
.map_err(|e| Into::<Box<EvalAltResult>>::into(e.to_string()))?;
|
||||||
|
Ok(transaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "get_server_addon_products", return_raw)]
|
||||||
|
pub fn get_server_addon_products(
|
||||||
|
client: &mut Client,
|
||||||
|
server_number: i64,
|
||||||
|
) -> Result<Array, Box<EvalAltResult>> {
|
||||||
|
let products = client
|
||||||
|
.get_server_addon_products(server_number)
|
||||||
|
.map_err(|e| Into::<Box<EvalAltResult>>::into(e.to_string()))?;
|
||||||
|
Ok(products.into_iter().map(Dynamic::from).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "get_server_addon_transactions", return_raw)]
|
||||||
|
pub fn get_server_addon_transactions(
|
||||||
|
client: &mut Client,
|
||||||
|
) -> Result<Array, Box<EvalAltResult>> {
|
||||||
|
let transactions = client
|
||||||
|
.get_server_addon_transactions()
|
||||||
|
.map_err(|e| Into::<Box<EvalAltResult>>::into(e.to_string()))?;
|
||||||
|
Ok(transactions.into_iter().map(Dynamic::from).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "get_server_addon_transaction_by_id", return_raw)]
|
||||||
|
pub fn get_server_addon_transaction_by_id(
|
||||||
|
client: &mut Client,
|
||||||
|
transaction_id: &str,
|
||||||
|
) -> Result<ServerAddonTransaction, Box<EvalAltResult>> {
|
||||||
|
let transaction = client
|
||||||
|
.get_server_addon_transaction_by_id(transaction_id)
|
||||||
|
.map_err(|e| Into::<Box<EvalAltResult>>::into(e.to_string()))?;
|
||||||
|
Ok(transaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "order_auction_server", return_raw)]
|
||||||
|
pub fn order_auction_server(
|
||||||
|
client: &mut Client,
|
||||||
|
order: OrderAuctionServerBuilder,
|
||||||
|
) -> Result<AuctionTransaction, Box<EvalAltResult>> {
|
||||||
|
println!("Builder struct being used to order server: {:#?}", order);
|
||||||
|
let transaction = client.order_auction_server(
|
||||||
|
order.product_id,
|
||||||
|
order.authorized_keys.unwrap_or(vec![]),
|
||||||
|
order.dist,
|
||||||
|
None,
|
||||||
|
order.lang,
|
||||||
|
order.comment,
|
||||||
|
order.addon,
|
||||||
|
order.test,
|
||||||
|
).map_err(|e| Into::<Box<EvalAltResult>>::into(e.to_string()))?;
|
||||||
|
Ok(transaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "order_server_addon", return_raw)]
|
||||||
|
pub fn order_server_addon(
|
||||||
|
client: &mut Client,
|
||||||
|
order: OrderServerAddonBuilder,
|
||||||
|
) -> Result<ServerAddonTransaction, Box<EvalAltResult>> {
|
||||||
|
println!("Builder struct being used to order server addon: {:#?}", order);
|
||||||
|
let transaction = client
|
||||||
|
.order_server_addon(order)
|
||||||
|
.map_err(|e| Into::<Box<EvalAltResult>>::into(e.to_string()))?;
|
||||||
|
Ok(transaction)
|
||||||
|
}
|
||||||
|
}
|
89
packages/clients/hetznerclient/src/rhai/ssh_keys.rs
Normal file
89
packages/clients/hetznerclient/src/rhai/ssh_keys.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
use crate::api::{Client, models::SshKey};
|
||||||
|
use prettytable::{Table, row};
|
||||||
|
use rhai::{Array, Dynamic, Engine, plugin::*};
|
||||||
|
|
||||||
|
pub fn register(engine: &mut Engine) {
|
||||||
|
let ssh_keys_module = exported_module!(ssh_keys_api);
|
||||||
|
engine.register_global_module(ssh_keys_module.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export_module]
|
||||||
|
pub mod ssh_keys_api {
|
||||||
|
use super::*;
|
||||||
|
use rhai::EvalAltResult;
|
||||||
|
|
||||||
|
#[rhai_fn(name = "get_ssh_keys", return_raw)]
|
||||||
|
pub fn get_ssh_keys(client: &mut Client) -> Result<Array, Box<EvalAltResult>> {
|
||||||
|
let ssh_keys = client
|
||||||
|
.get_ssh_keys()
|
||||||
|
.map_err(|e| Into::<Box<EvalAltResult>>::into(e.to_string()))?;
|
||||||
|
Ok(ssh_keys.into_iter().map(Dynamic::from).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "get_ssh_key", return_raw)]
|
||||||
|
pub fn get_ssh_key(
|
||||||
|
client: &mut Client,
|
||||||
|
fingerprint: &str,
|
||||||
|
) -> Result<SshKey, Box<EvalAltResult>> {
|
||||||
|
client
|
||||||
|
.get_ssh_key(fingerprint)
|
||||||
|
.map_err(|e| e.to_string().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "add_ssh_key", return_raw)]
|
||||||
|
pub fn add_ssh_key(
|
||||||
|
client: &mut Client,
|
||||||
|
name: &str,
|
||||||
|
data: &str,
|
||||||
|
) -> Result<SshKey, Box<EvalAltResult>> {
|
||||||
|
client
|
||||||
|
.add_ssh_key(name, data)
|
||||||
|
.map_err(|e| e.to_string().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "update_ssh_key_name", return_raw)]
|
||||||
|
pub fn update_ssh_key_name(
|
||||||
|
client: &mut Client,
|
||||||
|
fingerprint: &str,
|
||||||
|
name: &str,
|
||||||
|
) -> Result<SshKey, Box<EvalAltResult>> {
|
||||||
|
client
|
||||||
|
.update_ssh_key_name(fingerprint, name)
|
||||||
|
.map_err(|e| e.to_string().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "delete_ssh_key", return_raw)]
|
||||||
|
pub fn delete_ssh_key(
|
||||||
|
client: &mut Client,
|
||||||
|
fingerprint: &str,
|
||||||
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
|
client
|
||||||
|
.delete_ssh_key(fingerprint)
|
||||||
|
.map_err(|e| e.to_string().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "pretty_print")]
|
||||||
|
pub fn pretty_print_ssh_keys(keys: Array) {
|
||||||
|
let mut table = Table::new();
|
||||||
|
table.add_row(row![b =>
|
||||||
|
"Name",
|
||||||
|
"Fingerprint",
|
||||||
|
"Type",
|
||||||
|
"Size",
|
||||||
|
"Created At"
|
||||||
|
]);
|
||||||
|
|
||||||
|
for key_dyn in keys {
|
||||||
|
if let Some(key) = key_dyn.try_cast::<SshKey>() {
|
||||||
|
table.add_row(row![
|
||||||
|
key.name,
|
||||||
|
key.fingerprint,
|
||||||
|
key.key_type,
|
||||||
|
key.size.to_string(),
|
||||||
|
key.created_at
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table.printstd();
|
||||||
|
}
|
||||||
|
}
|
@ -26,6 +26,7 @@ sal-redisclient = { workspace = true }
|
|||||||
sal-postgresclient = { workspace = true }
|
sal-postgresclient = { workspace = true }
|
||||||
sal-virt = { workspace = true }
|
sal-virt = { workspace = true }
|
||||||
sal-mycelium = { workspace = true }
|
sal-mycelium = { workspace = true }
|
||||||
|
sal-hetzner = { workspace = true }
|
||||||
sal-text = { workspace = true }
|
sal-text = { workspace = true }
|
||||||
sal-net = { workspace = true }
|
sal-net = { workspace = true }
|
||||||
sal-zinit-client = { workspace = true }
|
sal-zinit-client = { workspace = true }
|
||||||
|
@ -90,6 +90,9 @@ pub use sal_zinit_client::rhai::register_zinit_module;
|
|||||||
// Re-export mycelium module
|
// Re-export mycelium module
|
||||||
pub use sal_mycelium::rhai::register_mycelium_module;
|
pub use sal_mycelium::rhai::register_mycelium_module;
|
||||||
|
|
||||||
|
// Re-export hetzner module
|
||||||
|
pub use sal_hetzner::rhai::register_hetzner_module;
|
||||||
|
|
||||||
// Re-export text module
|
// Re-export text module
|
||||||
pub use sal_text::rhai::register_text_module;
|
pub use sal_text::rhai::register_text_module;
|
||||||
|
|
||||||
@ -151,6 +154,9 @@ pub fn register(engine: &mut Engine) -> Result<(), Box<rhai::EvalAltResult>> {
|
|||||||
// Register Mycelium module functions
|
// Register Mycelium module functions
|
||||||
sal_mycelium::rhai::register_mycelium_module(engine)?;
|
sal_mycelium::rhai::register_mycelium_module(engine)?;
|
||||||
|
|
||||||
|
// Register Hetzner module functions
|
||||||
|
sal_hetzner::rhai::register_hetzner_module(engine)?;
|
||||||
|
|
||||||
// Register Text module functions
|
// Register Text module functions
|
||||||
sal_text::rhai::register_text_module(engine)?;
|
sal_text::rhai::register_text_module(engine)?;
|
||||||
|
|
||||||
|
@ -46,6 +46,9 @@ pub use sal_kubernetes as kubernetes;
|
|||||||
#[cfg(feature = "mycelium")]
|
#[cfg(feature = "mycelium")]
|
||||||
pub use sal_mycelium as mycelium;
|
pub use sal_mycelium as mycelium;
|
||||||
|
|
||||||
|
#[cfg(feature = "hetzner")]
|
||||||
|
pub use sal_hetzner as hetzner;
|
||||||
|
|
||||||
#[cfg(feature = "net")]
|
#[cfg(feature = "net")]
|
||||||
pub use sal_net as net;
|
pub use sal_net as net;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user