# Hetzner Cloud HTTP API Client use env.nu * # Get Bearer token for API authentication export def hetzner_api_auth []: nothing -> string { let token = (hetzner_api_token) if ($token | is-empty) { error make {msg: "HCLOUD_TOKEN environment variable not set. Set your Hetzner API token before using the API interface."} } $token } # Build full API URL export def hetzner_api_url [path: string]: nothing -> string { let base = (hetzner_api_url_base) $"($base)($path)" } # Generic HTTP request with error handling export def hetzner_api_request [method: string, path: string, data?: any]: nothing -> any { let token = (hetzner_api_auth) let url = (hetzner_api_url $path) if (hetzner_debug) { print $"DEBUG: hetzner_api_request method=($method) path=($path) url=($url)" | encode utf8 | into string } let headers = [Authorization $"Bearer ($token)"] let result = (do { match $method { "GET" => { http get --headers $headers --allow-errors $url } "POST" => { http post --headers $headers --content-type application/json --allow-errors $url $data } "PUT" => { http put --headers $headers --content-type application/json --allow-errors $url $data } "DELETE" => { http delete --headers $headers --allow-errors $url } _ => { error make {msg: $"Unsupported HTTP method: ($method)"} } } } | complete) if $result.exit_code != 0 { error make {msg: $"Hetzner API request failed: ($result.stderr)"} } else { $result.stdout } } # List all servers export def hetzner_api_list_servers []: nothing -> list { let response = (hetzner_api_request "GET" "/servers") if ($response | describe) =~ "error" { error make {msg: "Failed to list servers from API"} } if ($response | has servers) { $response.servers } else { [] } } # Get server info by ID or name export def hetzner_api_server_info [id_or_name: string]: nothing -> record { let response = (hetzner_api_request "GET" $"/servers/($id_or_name)") if ($response | describe) =~ "error" { error make {msg: $"Server not found: ($id_or_name)"} } if ($response | has server) { $response.server } else { $response } } # Create a new server export def hetzner_api_create_server [config: record]: nothing -> record { if (hetzner_debug) { print $"DEBUG: Creating server with config: ($config | to json)" | encode utf8 | into string } let response = (hetzner_api_request "POST" "/servers" $config) if ($response | describe) =~ "error" { error make {msg: $"Failed to create server: ($response)"} } if ($response | has server) { $response.server } else { $response } } # Delete a server export def hetzner_api_delete_server [id: string]: nothing -> nothing { let response = (hetzner_api_request "DELETE" $"/servers/($id)") null } # Perform server action (start, stop, reboot, etc.) export def hetzner_api_server_action [id: string, action: string]: nothing -> record { let data = {action: $action} let response = (hetzner_api_request "POST" $"/servers/($id)/actions/($action)" $data) if ($response | has action) { $response.action } else { $response } } # List all locations export def hetzner_api_list_locations []: nothing -> list { let response = (hetzner_api_request "GET" "/locations") if ($response | has locations) { $response.locations } else { [] } } # List all server types export def hetzner_api_list_server_types []: nothing -> list { let response = (hetzner_api_request "GET" "/server_types") if ($response | has server_types) { $response.server_types } else { [] } } # Get server type info export def hetzner_api_server_type_info [id_or_name: string]: nothing -> record { let response = (hetzner_api_request "GET" $"/server_types/($id_or_name)") if ($response | has server_type) { $response.server_type } else { $response } } # List all images export def hetzner_api_list_images []: nothing -> list { let response = (hetzner_api_request "GET" "/images") if ($response | has images) { $response.images } else { [] } } # List all volumes export def hetzner_api_list_volumes []: nothing -> list { let response = (hetzner_api_request "GET" "/volumes") if ($response | has volumes) { $response.volumes } else { [] } } # Create a volume export def hetzner_api_create_volume [config: record]: nothing -> record { let response = (hetzner_api_request "POST" "/volumes" $config) if ($response | has volume) { $response.volume } else { $response } } # Delete a volume export def hetzner_api_delete_volume [id: string]: nothing -> nothing { hetzner_api_request "DELETE" $"/volumes/($id)" null } # Attach volume to server export def hetzner_api_attach_volume [volume_id: string, server_id: string]: nothing -> record { let data = { server: ($server_id | into int) automount: false } let response = (hetzner_api_request "POST" $"/volumes/($volume_id)/actions/attach" $data) if ($response | has action) { $response.action } else { $response } } # Detach volume from server export def hetzner_api_detach_volume [volume_id: string]: nothing -> record { let response = (hetzner_api_request "POST" $"/volumes/($volume_id)/actions/detach" {}) if ($response | has action) { $response.action } else { $response } } # List all networks export def hetzner_api_list_networks []: nothing -> list { let response = (hetzner_api_request "GET" "/networks") if ($response | has networks) { $response.networks } else { [] } } # Get network info export def hetzner_api_network_info [id_or_name: string]: nothing -> record { let response = (hetzner_api_request "GET" $"/networks/($id_or_name)") if ($response | has network) { $response.network } else { $response } } # Attach network to server export def hetzner_api_attach_network [server_id: string, network_id: string, ip?: string]: nothing -> record { let data = if ($ip != null) { {server: ($server_id | into int), network: ($network_id | into int), ip: $ip} } else { {server: ($server_id | into int), network: ($network_id | into int)} } let response = (hetzner_api_request "POST" $"/servers/($server_id)/actions/attach_to_network" $data) if ($response | has action) { $response.action } else { $response } } # Detach network from server export def hetzner_api_detach_network [server_id: string, network_id: string]: nothing -> record { let data = {network: ($network_id | into int)} let response = (hetzner_api_request "POST" $"/servers/($server_id)/actions/detach_from_network" $data) if ($response | has action) { $response.action } else { $response } } # List all floating IPs export def hetzner_api_list_floating_ips []: nothing -> list { let response = (hetzner_api_request "GET" "/floating_ips") if ($response | has floating_ips) { $response.floating_ips } else { [] } } # Get pricing information export def hetzner_api_get_pricing []: nothing -> record { let response = (hetzner_api_request "GET" "/pricing") if ($response | has pricing) { $response.pricing } else { $response } } # List SSH keys export def hetzner_api_list_ssh_keys []: nothing -> list { let response = (hetzner_api_request "GET" "/ssh_keys") if ($response | has ssh_keys) { $response.ssh_keys } else { [] } } # Get SSH key info export def hetzner_api_ssh_key_info [id_or_name: string]: nothing -> record { let response = (hetzner_api_request "GET" $"/ssh_keys/($id_or_name)") if ($response | has ssh_key) { $response.ssh_key } else { $response } } # List firewalls export def hetzner_api_list_firewalls []: nothing -> list { let response = (hetzner_api_request "GET" "/firewalls") if ($response | has firewalls) { $response.firewalls } else { [] } } # Get firewall info export def hetzner_api_firewall_info [id_or_name: string]: nothing -> record { let response = (hetzner_api_request "GET" $"/firewalls/($id_or_name)") if ($response | has firewall) { $response.firewall } else { $response } } # Create firewall export def hetzner_api_create_firewall [config: record]: nothing -> record { let response = (hetzner_api_request "POST" "/firewalls" $config) if ($response | has firewall) { $response.firewall } else { $response } }