use crate::config::CliConfig;
use std::process::Command;
async fn get_service_id(config: &CliConfig) -> eyre::Result<String> {
let output = Command::new("cargo")
.args(["run", "--", "--emit-manifest"])
.output()?;
if !output.status.success() {
eyre::bail!("Failed to build service. Run from a cufflink service directory.");
}
let stdout = String::from_utf8(output.stdout)?;
let manifest: serde_json::Value = serde_json::from_str(stdout.trim())?;
let service_name = manifest["name"]
.as_str()
.ok_or_else(|| eyre::eyre!("No service name in manifest"))?;
let client = config.http_client();
let resp = config
.auth_request(
&client,
reqwest::Method::GET,
&format!("{}/api/services", config.api_url),
)
.send()
.await?;
let services: serde_json::Value = resp.json().await?;
let service = services
.as_array()
.and_then(|arr| {
arr.iter()
.find(|s| s["name"].as_str() == Some(service_name))
})
.ok_or_else(|| eyre::eyre!("Service '{}' not found on platform", service_name))?;
let id = service["id"]
.as_str()
.ok_or_else(|| eyre::eyre!("Service has no ID"))?;
Ok(id.to_string())
}
pub async fn export(output_path: &str, env: Option<&str>) -> eyre::Result<()> {
let config = CliConfig::load_with_env(env)?;
if let Some(ref name) = config.env_name {
println!("Environment: {}", name);
}
println!("Resolving service...");
let service_id = get_service_id(&config).await?;
println!("Exporting backup...");
let client = config.http_client();
let resp = config
.auth_request(
&client,
reqwest::Method::GET,
&format!("{}/api/services/{}/backup", config.api_url, service_id),
)
.send()
.await?;
if resp.status().is_success() {
let body = resp.text().await?;
std::fs::write(output_path, &body)?;
println!("Backup saved to {}", output_path);
} else {
let status = resp.status();
let body = resp.text().await.unwrap_or_default();
eyre::bail!("Backup failed ({}): {}", status, body);
}
Ok(())
}
pub async fn restore(
input_path: &str,
clear_existing: bool,
env: Option<&str>,
) -> eyre::Result<()> {
let config = CliConfig::load_with_env(env)?;
if let Some(ref name) = config.env_name {
println!("Environment: {}", name);
}
println!("Resolving service...");
let service_id = get_service_id(&config).await?;
let data = std::fs::read_to_string(input_path)?;
let mut payload: serde_json::Value = serde_json::from_str(&data)?;
if clear_existing {
if let Some(obj) = payload.as_object_mut() {
obj.insert("clear_existing".to_string(), serde_json::Value::Bool(true));
}
}
println!("Restoring backup...");
let client = config.http_client();
let resp = config
.auth_request(
&client,
reqwest::Method::POST,
&format!("{}/api/services/{}/restore", config.api_url, service_id),
)
.json(&payload)
.send()
.await?;
if resp.status().is_success() {
let body: serde_json::Value = resp.json().await?;
println!(
"Restore complete: {} tables, {} rows",
body["tables_restored"], body["rows_inserted"]
);
} else {
let status = resp.status();
let body = resp.text().await.unwrap_or_default();
eyre::bail!("Restore failed ({}): {}", status, body);
}
Ok(())
}