zc2 0.0.13

P2P compute broker with credit-based billing, WAL, and broker mesh support
//! Configuration utilities for the broker, including fetching config from dashboard API.

use serde::{Deserialize, Serialize};
use std::time::Duration;

/// Response from /api/broker/config/tailscale-key
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TailscaleKeyResponse {
    pub tailscale_auth_key: String,
}

/// Fetch Tailscale auth key from dashboard API and return it.
/// Requires ZAKURO_API_KEY and ZAKURO_API_URL. Use for `zc show-network-auth`.
pub fn get_tailscale_auth_key() -> Result<String, String> {
    let api_key = std::env::var("ZAKURO_API_KEY")
        .map_err(|_| "ZAKURO_API_KEY is not set".to_string())?;
    if api_key.is_empty() {
        return Err("ZAKURO_API_KEY is empty".to_string());
    }

    let api_url = std::env::var("ZAKURO_API_URL")
        .unwrap_or_else(|_| "https://my.zakuro-ai.com".to_string());
    let endpoint = format!("{}/api/broker/config/tailscale-key", api_url.trim_end_matches('/'));

    let agent = ureq::AgentBuilder::new()
        .timeout_connect(Duration::from_secs(10))
        .timeout_read(Duration::from_secs(10))
        .build();

    let response = agent
        .get(&endpoint)
        .set("X-Broker-Api-Key", &api_key)
        .call()
        .map_err(|e| format!("Failed to fetch Tailscale auth key: {}", e))?;

    if response.status() != 200 {
        let status = response.status();
        let body = response.into_string().unwrap_or_else(|_| String::from("<no body>"));
        return Err(format!("API returned status {}: {}", status, body));
    }

    let body = response
        .into_string()
        .map_err(|e| format!("Failed to read response: {}", e))?;

    let key_response: TailscaleKeyResponse = serde_json::from_str(&body)
        .map_err(|e| format!("Failed to parse response: {}", e))?;

    Ok(key_response.tailscale_auth_key)
}

/// Fetch Tailscale auth key from dashboard API
///
/// If successful, sets the TAILSCALE_AUTHKEY environment variable.
/// Returns Ok(()) if successful or if no broker API key is configured.
/// Returns Err if the API call fails.
pub fn fetch_tailscale_auth_key() -> Result<(), String> {
    // Check if API key is configured
    let api_key = match std::env::var("ZAKURO_API_KEY") {
        Ok(key) if !key.is_empty() => key,
        _ => return Ok(()),
    };

    let api_url = match std::env::var("ZAKURO_API_URL") {
        Ok(url) if !url.is_empty() => url,
        _ => return Ok(()),
    };

    // Check if Tailscale auth key is already set
    if let Ok(existing_key) = std::env::var("TAILSCALE_AUTHKEY") {
        if !existing_key.is_empty() {
            return Ok(());
        }
    }

    let key = get_tailscale_auth_key()?;
    std::env::set_var("TAILSCALE_AUTHKEY", &key);
    println!("  ✓ Retrieved Tailscale auth key from dashboard API");
    Ok(())
}

/// Fetch all broker configuration from dashboard API
///
/// Currently only fetches Tailscale auth key, but can be extended
/// to fetch other configuration values in the future.
pub fn fetch_broker_config() -> Result<(), String> {
    fetch_tailscale_auth_key()
}