//! Конфигурационный модуль для flusql //! //! Этот модуль отвечает за загрузку и управление конфигурацией //! сервера flusql из файла config.toml и переменных окружения. use serde::{Deserialize, Serialize}; use std::fs; use std::path::Path; use thiserror::Error; /// Основная конфигурация flusql #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Config { /// Общие настройки сервера #[serde(default = "default_server_config")] pub server: ServerConfig, /// Настройки базы данных #[serde(default = "default_database_config")] pub database: DatabaseConfig, /// Настройки логгера #[serde(default = "default_logging_config")] pub logging: LoggingConfig, /// Настройки Lua интерпретатора #[serde(default = "default_lua_config")] pub lua: LuaConfig, /// Настройки кластера #[serde(default = "default_cluster_config")] pub cluster: ClusterConfig, /// Настройки плагинов #[serde(default = "default_plugins_config")] pub plugins: PluginsConfig, /// Настройки HTTP сервера (если включен) #[serde(default = "default_http_config")] pub http: HttpConfig, /// Настройки репликации #[serde(default = "default_replication_config")] pub replication: ReplicationConfig, /// Настройки сети #[serde(default = "default_network_config")] pub network: NetworkConfig, } /// Конфигурация сети #[derive(Debug, Clone, Serialize, Deserialize)] pub struct NetworkConfig { /// IP-адрес для прослушивания #[serde(default = "default_network_host")] pub host: String, /// Порт для прослушивания #[serde(default = "default_network_port")] pub port: u16, /// Разрешить удаленные подключения #[serde(default = "default_allow_remote")] pub allow_remote: bool, /// Таймаут соединения в секундах #[serde(default = "default_connection_timeout")] pub connection_timeout: u64, /// Максимальное количество соединений #[serde(default = "default_max_connections")] pub max_connections: u32, /// Размер буфера для сетевых операций в байтах #[serde(default = "default_buffer_size")] pub buffer_size: usize, } /// Значения по умолчанию для NetworkConfig fn default_network_config() -> NetworkConfig { NetworkConfig { host: default_network_host(), port: default_network_port(), allow_remote: default_allow_remote(), connection_timeout: default_connection_timeout(), max_connections: default_max_connections(), buffer_size: default_buffer_size(), } } fn default_network_host() -> String { "127.0.0.1".to_string() } fn default_network_port() -> u16 { 8080 } fn default_allow_remote() -> bool { false } fn default_connection_timeout() -> u64 { 30 } fn default_max_connections() -> u32 { 100 } fn default_buffer_size() -> usize { 8192 } /// Конфигурация плагинов #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PluginsConfig { /// Включена ли система плагинов #[serde(default = "default_plugins_enabled")] pub enabled: bool, /// Директория для плагинов #[serde(default = "default_plugins_dir")] pub plugins_dir: String, /// Включить изоляцию плагинов (sandbox) #[serde(default = "default_sandbox_enabled")] pub sandbox_enabled: bool, /// Максимальное количество плагинов #[serde(default = "default_max_plugins")] pub max_plugins: usize, /// Автозагрузка плагинов при старте #[serde(default = "default_auto_load")] pub auto_load: bool, /// Включить горячую перезагрузку плагинов #[serde(default = "default_hot_reload")] pub hot_reload: bool, /// Таймаут выполнения плагина в секундах #[serde(default = "default_plugin_timeout")] pub plugin_timeout_sec: u64, /// Максимальный размер памяти плагина в МБ #[serde(default = "default_max_memory_mb")] pub max_memory_mb: u64, /// Разрешенные API для плагинов #[serde(default = "default_allowed_apis")] pub allowed_apis: Vec, /// Запрещенные функции Lua #[serde(default = "default_blocked_functions")] pub blocked_functions: Vec, } /// Значения по умолчанию для PluginsConfig fn default_plugins_config() -> PluginsConfig { PluginsConfig { enabled: default_plugins_enabled(), plugins_dir: default_plugins_dir(), sandbox_enabled: default_sandbox_enabled(), max_plugins: default_max_plugins(), auto_load: default_auto_load(), hot_reload: default_hot_reload(), plugin_timeout_sec: default_plugin_timeout(), max_memory_mb: default_max_memory_mb(), allowed_apis: default_allowed_apis(), blocked_functions: default_blocked_functions(), } } fn default_plugins_enabled() -> bool { true } fn default_plugins_dir() -> String { "./plugins".to_string() } fn default_sandbox_enabled() -> bool { true } fn default_max_plugins() -> usize { 50 } fn default_auto_load() -> bool { true } fn default_hot_reload() -> bool { false } fn default_plugin_timeout() -> u64 { 30 } fn default_max_memory_mb() -> u64 { 100 } fn default_allowed_apis() -> Vec { vec![ "database".to_string(), "table".to_string(), "query".to_string(), "index".to_string(), "event".to_string(), "log".to_string(), ] } fn default_blocked_functions() -> Vec { vec![ "io.popen".to_string(), "os.execute".to_string(), "os.exit".to_string(), "debug.debug".to_string(), "debug.getregistry".to_string(), "debug.setmetatable".to_string(), ] } /// Конфигурация HTTP сервера #[derive(Debug, Clone, Serialize, Deserialize)] pub struct HttpConfig { /// Включен ли HTTP сервер pub enabled: bool, /// Хост для HTTP сервера #[serde(default = "default_http_host")] pub host: String, /// Порт HTTP сервера #[serde(default = "default_http_port")] pub port: u16, /// Порт HTTPS сервера #[serde(default = "default_https_port")] pub https_port: u16, /// Включена ли поддержка HTTP/2 #[serde(default)] pub http2_enabled: bool, /// Включена ли поддержка TLS #[serde(default)] pub tls_enabled: bool, /// Путь к сертификату TLS #[serde(default)] pub tls_cert_path: Option, /// Путь к приватному ключу TLS #[serde(default)] pub tls_key_path: Option, } /// Значения по умолчанию для HttpConfig fn default_http_config() -> HttpConfig { HttpConfig { enabled: false, host: default_http_host(), port: default_http_port(), https_port: default_https_port(), http2_enabled: false, tls_enabled: false, tls_cert_path: None, tls_key_path: None, } } fn default_http_host() -> String { "127.0.0.1".to_string() } fn default_http_port() -> u16 { 8080 } fn default_https_port() -> u16 { 8443 } /// Конфигурация репликации #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ReplicationConfig { /// Включена ли репликация pub enabled: bool, /// Режим репликации #[serde(default = "default_replication_mode")] pub mode: String, /// Мастер-сервер для репликации #[serde(default)] pub master: Option, /// Список слейв-серверов #[serde(default)] pub slaves: Vec, } /// Значения по умолчанию для ReplicationConfig fn default_replication_config() -> ReplicationConfig { ReplicationConfig { enabled: false, mode: default_replication_mode(), master: None, slaves: vec![], } } fn default_replication_mode() -> String { "async".to_string() } /// Конфигурация сервера #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ServerConfig { /// Порт сервера #[serde(default = "default_server_port")] pub port: u16, /// Хост сервера #[serde(default = "default_server_host")] pub host: String, /// Максимальное количество одновременных соединений #[serde(default = "default_server_max_connections")] pub max_connections: u32, /// Таймаут соединения в секундах #[serde(default = "default_server_timeout")] pub timeout: u64, /// Размер пула потоков #[serde(default = "default_thread_pool_size")] pub thread_pool_size: usize, /// Включить отладку #[serde(default = "default_debug_enabled")] pub debug: bool, /// Путь к PID файлу #[serde(default)] pub pid_file: Option, } /// Значения по умолчанию для ServerConfig fn default_server_config() -> ServerConfig { ServerConfig { port: default_server_port(), host: default_server_host(), max_connections: default_server_max_connections(), timeout: default_server_timeout(), thread_pool_size: default_thread_pool_size(), debug: default_debug_enabled(), pid_file: None, } } fn default_server_port() -> u16 { 5432 } fn default_server_host() -> String { "127.0.0.1".to_string() } fn default_server_max_connections() -> u32 { 100 } fn default_server_timeout() -> u64 { 30 } fn default_thread_pool_size() -> usize { 4 } fn default_debug_enabled() -> bool { false } /// Конфигурация базы данных #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DatabaseConfig { /// Директория для хранения данных #[serde(default = "default_data_dir")] pub data_dir: String, /// Автоматически создавать базу данных при первом подключении #[serde(default = "default_auto_create")] pub auto_create: bool, /// Режим транзакций #[serde(default = "default_transaction_mode")] pub transaction_mode: String, /// Размер кэша в МБ #[serde(default = "default_cache_size")] pub cache_size_mb: u64, /// Размер страницы в байтах #[serde(default = "default_page_size")] pub page_size: u32, /// Включить MVCC (Multi-Version Concurrency Control) #[serde(default = "default_mvcc_enabled")] pub mvcc_enabled: bool, /// Включить WAL (Write-Ahead Logging) #[serde(default = "default_wal_enabled")] pub wal_enabled: bool, /// Максимальный размер WAL в МБ #[serde(default = "default_max_wal_size")] pub max_wal_size_mb: u64, /// Автоматическая проверка целостности при запуске #[serde(default = "default_integrity_check")] pub integrity_check: bool, /// Частота автоматического сохранения в секундах #[serde(default = "default_auto_save_interval")] pub auto_save_interval: u64, /// Максимальное количество открытых файлов БД #[serde(default = "default_max_open_files")] pub max_open_files: u32, } /// Значения по умолчанию для DatabaseConfig fn default_database_config() -> DatabaseConfig { DatabaseConfig { data_dir: default_data_dir(), auto_create: default_auto_create(), transaction_mode: default_transaction_mode(), cache_size_mb: default_cache_size(), page_size: default_page_size(), mvcc_enabled: default_mvcc_enabled(), wal_enabled: default_wal_enabled(), max_wal_size_mb: default_max_wal_size(), integrity_check: default_integrity_check(), auto_save_interval: default_auto_save_interval(), max_open_files: default_max_open_files(), } } fn default_data_dir() -> String { "./data".to_string() } fn default_auto_create() -> bool { true } fn default_transaction_mode() -> String { "write_ahead_log".to_string() } fn default_cache_size() -> u64 { 100 } fn default_page_size() -> u32 { 8192 } // 8KB страницы по умолчанию fn default_mvcc_enabled() -> bool { true } fn default_wal_enabled() -> bool { true } fn default_max_wal_size() -> u64 { 100 } fn default_integrity_check() -> bool { true } fn default_auto_save_interval() -> u64 { 60 } // 60 секунд fn default_max_open_files() -> u32 { 1000 } /// Конфигурация логгера #[derive(Debug, Clone, Serialize, Deserialize)] pub struct LoggingConfig { /// Уровень логирования #[serde(default = "default_log_level")] pub level: String, /// Путь к файлу логов #[serde(default = "default_log_path")] pub log_file: String, /// Максимальный размер файла логов в МБ #[serde(default = "default_max_log_size")] pub max_size_mb: u64, /// Количество ротируемых файлов #[serde(default = "default_backup_count")] pub backup_count: u32, /// Формат логов #[serde(default = "default_log_format")] pub format: String, /// Включить логирование в stdout #[serde(default = "default_stdout_enabled")] pub stdout_enabled: bool, /// Включить логирование в stderr #[serde(default = "default_stderr_enabled")] pub stderr_enabled: bool, /// Включить логирование SQL запросов #[serde(default = "default_sql_logging")] pub sql_logging: bool, /// Включить медленный лог (запросы дольше N секунд) #[serde(default)] pub slow_query_threshold_sec: Option, } /// Значения по умолчанию для LoggingConfig fn default_logging_config() -> LoggingConfig { LoggingConfig { level: default_log_level(), log_file: default_log_path(), max_size_mb: default_max_log_size(), backup_count: default_backup_count(), format: default_log_format(), stdout_enabled: default_stdout_enabled(), stderr_enabled: default_stderr_enabled(), sql_logging: default_sql_logging(), slow_query_threshold_sec: None, } } fn default_log_level() -> String { "info".to_string() } fn default_log_path() -> String { "flusql.log".to_string() } fn default_max_log_size() -> u64 { 10 } fn default_backup_count() -> u32 { 5 } fn default_log_format() -> String { "json".to_string() } fn default_stdout_enabled() -> bool { true } fn default_stderr_enabled() -> bool { false } fn default_sql_logging() -> bool { true } /// Конфигурация Lua интерпретатора #[derive(Debug, Clone, Serialize, Deserialize)] pub struct LuaConfig { /// Включен ли Lua интерпретатор #[serde(default = "default_lua_enabled")] pub enabled: bool, /// Путь к директории со скриптами #[serde(default = "default_lua_scripts_dir")] pub scripts_dir: String, /// Максимальное время выполнения скрипта в секундах #[serde(default = "default_lua_timeout")] pub timeout_seconds: u64, /// Максимальная память для Lua VM в МБ #[serde(default = "default_lua_memory_limit")] pub memory_limit_mb: u64, /// Разрешить доступ к файловой системе #[serde(default = "default_lua_filesystem_access")] pub filesystem_access: bool, /// Разрешить сетевые операции #[serde(default = "default_lua_network_access")] pub network_access: bool, /// Список разрешенных модулей #[serde(default = "default_lua_allowed_modules")] pub allowed_modules: Vec, } /// Значения по умолчанию для LuaConfig fn default_lua_config() -> LuaConfig { LuaConfig { enabled: default_lua_enabled(), scripts_dir: default_lua_scripts_dir(), timeout_seconds: default_lua_timeout(), memory_limit_mb: default_lua_memory_limit(), filesystem_access: default_lua_filesystem_access(), network_access: default_lua_network_access(), allowed_modules: default_lua_allowed_modules(), } } fn default_lua_enabled() -> bool { true } fn default_lua_scripts_dir() -> String { "./lua-scripts".to_string() } fn default_lua_timeout() -> u64 { 30 } fn default_lua_memory_limit() -> u64 { 100 } fn default_lua_filesystem_access() -> bool { false } fn default_lua_network_access() -> bool { false } fn default_lua_allowed_modules() -> Vec { vec![ "string".to_string(), "table".to_string(), "math".to_string(), "os".to_string(), ] } /// Конфигурация кластера #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ClusterConfig { /// Включен ли режим кластера #[serde(default = "default_cluster_enabled")] pub enabled: bool, /// Идентификатор узла #[serde(default = "default_node_id")] pub node_id: String, /// Адрес узла #[serde(default = "default_node_address")] pub node_address: String, /// Режим кластера #[serde(default = "default_cluster_mode")] pub mode: String, /// Список узлов кластера #[serde(default)] pub nodes: Vec, /// Интервал heartbeat в секундах #[serde(default = "default_heartbeat_interval")] pub heartbeat_interval: u64, /// Таймаут heartbeat в секундах #[serde(default = "default_heartbeat_timeout")] pub heartbeat_timeout: u64, /// Включить автоматическое восстановление #[serde(default = "default_auto_recovery")] pub auto_recovery: bool, /// Максимальное количество реплик #[serde(default = "default_max_replicas")] pub max_replicas: u32, } /// Значения по умолчанию для ClusterConfig fn default_cluster_config() -> ClusterConfig { ClusterConfig { enabled: default_cluster_enabled(), node_id: default_node_id(), node_address: default_node_address(), mode: default_cluster_mode(), nodes: vec![], heartbeat_interval: default_heartbeat_interval(), heartbeat_timeout: default_heartbeat_timeout(), auto_recovery: default_auto_recovery(), max_replicas: default_max_replicas(), } } fn default_cluster_enabled() -> bool { false } fn default_node_id() -> String { "node_1".to_string() } fn default_node_address() -> String { "127.0.0.1:8080".to_string() } fn default_cluster_mode() -> String { "single".to_string() } fn default_heartbeat_interval() -> u64 { 5 } fn default_heartbeat_timeout() -> u64 { 30 } fn default_auto_recovery() -> bool { true } fn default_max_replicas() -> u32 { 3 } impl Default for Config { fn default() -> Self { Self { server: default_server_config(), database: default_database_config(), logging: default_logging_config(), lua: default_lua_config(), cluster: default_cluster_config(), plugins: default_plugins_config(), http: default_http_config(), replication: default_replication_config(), network: default_network_config(), } } } impl Config { /// Загрузка конфигурации из файла pub fn load(path: &str) -> Result { let config_content = fs::read_to_string(path) .map_err(|e| ConfigError::IoError(e))?; let config: Config = toml::from_str(&config_content) .map_err(|e| ConfigError::ParseError(e))?; Ok(config) } /// Создание конфигурации по умолчанию pub fn default_with_path(data_dir: &str) -> Self { let mut config = Self::default(); config.database.data_dir = data_dir.to_string(); config } /// Получение пути к директории данных для базы данных pub fn get_data_path(&self, db_name: &str) -> String { format!("{}/{}", self.database.data_dir, db_name) } /// Сохранение конфигурации в файл pub fn save(&self, path: &str) -> Result<(), ConfigError> { let config_content = toml::to_string_pretty(self) .map_err(|e| ConfigError::SerializeError(e))?; // Создаем директорию если она не существует if let Some(parent) = Path::new(path).parent() { fs::create_dir_all(parent) .map_err(|e| ConfigError::IoError(e))?; } fs::write(path, config_content) .map_err(|e| ConfigError::IoError(e)) } /// Создание файла конфигурации по умолчанию pub fn create_default_config(path: &str) -> Result<(), ConfigError> { let default_config = Self::default(); default_config.save(path) } /// Получение конфигурации из переменных окружения или файла pub fn from_env_or_file(default_path: &str) -> Result { // Сначала пробуем загрузить из переменной окружения if let Ok(config_path) = std::env::var("FLUSQL_CONFIG") { return Self::load(&config_path); } // Пробуем загрузить из текущей директории if Path::new(default_path).exists() { return Self::load(default_path); } // Пробуем загрузить из домашней директории if let Ok(home_dir) = std::env::var("HOME") { let home_config = format!("{}/.config/flusql/config.toml", home_dir); if Path::new(&home_config).exists() { return Self::load(&home_config); } } // Пробуем загрузить из /etc let etc_config = "/etc/flusql/config.toml"; if Path::new(etc_config).exists() { return Self::load(etc_config); } // Создаем конфигурацию по умолчанию Ok(Self::default()) } /// Проверка валидности конфигурации pub fn validate(&self) -> Result<(), ConfigError> { // Проверяем размер страницы (должен быть степенью двойки и в разумных пределах) if self.database.page_size < 512 || self.database.page_size > 65536 { return Err(ConfigError::Invalid(format!( "Page size must be between 512 and 65536 bytes, got {}", self.database.page_size ))); } // Проверяем что page_size является степенью двойки if self.database.page_size & (self.database.page_size - 1) != 0 { return Err(ConfigError::Invalid(format!( "Page size must be a power of two, got {}", self.database.page_size ))); } // Проверяем порты // Исправление: убраны все проверки на > 65535, так как тип u16 гарантирует этот предел // Оставляем только проверку на 0 (порт 0 недопустим для сервера) if self.server.port == 0 { return Err(ConfigError::Invalid( "Server port cannot be 0".to_string() )); } if self.http.enabled { // Исправление: убраны все проверки на > 65535, так как тип u16 гарантирует этот предел // Оставляем только проверку на 0 (порт 0 недопустим для HTTP сервера) if self.http.port == 0 { return Err(ConfigError::Invalid( "HTTP port cannot be 0".to_string() )); } if self.http.tls_enabled { if self.http.tls_cert_path.is_none() || self.http.tls_key_path.is_none() { return Err(ConfigError::Invalid( "TLS requires both certificate and key paths".to_string() )); } } } // Проверяем директорию данных if self.database.data_dir.trim().is_empty() { return Err(ConfigError::Invalid("Data directory cannot be empty".to_string())); } Ok(()) } } /// Ошибки конфигурации #[derive(Debug, Error)] pub enum ConfigError { #[error("IO error: {0}")] IoError(std::io::Error), #[error("Parse error: {0}")] ParseError(toml::de::Error), #[error("Serialize error: {0}")] SerializeError(toml::ser::Error), #[error("Configuration not found")] NotFound, #[error("Invalid configuration: {0}")] Invalid(String), }