lmrc-cli 0.3.16

CLI tool for scaffolding LMRC Stack infrastructure projects
Documentation
//! Hetzner service layer - business logic for Hetzner resource monitoring

use super::models::*;
use crate::error::AppError;
use lmrc_hetzner::{HetznerClient, HetznerConfig};

pub struct HetznerService {
    client: HetznerClient,
}

impl HetznerService {
    pub fn new(api_token: String) -> Result<Self, AppError> {
        let config = HetznerConfig::new(api_token);
        let client = HetznerClient::new(config);
        Ok(Self { client })
    }

    /// List all servers in the Hetzner account
    pub async fn list_servers(&self) -> Result<ServersListResponse, AppError> {
        let servers = self.client
            .list_servers()
            .await
            .map_err(|e| AppError::External(format!("Hetzner API error: {}", e)))?;

        let server_responses: Vec<ServerResponse> = servers
            .iter()
            .map(|s| ServerResponse {
                id: s.id,
                name: s.name.clone(),
                status: s.status.clone(),
                server_type: s.server_type.name.clone(),
                public_ip: s.public_net.ipv4.as_ref().map(|ip| ip.ip.clone()),
                private_ip: s.private_net.first().map(|net| net.ip.clone()),
                created_at: s.created,
                datacenter: s.datacenter.name.clone(),
            })
            .collect();

        Ok(ServersListResponse {
            total: server_responses.len(),
            servers: server_responses,
        })
    }

    /// List all networks
    pub async fn list_networks(&self) -> Result<NetworksListResponse, AppError> {
        let networks = self.client
            .list_networks()
            .await
            .map_err(|e| AppError::External(format!("Hetzner API error: {}", e)))?;

        let network_responses: Vec<NetworkResponse> = networks
            .iter()
            .map(|n| NetworkResponse {
                id: n.id,
                name: n.name.clone(),
                ip_range: n.ip_range.clone(),
                servers: n.servers.clone(),
                subnets: n.subnets.iter().map(|s| SubnetResponse {
                    ip_range: s.ip_range.clone(),
                    network_zone: s.network_zone.clone(),
                    gateway: s.gateway.clone(),
                }).collect(),
                created_at: n.created,
            })
            .collect();

        Ok(NetworksListResponse {
            total: network_responses.len(),
            networks: network_responses,
        })
    }

    /// List all load balancers
    pub async fn list_load_balancers(&self) -> Result<LoadBalancersListResponse, AppError> {
        let load_balancers = self.client
            .list_load_balancers()
            .await
            .map_err(|e| AppError::External(format!("Hetzner API error: {}", e)))?;

        let lb_responses: Vec<LoadBalancerResponse> = load_balancers
            .iter()
            .map(|lb| LoadBalancerResponse {
                id: lb.id,
                name: lb.name.clone(),
                public_ip: lb.public_net.ipv4.ip.clone(),
                algorithm: lb.algorithm.clone(),
                targets: vec![], // Would need server details lookup
                health_status: "unknown".to_string(),
                created_at: lb.created,
            })
            .collect();

        Ok(LoadBalancersListResponse {
            total: lb_responses.len(),
            load_balancers: lb_responses,
        })
    }

    /// List all firewalls
    pub async fn list_firewalls(&self) -> Result<FirewallsListResponse, AppError> {
        let firewalls = self.client
            .list_firewalls()
            .await
            .map_err(|e| AppError::External(format!("Hetzner API error: {}", e)))?;

        let firewall_responses: Vec<FirewallResponse> = firewalls
            .iter()
            .map(|fw| FirewallResponse {
                id: fw.id,
                name: fw.name.clone(),
                rules_count: fw.rules.len(),
                applied_to: fw.applied_to.iter().map(|a| FirewallApplicationResponse {
                    server_id: a.server,
                    server_name: None, // Would need server details lookup
                }).collect(),
                created_at: fw.created,
            })
            .collect();

        Ok(FirewallsListResponse {
            total: firewall_responses.len(),
            firewalls: firewall_responses,
        })
    }
}