diff --git a/README.md b/README.md new file mode 100644 index 0000000..709ea85 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# Hetzner Robot API Rhai Client + +This project provides a Rhai scripting client for interacting with the Hetzner Robot API. It allows users to manage their Hetzner servers, SSH keys, boot configurations, and order new servers and addons directly from Rhai scripts. + +## Goal + +The primary goal of this project is to offer a flexible and scriptable interface to the Hetzner Robot API, enabling automation of server management tasks. By leveraging Rhai, users can write simple yet powerful scripts to interact with their Hetzner infrastructure. + +## Examples + +The `examples/` directory contains several Rhai scripts demonstrating the capabilities of this client: + +- [`examples/server_management.rhai`](examples/server_management.rhai): Showcases basic server management operations, such as fetching server details and updating server names. +- [`examples/ssh_key_management.rhai`](examples/ssh_key_management.rhai): Demonstrates how to list, add, update, and delete SSH keys. +- [`examples/boot_management.rhai`](examples/boot_management.rhai): Provides examples for managing server boot configurations, including rescue mode. +- [`examples/server_ordering.rhai`](examples/server_ordering.rhai): Contains comprehensive examples for ordering new servers and managing server addons, including: + - Getting available server products. + - Ordering new servers. + - Listing server order transactions. + - Fetching specific transaction details. + - Listing and ordering auction servers. + - Getting available server addon products. + - Listing server addon transactions. + - Ordering server addons. + - Querying specific server addon transactions. + +## Important Note on IPv6 Addresses + +When ordering a server without an IPv4 addon, the server might be assigned an IPv6 network like `2a01:4f8:221:1fe3::/64`. It's important to note that you cannot directly SSH to this network address. Hetzner does not use SLAAC for server assignments. The actual address to SSH to will typically be the network address with `2` appended (e.g., `2a01:4f8:221:1fe3::2`). \ No newline at end of file diff --git a/src/api/mod.rs b/src/api/mod.rs index 188c04b..d34406c 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -504,7 +504,7 @@ impl Client { .http_client .post(format!("{}/order/server_addon/transaction", &self.config.api_url)) .basic_auth(&self.config.username, Some(&self.config.password)) - .json(¶ms) + .form(¶ms) .send()?; let wrapped: ServerAddonTransactionWrapper = self.handle_response(response)?; diff --git a/src/api/models.rs b/src/api/models.rs index e2b4aeb..d7f79d5 100644 --- a/src/api/models.rs +++ b/src/api/models.rs @@ -12,8 +12,8 @@ pub struct ServerWrapper { #[derive(Debug, Deserialize, Clone, CustomType)] #[rhai_type(extra = Self::build_rhai_type)] pub struct Server { - pub server_ip: String, - pub server_ipv6_net: String, + pub server_ip: Option, + pub server_ipv6_net: Option, pub server_number: i32, pub server_name: String, pub product: String, @@ -22,7 +22,7 @@ pub struct Server { pub status: String, pub cancelled: bool, pub paid_until: String, - pub ip: Vec, + pub ip: Option>, pub subnet: Option>, pub reset: Option, pub rescue: Option, @@ -39,9 +39,11 @@ impl Server { fn build_rhai_type(builder: &mut TypeBuilder) { builder .with_name("Server") - .with_get("server_ip", |s: &mut Server| s.server_ip.clone()) + .with_get("server_ip", |s: &mut Server| { + s.server_ip.clone().unwrap_or_default() + }) .with_get("server_ipv6_net", |s: &mut Server| { - s.server_ipv6_net.clone() + s.server_ipv6_net.clone().unwrap_or_default() }) .with_get("server_number", |s: &mut Server| s.server_number) .with_get("server_name", |s: &mut Server| s.server_name.clone()) @@ -51,7 +53,7 @@ impl Server { .with_get("status", |s: &mut Server| s.status.clone()) .with_get("cancelled", |s: &mut Server| s.cancelled) .with_get("paid_until", |s: &mut Server| s.paid_until.clone()) - .with_get("ip", |s: &mut Server| s.ip.clone()) + .with_get("ip", |s: &mut Server| s.ip.clone().unwrap_or_default()) .with_get("subnet", |s: &mut Server| s.subnet.clone()) .with_get("reset", |s: &mut Server| s.reset.clone()) .with_get("rescue", |s: &mut Server| s.rescue.clone()) @@ -78,16 +80,22 @@ impl fmt::Display for Server { table.add_row(row!["Property", "Value"]); table.add_row(row!["Server Number", self.server_number.to_string()]); table.add_row(row!["Server Name", self.server_name.clone()]); - table.add_row(row!["Server IP", self.server_ip.clone()]); - table.add_row(row!["IPv6 Network", self.server_ipv6_net.clone()]); + table.add_row(row![ + "Server IP", + self.server_ip.as_deref().unwrap_or("N/A") + ]); + table.add_row(row![ + "IPv6 Network", + self.server_ipv6_net.as_deref().unwrap_or("N/A") + ]); table.add_row(row!["Product", self.product.clone()]); table.add_row(row!["Datacenter", self.dc.clone()]); table.add_row(row!["Traffic", self.traffic.clone()]); table.add_row(row!["Status", self.status.clone()]); table.add_row(row!["Cancelled", self.cancelled.to_string()]); table.add_row(row!["Paid Until", self.paid_until.clone()]); + table.add_row(row!["IP Addresses", self.ip.as_deref().unwrap_or_default().join(", ")]); table.add_row(row!["Reset", self.reset.unwrap_or(false).to_string()]); - table.add_row(row!["Rescue", self.rescue.unwrap_or(false).to_string()]); table.add_row(row!["VNC", self.vnc.unwrap_or(false).to_string()]); table.add_row(row!["Windows", self.windows.is_some().to_string()]); table.add_row(row!["Plesk", self.plesk.is_some().to_string()]); diff --git a/src/scripting/printing/servers_table.rs b/src/scripting/printing/servers_table.rs index dfd3dea..446720a 100644 --- a/src/scripting/printing/servers_table.rs +++ b/src/scripting/printing/servers_table.rs @@ -19,7 +19,7 @@ pub fn pretty_print_servers(servers: Array) { table.add_row(row![ server.server_number.to_string(), server.server_name, - server.server_ip, + server.server_ip.unwrap_or("N/A".to_string()), server.product, server.dc, server.status