use serde::{Deserialize, Serialize};
use std::time::Duration;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TailscaleKeyResponse {
pub tailscale_auth_key: String,
}
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)
}
pub fn fetch_tailscale_auth_key() -> Result<(), String> {
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(()),
};
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(())
}
pub fn fetch_broker_config() -> Result<(), String> {
fetch_tailscale_auth_key()
}