From 6c7849bc5a319de49ec7d26cc4737b54417f997e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=93=D1=80=D0=B8=D0=B3=D0=BE=D1=80=D0=B8=D0=B9=20=D0=A1?= =?UTF-8?q?=D0=B0=D1=84=D1=80=D0=BE=D0=BD=D0=BE=D0=B2?= Date: Wed, 14 Jan 2026 20:59:58 +0000 Subject: [PATCH] Upload files to "src/utils" --- src/utils/config.rs | 750 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 750 insertions(+) create mode 100644 src/utils/config.rs diff --git a/src/utils/config.rs b/src/utils/config.rs new file mode 100644 index 0000000..0ab352d --- /dev/null +++ b/src/utils/config.rs @@ -0,0 +1,750 @@ +//! Конфигурационный модуль для 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), +}