use anyhow::Result;
use super::style::*;
use crate::{cli::SecretsCommand, config};
pub async fn cmd_secrets(sub: SecretsCommand) -> Result<()> {
let config = config::load()?;
match sub {
SecretsCommand::Reload => {
ok("secrets reloaded -- restart the gateway to apply (hot reload not supported for secrets)");
}
SecretsCommand::Audit => {
banner(&format!("rsclaw secrets audit v{}", option_env!("RSCLAW_BUILD_VERSION").unwrap_or("dev")));
let secrets = config.ops.secrets.as_ref();
if let Some(s) = secrets {
println!(
" {:<20} {}",
bold("PROVIDER"),
bold("TYPE")
);
for (name, p) in &s.providers {
println!(" {:<20} {}", cyan(name), dim(&format!("{:?}", p.kind)));
}
} else {
warn_msg("no secrets configured");
}
}
SecretsCommand::Configure => {
banner(&format!("rsclaw secrets configure v{}", option_env!("RSCLAW_BUILD_VERSION").unwrap_or("dev")));
println!(" Add a secrets provider to rsclaw.json5:");
println!();
println!(" {}", dim(r#"secrets: {"#));
println!(" {}", dim(r#" providers: {"#));
println!(" {}", dim(r#" // env: reads from environment variables"#));
println!(" {}", dim(r#" env: { type: "env" },"#));
println!(" {}", dim(r#" // file: reads from a JSON/dotenv file"#));
println!(" {}", dim(r#" file: { type: "file", file: "~/.rsclaw/secrets.json" },"#));
println!(" {}", dim(r#" }"#));
println!(" {}", dim(r#"}"#));
println!();
println!(" Then reference secrets in config as: {}", cyan("${MY_SECRET_KEY}"));
}
SecretsCommand::Apply(args) => {
let from_path = std::path::Path::new(&args.from);
if !from_path.exists() {
anyhow::bail!("secrets file not found: {}", args.from);
}
let raw = std::fs::read_to_string(from_path)?;
let mut count = 0usize;
if args.from.ends_with(".json") || args.from.ends_with(".json5") {
let val: serde_json::Value = json5::from_str(&raw)?;
if let Some(obj) = val.as_object() {
for (k, v) in obj {
let s = v.as_str().unwrap_or_default();
if !args.dry_run {
unsafe { std::env::set_var(k, s) };
}
let label = if args.dry_run {
yellow("[dry-run]")
} else {
green("set ")
};
println!(
" {} {}={}",
label,
cyan(k),
&s[..s.len().min(4)].replace(|_: char| true, "*")
);
count += 1;
}
}
} else {
for line in raw.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
if let Some((k, v)) = line.split_once('=') {
if !args.dry_run {
unsafe { std::env::set_var(k.trim(), v.trim()) };
}
let label = if args.dry_run {
yellow("[dry-run]")
} else {
green("set ")
};
println!(
" {} {}=****",
label,
cyan(k.trim())
);
count += 1;
}
}
}
if args.dry_run {
warn_msg(&format!("{} secret(s) would be applied", bold(&count.to_string())));
} else {
ok(&format!("{} secret(s) applied to environment", bold(&count.to_string())));
}
}
}
Ok(())
}