use std::net::SocketAddr;
use crate::cli::admin_auth::{admin_get, parse_error_message, request_with_admin_auth};
pub struct RouteAddOpts {
pub model: String,
pub endpoints: Vec<String>,
pub strategy: Option<String>,
}
pub fn run_list(
config: &bitrouter_config::BitrouterConfig,
addr: SocketAddr,
) -> Result<(), Box<dyn std::error::Error>> {
let resp = admin_get(config, addr, "/admin/routes")?;
if !resp.status().is_success() {
let msg = parse_error_message(resp)?;
return Err(format!("failed to list routes: {msg}").into());
}
let body: serde_json::Value = resp.json()?;
let routes = body["routes"].as_array();
match routes {
Some(routes) if !routes.is_empty() => {
for route in routes {
let model = route["name"].as_str().unwrap_or("?");
let source = route["source"].as_str().unwrap_or("?");
let endpoints = route["endpoints"].as_array();
let targets: Vec<String> = endpoints
.map(|eps| {
eps.iter()
.map(|ep| {
let provider = ep["provider"].as_str().unwrap_or("?");
let service_id = ep["service_id"].as_str().unwrap_or("?");
if service_id.is_empty() {
provider.to_owned()
} else {
format!("{provider}:{service_id}")
}
})
.collect()
})
.unwrap_or_default();
let strategy = route["strategy"].as_str().unwrap_or("");
let strategy_suffix = if strategy.is_empty() {
String::new()
} else {
format!(" ({strategy})")
};
println!(
" {model} → {}{strategy_suffix} [{source}]",
targets.join(", ")
);
}
}
_ => {
println!(" (no routes configured)");
}
}
Ok(())
}
pub fn run_add(
config: &bitrouter_config::BitrouterConfig,
addr: SocketAddr,
opts: RouteAddOpts,
) -> Result<(), Box<dyn std::error::Error>> {
let endpoints: Vec<serde_json::Value> = opts
.endpoints
.iter()
.map(|ep| {
let (provider, service_id) = ep.split_once(':').unwrap_or((ep, ""));
serde_json::json!({
"provider": provider,
"service_id": service_id,
})
})
.collect();
let body = serde_json::json!({
"name": opts.model,
"strategy": opts.strategy.unwrap_or_else(|| "priority".to_owned()),
"endpoints": endpoints,
});
let url = format!("http://{addr}/admin/routes");
let client = reqwest::blocking::Client::new();
let resp = request_with_admin_auth(config, client.post(&url))?
.json(&body)
.send()?;
if resp.status().is_success() {
println!("route '{}' added", opts.model);
} else {
let msg = parse_error_message(resp)?;
return Err(format!("failed to add route: {msg}").into());
}
Ok(())
}
pub fn run_remove(
config: &bitrouter_config::BitrouterConfig,
addr: SocketAddr,
name: &str,
) -> Result<(), Box<dyn std::error::Error>> {
let url = format!("http://{addr}/admin/routes/{name}");
let client = reqwest::blocking::Client::new();
let resp = request_with_admin_auth(config, client.delete(&url))?.send()?;
if resp.status().is_success() {
println!("route '{name}' removed");
} else {
let msg = parse_error_message(resp)?;
return Err(format!("failed to remove route: {msg}").into());
}
Ok(())
}