pub mod service_registry;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use tokio::fs;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
#[serde(rename = "embeddingModel")]
pub embedding_model: String,
#[serde(rename = "embeddingDimensions")]
pub embedding_dimensions: u32,
#[serde(rename = "vectorStore")]
pub vector_store: VectorStoreConfig,
#[serde(rename = "chunkSize")]
pub chunk_size: usize,
#[serde(rename = "searchTopK")]
pub search_top_k: usize,
#[serde(rename = "privacyLevel")]
pub privacy_level: String,
#[serde(rename = "backendMapping")]
pub backend_mapping: bool,
pub encryption: crate::types::EncryptionConfig,
pub embedding: crate::types::EmbeddingConfig,
pub privacy: PrivacyConfig,
pub iam: IAMConfig,
#[serde(rename = "s3Config")]
pub s3_config: S3Config,
#[serde(rename = "azureBlobConfig")]
pub azure_blob_config: AzureBlobConfig,
#[serde(rename = "gcsConfig")]
pub gcs_config: GcsConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VectorStoreConfig {
pub backend: String,
pub connection: crate::types::QdrantConnectionConfig,
#[serde(rename = "storagePath")]
pub storage_path: Option<String>,
#[serde(rename = "serverSyncUrl")]
pub server_sync_url: Option<String>,
#[serde(rename = "enableServerSync")]
pub enable_server_sync: bool,
}
impl Default for VectorStoreConfig {
fn default() -> Self {
Self {
backend: "qdrant-embedded".to_string(),
connection: crate::types::QdrantConnectionConfig {
url: "http://localhost:6333".to_string(),
api_key: None,
timeout_secs: 30,
},
storage_path: Some("./qdrant-data".to_string()),
server_sync_url: Some("http://dev-qdrant-nlb-e8c337edc3ee861b.elb.ap-south-1.amazonaws.com:6334".to_string()),
enable_server_sync: true,
}
}
}
impl Default for Config {
fn default() -> Self {
Self {
embedding_model: "embaas/sentence-transformers-e5-large-v2".to_string(),
embedding_dimensions: 1024,
vector_store: VectorStoreConfig::default(),
chunk_size: 1024,
search_top_k: 10,
privacy_level: "minimal-aws".to_string(),
backend_mapping: false,
encryption: crate::types::EncryptionConfig::default(),
embedding: crate::types::EmbeddingConfig::default(),
privacy: PrivacyConfig::default(),
iam: IAMConfig::default(),
s3_config: S3Config::default(),
azure_blob_config: AzureBlobConfig::default(),
gcs_config: GcsConfig::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EncryptionConfig {
pub algorithm: String,
#[serde(rename = "enableContentEncryption")]
pub enable_content_encryption: bool,
#[serde(rename = "enableEmbeddingEncryption")]
pub enable_embedding_encryption: bool,
#[serde(rename = "enableMetadataEncryption")]
pub enable_metadata_encryption: bool,
#[serde(rename = "enableIdHashing")]
pub enable_id_hashing: bool,
#[serde(rename = "keyRotationDays")]
pub key_rotation_days: u32,
}
impl Default for EncryptionConfig {
fn default() -> Self {
Self {
algorithm: "AES-256-GCM".to_string(),
enable_content_encryption: false,
enable_embedding_encryption: false,
enable_metadata_encryption: false,
enable_id_hashing: false,
key_rotation_days: 90,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct S3Config {
pub enabled: bool,
pub bucket: String,
pub region: String,
#[serde(rename = "encryptionEnabled")]
pub encryption_enabled: bool,
#[serde(rename = "endpointUrl")]
pub endpoint_url: Option<String>,
}
impl Default for S3Config {
fn default() -> Self {
Self {
enabled: false,
bucket: String::new(),
region: "us-east-1".to_string(),
encryption_enabled: true,
endpoint_url: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AzureBlobConfig {
pub enabled: bool,
#[serde(rename = "containerName")]
pub container_name: String,
#[serde(rename = "encryptionEnabled")]
pub encryption_enabled: bool,
}
impl Default for AzureBlobConfig {
fn default() -> Self {
Self {
enabled: false,
container_name: String::new(),
encryption_enabled: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GcsConfig {
pub enabled: bool,
#[serde(rename = "bucketName")]
pub bucket_name: String,
#[serde(rename = "encryptionEnabled")]
pub encryption_enabled: bool,
}
impl Default for GcsConfig {
fn default() -> Self {
Self {
enabled: false,
bucket_name: String::new(),
encryption_enabled: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IAMConfig {
pub enable_iam_analysis: bool,
pub supported_services: Vec<String>,
pub risk_assessment: bool,
}
impl Default for IAMConfig {
fn default() -> Self {
Self {
enable_iam_analysis: true,
supported_services: vec![
"ec2".to_string(),
"rds".to_string(),
"s3".to_string(),
"lambda".to_string(),
],
risk_assessment: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PrivacyConfig {
pub level: String,
pub enable_data_filtering: bool,
pub confidential_fields: Vec<String>,
pub non_confidential_fields: Vec<String>,
}
impl Default for PrivacyConfig {
fn default() -> Self {
Self {
level: "minimal-aws".to_string(),
enable_data_filtering: true,
confidential_fields: vec![
"resource_id".to_string(),
"arn".to_string(),
"vpc_id".to_string(),
"security_groups".to_string(),
"iam_roles".to_string(),
],
non_confidential_fields: vec![
"account_id".to_string(),
"region".to_string(),
"service".to_string(),
"instance_type".to_string(),
"environment".to_string(),
],
}
}
}
#[derive(Clone)]
pub struct ConfigManager {
base_path: PathBuf,
config_path: PathBuf,
default_config: Config,
config: Config,
}
impl ConfigManager {
pub async fn new(base_path: &Path) -> Result<Self> {
let base_path = base_path.to_path_buf();
let config_path = base_path.join("config").join("config.yaml");
let default_config = Config::default();
let mut manager = Self {
base_path,
config_path,
default_config: default_config.clone(),
config: default_config,
};
manager.initialize().await?;
Ok(manager)
}
pub async fn initialize(&mut self) -> Result<bool> {
if let Some(parent) = self.config_path.parent() {
fs::create_dir_all(parent).await?;
}
self.load_config().await?;
Ok(true)
}
pub async fn load_config(&mut self) -> Result<()> {
if self.config_path.exists() {
let config_text = fs::read_to_string(&self.config_path).await?;
if let Ok(file_config) = serde_yaml::from_str::<Config>(&config_text) {
self.config = self.merge_configs(&self.default_config, &file_config);
} else if let Ok(file_config) = serde_json::from_str::<Config>(&config_text) {
self.config = self.merge_configs(&self.default_config, &file_config);
} else {
eprintln!("Failed to load config, using defaults");
self.config = self.default_config.clone();
self.save_config().await?;
}
} else {
self.config = self.default_config.clone();
self.save_config().await?;
}
Ok(())
}
pub async fn save_config(&self) -> Result<()> {
let config_text = serde_yaml::to_string(&self.config)?;
fs::write(&self.config_path, config_text).await?;
Ok(())
}
pub async fn update_config(&mut self, updates: Config) -> Result<()> {
self.config = self.merge_configs(&self.config, &updates);
self.save_config().await?;
Ok(())
}
pub fn get_config(&self) -> Config {
self.config.clone()
}
pub fn get(&self, key: &str) -> Option<serde_yaml::Value> {
let config_value = serde_yaml::to_value(&self.config).ok()?;
if let serde_yaml::Value::Mapping(map) = config_value {
map.get(&serde_yaml::Value::String(key.to_string())).cloned()
} else {
None
}
}
pub async fn set(&mut self, key: &str, value: serde_yaml::Value) -> Result<()> {
let mut config_value = serde_yaml::to_value(&self.config)?;
if let serde_yaml::Value::Mapping(ref mut map) = config_value {
map.insert(serde_yaml::Value::String(key.to_string()), value);
self.config = serde_yaml::from_value(config_value)?;
self.save_config().await?;
}
Ok(())
}
pub fn get_encryption_config(&self) -> &crate::types::EncryptionConfig {
&self.config.encryption
}
pub fn get_s3_config(&self) -> &S3Config {
&self.config.s3_config
}
pub fn get_azure_blob_config(&self) -> &AzureBlobConfig {
&self.config.azure_blob_config
}
pub fn get_gcs_config(&self) -> &GcsConfig {
&self.config.gcs_config
}
fn merge_configs(&self, base: &Config, updates: &Config) -> Config {
updates.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[tokio::test]
async fn test_config_manager_creation() {
let temp_dir = TempDir::new().unwrap();
let config_manager = ConfigManager::new(temp_dir.path()).await.unwrap();
let config = config_manager.get_config();
assert_eq!(config.embedding_model, "BAAI/bge-m3");
assert_eq!(config.embedding_dimensions, 1024);
assert_eq!(config.privacy_level, "minimal-aws");
}
#[tokio::test]
async fn test_config_save_load() {
let temp_dir = TempDir::new().unwrap();
let mut config_manager = ConfigManager::new(temp_dir.path()).await.unwrap();
let mut new_config = Config::default();
new_config.embedding_model = "test-model".to_string();
new_config.chunk_size = 2048;
config_manager.update_config(new_config).await.unwrap();
let new_config_manager = ConfigManager::new(temp_dir.path()).await.unwrap();
let config = new_config_manager.get_config();
assert_eq!(config.embedding_model, "test-model");
assert_eq!(config.chunk_size, 2048);
}
#[tokio::test]
async fn test_config_get_set() {
let temp_dir = TempDir::new().unwrap();
let mut config_manager = ConfigManager::new(temp_dir.path()).await.unwrap();
let chunk_size = config_manager.get("chunkSize");
assert!(chunk_size.is_some());
let new_value = serde_yaml::Value::Number(serde_yaml::Number::from(2048));
config_manager.set("chunkSize", new_value).await.unwrap();
let config = config_manager.get_config();
assert_eq!(config.chunk_size, 2048);
}
#[tokio::test]
async fn test_encryption_config() {
let temp_dir = TempDir::new().unwrap();
let config_manager = ConfigManager::new(temp_dir.path()).await.unwrap();
let encryption_config = config_manager.get_encryption_config();
assert_eq!(encryption_config.algorithm, "AES-256-GCM");
assert_eq!(encryption_config.enable_content_encryption, true);
assert_eq!(encryption_config.enable_metadata_encryption, true);
assert_eq!(encryption_config.key_rotation_days, Some(90));
}
#[tokio::test]
async fn test_cloud_configs() {
let temp_dir = TempDir::new().unwrap();
let config_manager = ConfigManager::new(temp_dir.path()).await.unwrap();
let s3_config = config_manager.get_s3_config();
assert_eq!(s3_config.enabled, false);
assert_eq!(s3_config.region, "us-east-1");
assert_eq!(s3_config.encryption_enabled, true);
let azure_config = config_manager.get_azure_blob_config();
assert_eq!(azure_config.enabled, false);
assert_eq!(azure_config.encryption_enabled, true);
let gcs_config = config_manager.get_gcs_config();
assert_eq!(gcs_config.enabled, false);
assert_eq!(gcs_config.encryption_enabled, true);
}
}