// src/common/mod.rs //! Общие модули для Futriix //! //! Содержит общие структуры данных, ошибки, протоколы и конфигурацию, //! используемые во всех компонентах системы с wait-free архитектурой. use thiserror::Error; use serde::{Deserialize, Serialize}; use std::fs; use std::path::Path; /// Основной тип ошибки для Futriix #[derive(Error, Debug)] pub enum FutriixError { #[error("Configuration error: {0}")] ConfigError(String), #[error("Database error: {0}")] DatabaseError(String), #[error("Lua error: {0}")] LuaError(String), #[error("Network error: {0}")] NetworkError(String), #[error("Replication error: {0}")] ReplicationError(String), #[error("HTTP error: {0}")] HttpError(String), #[error("Serialization error: {0}")] SerializationError(String), #[error("IO error: {0}")] IoError(#[from] std::io::Error), #[error("CSV error: {0}")] CsvError(String), #[error("Unknown error: {0}")] Unknown(String), } // Реализация преобразования из rlua::Error в FutriixError impl From for FutriixError { fn from(error: rlua::Error) -> Self { FutriixError::LuaError(error.to_string()) } } /// Тип результата для Futriix pub type Result = std::result::Result; // Модуль конфигурации pub mod config { use super::*; /// Конфигурация сервера #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Config { #[serde(default = "ServerConfig::default")] pub server: ServerConfig, #[serde(default = "ReplicationConfig::default")] pub replication: ReplicationConfig, #[serde(default = "ClusterConfig::default")] // Новая секция кластера pub cluster: ClusterConfig, #[serde(default = "LuaConfig::default")] pub lua: LuaConfig, #[serde(default = "AclConfig::default")] pub acl: AclConfig, #[serde(default = "TlsConfig::default")] pub tls: TlsConfig, #[serde(default = "CsvConfig::default")] pub csv: CsvConfig, } /// Конфигурация серверных параметров #[derive(Debug, Serialize, Deserialize, Clone)] pub struct ServerConfig { #[serde(default = "default_host")] pub host: String, #[serde(default = "default_port")] pub port: u16, #[serde(default)] pub http_port: Option, #[serde(default)] pub https_port: Option, #[serde(default)] pub http2_enabled: Option, #[serde(default = "default_http_enabled")] // Новая директива pub http: bool, #[serde(default = "default_https_enabled")] // Новая директива pub https: bool, } /// Конфигурация репликации #[derive(Debug, Serialize, Deserialize, Clone)] pub struct ReplicationConfig { #[serde(default)] pub enabled: bool, #[serde(default = "default_master_nodes")] pub master_nodes: Vec, #[serde(default = "default_sync_interval")] pub sync_interval: u64, } /// Конфигурация кластера #[derive(Debug, Serialize, Deserialize, Clone)] pub struct ClusterConfig { #[serde(default)] pub enabled: bool, #[serde(default = "default_cluster_name")] pub name: String, } /// Конфигурация Lua #[derive(Debug, Serialize, Deserialize, Clone)] pub struct LuaConfig { #[serde(default = "default_scripts_dir")] pub scripts_dir: String, #[serde(default)] pub auto_execute: Vec, } /// Конфигурация ACL #[derive(Debug, Serialize, Deserialize, Clone)] pub struct AclConfig { #[serde(default)] pub enabled: bool, #[serde(default = "default_allowed_ips")] pub allowed_ips: Vec, #[serde(default)] pub denied_ips: Vec, } /// Конфигурация TLS #[derive(Debug, Serialize, Deserialize, Clone)] pub struct TlsConfig { #[serde(default)] pub enabled: bool, #[serde(default = "default_cert_path")] pub cert_path: String, #[serde(default = "default_key_path")] pub key_path: String, } /// Конфигурация CSV #[derive(Debug, Serialize, Deserialize, Clone)] pub struct CsvConfig { #[serde(default = "default_csv_import_dir")] pub import_dir: String, #[serde(default = "default_csv_export_dir")] pub export_dir: String, #[serde(default = "default_max_csv_file_size")] pub max_file_size: u64, } // Функции для значений по умолчанию fn default_host() -> String { "127.0.0.1".to_string() } fn default_port() -> u16 { 8081 } fn default_http_enabled() -> bool { true } fn default_https_enabled() -> bool { false } fn default_master_nodes() -> Vec { vec!["127.0.0.1:8081".to_string(), "127.0.0.1:8083".to_string()] } fn default_sync_interval() -> u64 { 5000 } fn default_cluster_name() -> String { "futriix-default-cluster".to_string() } fn default_scripts_dir() -> String { "lua_scripts".to_string() } fn default_allowed_ips() -> Vec { vec!["127.0.0.1".to_string(), "::1".to_string()] } fn default_cert_path() -> String { "certs/cert.pem".to_string() } fn default_key_path() -> String { "certs/key.pem".to_string() } fn default_csv_import_dir() -> String { "/futriix/csv/import".to_string() } fn default_csv_export_dir() -> String { "/futriix/csv/export".to_string() } fn default_max_csv_file_size() -> u64 { 104857600 // 100MB } impl Default for ServerConfig { fn default() -> Self { Self { host: default_host(), port: default_port(), http_port: Some(8082), https_port: None, http2_enabled: Some(false), http: default_http_enabled(), https: default_https_enabled(), } } } impl Default for ReplicationConfig { fn default() -> Self { Self { enabled: false, master_nodes: default_master_nodes(), sync_interval: default_sync_interval(), } } } impl Default for ClusterConfig { fn default() -> Self { Self { enabled: false, name: default_cluster_name(), } } } impl Default for LuaConfig { fn default() -> Self { Self { scripts_dir: default_scripts_dir(), auto_execute: vec!["init.lua".to_string()], } } } impl Default for AclConfig { fn default() -> Self { Self { enabled: false, allowed_ips: default_allowed_ips(), denied_ips: vec![], } } } impl Default for TlsConfig { fn default() -> Self { Self { enabled: false, cert_path: default_cert_path(), key_path: default_key_path(), } } } impl Default for CsvConfig { fn default() -> Self { Self { import_dir: default_csv_import_dir(), export_dir: default_csv_export_dir(), max_file_size: default_max_csv_file_size(), } } } impl Default for Config { fn default() -> Self { Self { server: ServerConfig::default(), replication: ReplicationConfig::default(), cluster: ClusterConfig::default(), lua: LuaConfig::default(), acl: AclConfig::default(), tls: TlsConfig::default(), csv: CsvConfig::default(), } } } impl Config { /// Загрузка конфигурации из файла pub fn load(path: &str) -> Result { let path = Path::new(path); if !path.exists() { // Создание конфигурации по умолчанию let default_config = Config::default(); let toml_content = toml::to_string_pretty(&default_config) .map_err(|e| FutriixError::ConfigError(e.to_string()))?; fs::write(path, toml_content) .map_err(|e| FutriixError::ConfigError(e.to_string()))?; println!("Created default configuration file: {}", path.display()); return Ok(default_config); } let content = fs::read_to_string(path) .map_err(|e| FutriixError::ConfigError(e.to_string()))?; // Парсим конфигурацию с использованием значений по умолчанию let mut config: Config = toml::from_str(&content) .map_err(|e| FutriixError::ConfigError(e.to_string()))?; // Убеждаемся, что все поля имеют значения по умолчанию, если они отсутствуют if config.server.host.is_empty() { config.server.host = default_host(); } if config.server.port == 0 { config.server.port = default_port(); } if config.replication.master_nodes.is_empty() { config.replication.master_nodes = default_master_nodes(); } if config.replication.sync_interval == 0 { config.replication.sync_interval = default_sync_interval(); } if config.cluster.name.is_empty() { config.cluster.name = default_cluster_name(); } if config.lua.scripts_dir.is_empty() { config.lua.scripts_dir = default_scripts_dir(); } if config.acl.allowed_ips.is_empty() { config.acl.allowed_ips = default_allowed_ips(); } if config.tls.cert_path.is_empty() { config.tls.cert_path = default_cert_path(); } if config.tls.key_path.is_empty() { config.tls.key_path = default_key_path(); } if config.csv.import_dir.is_empty() { config.csv.import_dir = default_csv_import_dir(); } if config.csv.export_dir.is_empty() { config.csv.export_dir = default_csv_export_dir(); } if config.csv.max_file_size == 0 { config.csv.max_file_size = default_max_csv_file_size(); } Ok(config) } /// Сохранение конфигурации в файл #[allow(dead_code)] pub fn save(&self, path: &str) -> Result<()> { let toml_content = toml::to_string_pretty(self) .map_err(|e| FutriixError::ConfigError(e.to_string()))?; fs::write(path, toml_content) .map_err(|e| FutriixError::ConfigError(e.to_string())) } } } // Модуль протокола pub mod protocol { use serde::{Deserialize, Serialize}; /// Команды для выполнения в базе данных #[derive(Debug, Clone, Serialize, Deserialize)] pub enum Command { // Базовые CRUD команды Create { collection: String, document: Vec, }, Read { collection: String, id: String, }, Update { collection: String, id: String, document: Vec, }, Delete { collection: String, id: String, }, Query { collection: String, filter: Vec, }, // Команды для процедур и транзакций CreateProcedure { name: String, code: Vec, }, CallProcedure { name: String, }, BeginTransaction { transaction_id: String, }, CommitTransaction { transaction_id: String, }, RollbackTransaction { transaction_id: String, }, // Команды для индексов CreateIndex { collection: String, index: crate::server::database::Index, }, QueryByIndex { collection: String, index_name: String, value: Vec, }, // Команды для шардинга с Raft AddShardNode { node_id: String, address: String, capacity: u64, }, RemoveShardNode { node_id: String, }, MigrateShard { collection: String, from_node: String, to_node: String, shard_key: String, }, RebalanceCluster, GetClusterStatus, StartElection, // Новая команда для Raft выборов GetRaftNodes, // Новая команда для получения Raft узлов // Команды для constraints AddConstraint { collection: String, constraint_name: String, constraint_type: String, field: String, value: Vec, }, RemoveConstraint { collection: String, constraint_name: String, }, // Команды для компрессии EnableCompression { collection: String, algorithm: String, }, DisableCompression { collection: String, }, // Команды для глобальных индексов CreateGlobalIndex { name: String, field: String, unique: bool, }, QueryGlobalIndex { index_name: String, value: Vec, }, // Новые команды для CSV импорта/экспорта ImportCsv { collection: String, file_path: String, }, ExportCsv { collection: String, file_path: String, }, ListCsvFiles, GetImportProgress { collection: String, }, } /// Ответы от базы данных #[derive(Debug, Clone, Serialize, Deserialize)] pub enum Response { Success(Vec), Error(String), } /// Сообщение для репликации #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ReplicationMessage { pub sequence: u64, pub command: Command, pub timestamp: i64, } /// Структура для информации о шарде #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ShardInfo { pub node_id: String, pub address: String, pub capacity: u64, pub used: u64, pub collections: Vec, } /// Структура для информации о Raft узле #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RaftNodeInfo { pub node_id: String, pub address: String, pub state: String, // "leader", "follower", "candidate" pub term: u64, pub last_heartbeat: i64, } /// Структура для статуса кластера #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ClusterStatus { pub nodes: Vec, pub total_capacity: u64, pub total_used: u64, pub rebalance_needed: bool, pub cluster_formed: bool, // Новое поле: собран ли кластер pub leader_exists: bool, // Новое поле: существует ли лидер pub raft_nodes: Vec, // Новое поле: список Raft узлов } /// Wait-Free сериализация сообщений pub fn serialize(value: &T) -> crate::common::Result> { rmp_serde::to_vec(value) .map_err(|e| crate::common::FutriixError::SerializationError(e.to_string())) } /// Wait-Free десериализация сообщений pub fn deserialize<'a, T: serde::Deserialize<'a>>(bytes: &'a [u8]) -> crate::common::Result { rmp_serde::from_slice(bytes) .map_err(|e| crate::common::FutriixError::SerializationError(e.to_string())) } }