use clap::Parser;
use keyflux::{cli::Cli, ConfigError, KeyManager};
use keyflux::config::KeyFluxConfig;
use log::{error, info, warn, trace};
use tokio;
use std::path::Path;
use sodiumoxide;
use std::collections::HashMap;
use std::{env};
use std::fs::{self};
use keyflux::config::EnvConfig;
use keyflux::flux::file::FileFlux;
use keyflux::flux::vercel::VercelEnvironmentFlux;
use keyflux::flux::supabase_vault::SupabaseVaultFlux;
use keyflux::_rust_i18n_translate;
fn load_env_from_json(file: &str) {
info!("{}", t!("loading.file", file = file));
let data = fs::read_to_string(file).expect(&t!("error.unable_read_file"));
let vars: HashMap<String, String> = serde_json::from_str(&data).expect(&t!("error.json_format"));
for (key, value) in vars {
std::env::set_var(key, value);
}
}
fn load_env_from_yaml(file: &str) {
let data = fs::read_to_string(file).expect(&t!("error.unable_read_file"));
let vars: HashMap<String, String> = serde_yaml::from_str(&data).expect(&t!("error.yaml_format"));
for (key, value) in vars {
std::env::set_var(key, value);
}
}
fn load_env_from_toml(file: &str) {
let data = fs::read_to_string(file).expect(&t!("error.unable_read_file"));
let vars: HashMap<String, String> = toml::from_str(&data).expect(&t!("error.toml_format"));
for (key, value) in vars {
std::env::set_var(key, value);
}
}
#[macro_use]
extern crate rust_i18n;
fn process_env_config(env_config: &EnvConfig) {
trace!("{}", t!("trace.env_config", env_config = format!("{:?}", env_config)));
match env_config {
EnvConfig::Variable { name, value } => {
info!("{}", t!("info.setting_env_var", name = name));
env::set_var(name, value);
}
EnvConfig::File(file) => {
trace!("{}", t!("trace.loading_env_file", file = file));
match Path::new(file).extension().and_then(|s| s.to_str()) {
Some("json") => load_env_from_json(file),
Some("yaml") | Some("yml") => load_env_from_yaml(file),
Some("toml") => load_env_from_toml(file),
Some("env") => {
if dotenv::from_filename(file).is_err() {
error!("{}", t!("error.failed_load_dotenv", file = file));
std::process::exit(1);
}
}
_ => warn!("{}", t!("warn.unsupported_file_format", file = file)),
}
}
}
}
use keyflux::flux_registry::FLUX_REGISTRY;
static VERCEL: &str = "vercel";
static FILE: &str = "file";
static SUPABASE: &str = "supabase";
static SUPABASE_VAULT: &str = "supabase_vault";
static GITHUB: &str = "github";
fn register_all_fluxes() {
let mut registry = FLUX_REGISTRY.lock().unwrap();
registry.register(VERCEL, Box::new(|config| {
trace!("{}", t!("trace.creating_vercel_flux", config = format!("{:?}", config)));
let flux: VercelEnvironmentFlux = serde_json::from_value(config.clone())
.map_err(|e| ConfigError::InvalidConfig(format!("{}", t!("error.parsing_vercel_flux", error = format!("{:?}", e)))))?;
Ok(Box::new(flux))
}));
registry.register(FILE, Box::new(|config| {
trace!("{}", t!("trace.creating_file_flux", config = format!("{:?}", config)));
let flux: FileFlux = serde_json::from_value(config.clone())
.map_err(|e| ConfigError::InvalidConfig(format!("{}", t!("error.parsing_file_flux", error = format!("{:?}", e)))))?;
Ok(Box::new(flux))
}));
registry.register(SUPABASE, Box::new(|config| {
trace!("{}", t!("trace.creating_supabase_flux", config = format!("{:?}", config)));
let flux: SupabaseFlux = serde_json::from_value(config.clone())
.map_err(|e| ConfigError::InvalidConfig(format!("{}", t!("error.parsing_supabase_flux", error = format!("{:?}", e)))))?;
Ok(Box::new(flux))
}));
registry.register(SUPABASE_VAULT, Box::new(|config| {
trace!("{}", t!("trace.creating_supabase_vault_flux", config = format!("{:?}", config)));
let flux: SupabaseVaultFlux = serde_json::from_value(config.clone())
.map_err(|e| ConfigError::InvalidConfig(format!("{}", t!("error.parsing_supabase_vault_flux", error = format!("{:?}", e)))))?;
Ok(Box::new(flux))
}));
registry.register(GITHUB, Box::new(|config| {
trace!("{}", t!("trace.creating_github_flux", config = format!("{:?}", config)));
let flux: GitHubFlux = serde_json::from_value(config.clone())
.map_err(|e| ConfigError::InvalidConfig(format!("{}", t!("error.parsing_github_flux", error = format!("{:?}", e)))))?;
Ok(Box::new(flux))
}));
}
use env_logger::fmt::{WriteStyle};
use keyflux::flux::github::GitHubFlux;
use keyflux::flux::supabase::SupabaseFlux;
#[tokio::main]
async fn main() {
let cli = Cli::parse();
rust_i18n::set_locale(cli.locale.clone().into());
register_all_fluxes();
env_logger::Builder::new()
.write_style(WriteStyle::Always)
.filter_level(cli.log_level.clone().into())
.init();
sodiumoxide::init().expect(&t!("error.sodiumoxide_init"));
trace!("{}", t!("trace.parsed_cli_args", cli = format!("{:?}", cli)));
if let Some(env_files) = cli.env_files {
for env_file in env_files.split(',') {
trace!("{}", t!("trace.loading_env_file", file = env_file));
match Path::new(env_file).extension().and_then(|s| s.to_str()) {
Some("json") => load_env_from_json(env_file),
Some("yaml") | Some("yml") => load_env_from_yaml(env_file),
Some("toml") => load_env_from_toml(env_file),
Some("env") => {
if dotenv::from_filename(env_file).is_err() {
error!("{}", t!("error.failed_load_dotenv", file = env_file));
std::process::exit(1);
}
}
_ => warn!("{}", t!("warn.unsupported_file_format", file = env_file)),
}
}
}
trace!("{}", t!("trace.loading_config_file", file = format!("{:?}", cli.config_file)));
let config = KeyFluxConfig::from_file(&cli.config_file).unwrap_or_else(|e| {
error!("{}", t!("error.load_config", error = format!("{:?}", e)));
std::process::exit(1);
});
if let Some(env_configs) = &config.env {
for env_config in env_configs {
process_env_config(env_config);
}
}
trace!("{}", t!("trace.loaded_config"));
let mut manager = KeyManager::new(config);
trace!("{}", t!("trace.starting_sync"));
if let Err(e) = manager.flux_keys().await {
error!("{}", t!("error.sync_secrets", error = format!("{:?}", e.to_string())));
std::process::exit(1);
}
info!("{}", t!("info.secrets_synced"));
}