#[cfg(feature = "upload")]
use anyhow::{Context, Result};
#[cfg(feature = "upload")]
use files_sdk::{FileHandler, FilesClient};
#[cfg(feature = "upload")]
use redisctl_core::Config;
#[cfg(feature = "upload")]
pub async fn upload_package(
api_key: &str,
package_data: &[u8],
filename: &str,
remote_path: Option<&str>,
) -> Result<String> {
let client = FilesClient::builder()
.api_key(api_key)
.build()
.context("Failed to create Files.com client")?;
let handler = FileHandler::new(client);
let upload_path = remote_path.unwrap_or("/RLEC_Customers/Uploads");
let full_path = format!("{}/{}", upload_path, filename);
println!("Uploading to Files.com: {}", full_path);
println!("Size: {} bytes", package_data.len());
let file = handler
.upload_file(&full_path, package_data)
.await
.context("Failed to upload file to Files.com")?;
Ok(file.path.unwrap_or_else(|| full_path.clone()))
}
#[cfg(feature = "upload")]
pub fn get_files_api_key(profile_name: Option<&str>) -> Result<String> {
if let Ok(key) = std::env::var("REDIS_ENTERPRISE_FILES_API_KEY") {
return Ok(key);
}
if let Ok(config) = Config::load() {
if let Some(profile_name) = profile_name
&& let Some(profile) = config.profiles.get(profile_name)
&& let Some(key) = &profile.files_api_key
{
return resolve_config_key(key);
}
if let Some(key) = &config.files_api_key {
return resolve_config_key(key);
}
}
#[cfg(feature = "secure-storage")]
{
if let Ok(key) = get_from_keyring() {
return Ok(key);
}
}
if let Ok(key) = std::env::var("REDIS_FILES_API_KEY") {
return Ok(key);
}
#[cfg(feature = "secure-storage")]
let error_msg = "Files.com API key not found. Options:\n\
1. Set REDIS_ENTERPRISE_FILES_API_KEY environment variable\n\
2. Store securely: redisctl files-key set <key> --use-keyring\n\
3. Add to config: files_api_key = \"...\" (global) or per-profile\n\
4. Set REDIS_FILES_API_KEY environment variable (fallback)";
#[cfg(not(feature = "secure-storage"))]
let error_msg = "Files.com API key not found. Options:\n\
1. Set REDIS_ENTERPRISE_FILES_API_KEY environment variable\n\
2. Add to config: files_api_key = \"...\" (global) or per-profile\n\
3. Set REDIS_FILES_API_KEY environment variable (fallback)";
anyhow::bail!(error_msg)
}
#[cfg(feature = "upload")]
fn resolve_config_key(key: &str) -> Result<String> {
if let Some(keyring_key) = key.strip_prefix("keyring:") {
#[cfg(feature = "secure-storage")]
{
let entry = keyring::Entry::new("redisctl", keyring_key)
.context("Failed to access system keyring")?;
entry
.get_password()
.context(format!("Failed to retrieve '{}' from keyring", keyring_key))
}
#[cfg(not(feature = "secure-storage"))]
anyhow::bail!(
"Config references keyring ('{}'), but secure-storage feature not enabled",
key
)
} else {
Ok(key.to_string())
}
}
#[cfg(all(feature = "upload", feature = "secure-storage"))]
pub fn set_in_keyring(api_key: &str) -> Result<()> {
let entry = keyring::Entry::new("redisctl", "files-api-key")
.context("Failed to access system keyring")?;
entry
.set_password(api_key)
.context("Failed to store API key in keyring")?;
Ok(())
}
#[cfg(all(feature = "upload", feature = "secure-storage"))]
pub fn get_from_keyring() -> Result<String> {
let entry = keyring::Entry::new("redisctl", "files-api-key")
.context("Failed to access system keyring")?;
entry.get_password().context("API key not found in keyring")
}
#[cfg(all(feature = "upload", feature = "secure-storage"))]
pub fn delete_from_keyring() -> Result<()> {
let entry = keyring::Entry::new("redisctl", "files-api-key")
.context("Failed to access system keyring")?;
entry
.delete_credential()
.context("Failed to delete API key from keyring")?;
Ok(())
}