Files
futriix-old/src/server/mod.rs

353 lines
15 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// src/server/mod.rs
//! Сервер Futriix - документо-ориентированная БД с lock-free архитектурой
//!
//! Основной модуль сервера, реализующий lock-free доступ к данным,
//! синхронную master-master репликацию и поддержку HTTP/HTTPS.
#![allow(dead_code)]
use std::sync::Arc;
use std::fs::OpenOptions;
use std::io::Write;
use crate::common::Result;
use crate::common::config::Config;
use crate::lua_shell::LuaShell;
// Импортируем подмодули
pub mod database;
pub mod lua_engine;
pub mod http;
pub mod sharding; // Объединенный модуль шардинга и репликации
pub mod csv_import_export; // Модуль для CSV импорта/экспорта
/// Функция для логирования в файл
fn log_to_file(message: &str) {
if let Ok(mut file) = OpenOptions::new()
.create(true)
.append(true)
.open("futriix.log")
{
// ИСПРАВЛЕНИЕ: Используем системное время с миллисекундами
let timestamp = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f").to_string();
let log_message = format!("[{}] {}\n", timestamp, message);
let _ = file.write_all(log_message.as_bytes());
}
}
/// Функция для вывода текста с ANSI цветом
#[allow(dead_code)]
fn print_colored(text: &str, ansi_color: &str) {
println!("{}{}\x1b[0m", ansi_color, text);
}
/// Конвертация HEX цвета в ANSI escape code
fn hex_to_ansi(hex_color: &str) -> String {
let hex = hex_color.trim_start_matches('#');
if hex.len() == 6 {
if let (Ok(r), Ok(g), Ok(b)) = (
u8::from_str_radix(&hex[0..2], 16),
u8::from_str_radix(&hex[2..4], 16),
u8::from_str_radix(&hex[4..6], 16),
) {
return format!("\x1b[38;2;{};{};{}m", r, g, b);
}
}
"\x1b[38;2;255;255;255m".to_string()
}
/// Основный сервер Futriix с lock-free архитектурой
pub struct FutriixServer {
config: Config,
database: Arc<database::Database>,
lua_engine: lua_engine::LuaEngine,
sharding_manager: Arc<sharding::ShardingManager>, // Объединенный менеджер
http_enabled: bool,
csv_manager: Arc<csv_import_export::CsvManager>,
}
impl FutriixServer {
/// Создание нового сервера с lock-free архитектурой
pub async fn new(config_path: &str) -> Result<Self> {
// Загрузка конфигурации
let config = Config::load(config_path)?;
// Инициализация компонентов с lock-free подходами
let database = Arc::new(database::Database::new());
let lua_engine = lua_engine::LuaEngine::new()?;
// Генерируем уникальный ID для текущего узла
let node_id = format!("node_{}", uuid::Uuid::new_v4().to_string()[..8].to_string());
// Инициализация объединенного менеджера шардинга и репликации
let sharding_manager = Arc::new(sharding::ShardingManager::new(
config.sharding.virtual_nodes_per_node,
config.replication.enabled,
config.sharding.min_nodes_for_cluster,
node_id,
));
// Инициализация менеджера CSV
let csv_manager = Arc::new(csv_import_export::CsvManager::new(
database.clone(),
config.csv.clone(),
));
// Регистрация функций БД в Lua
lua_engine.register_db_functions(database.clone(), sharding_manager.clone())?;
// Инициализация базы данных
FutriixServer::initialize_database(database.clone())?;
// Проверяем, включен ли HTTP режим (теперь учитываем новые директивы)
let http_enabled = (config.server.http_port.is_some() && config.server.http) ||
(config.server.https_port.is_some() && config.server.https);
Ok(Self {
config,
database,
lua_engine,
sharding_manager,
http_enabled,
csv_manager,
})
}
/// Инициализация базы данных с lock-free структурами
fn initialize_database(db: Arc<database::Database>) -> Result<()> {
// Создаем системные коллекции с lock-free доступом
let _system_collection = db.get_collection("_system");
let _users_collection = db.get_collection("_users");
let _logs_collection = db.get_collection("_logs");
let _procedures_collection = db.get_collection("_procedures");
let _triggers_collection = db.get_collection("_triggers");
let _csv_imports_collection = db.get_collection("_csv_imports");
// Создаем директорию для бэкапов
let backup_dir = "./futriix_backups";
if let Err(e) = std::fs::create_dir_all(backup_dir) {
eprintln!("Warning: Failed to create backup directory '{}': {}", backup_dir, e);
} else {
println!("Backup directory created at: {}", backup_dir);
}
// Создаем директорию для CSV файлов
let csv_dir = "./futriix_csv";
if let Err(e) = std::fs::create_dir_all(csv_dir) {
eprintln!("Warning: Failed to create CSV directory '{}': {}", csv_dir, e);
} else {
println!("CSV directory created at: {}", csv_dir);
}
// Создаем поддиректории для CSV импорта и экспорта
let import_dir = "./futriix_csv/import";
let export_dir = "./futriix_csv/export";
let _ = std::fs::create_dir_all(import_dir);
let _ = std::fs::create_dir_all(export_dir);
// Создаем директорию для статических файлов
let static_dir = "static";
if let Err(e) = std::fs::create_dir_all(static_dir) {
eprintln!("Warning: Failed to create static files directory '{}': {}", static_dir, e);
} else {
println!("Static files directory created at: {}", static_dir);
}
// СОЗДАЕМ ПРОСТОЙ INDEX.HTML ДЛЯ ТЕСТИРОВАНИЯ
let index_html_content = r#"<!DOCTYPE html>
<html>
<head>
<title>Futriix Database Server</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
h1 { color: #00bfff; }
.status { padding: 10px; background: #f0f0f0; border-radius: 5px; }
</style>
</head>
<body>
<h1>Futriix Database Server</h1>
<div class="status">
<p>Server is running successfully!</p>
<p>This is a test page to verify HTTP server functionality.</p>
<p>Current time: <span id="time"></span></p>
</div>
<script>
document.getElementById('time').textContent = new Date().toLocaleString();
</script>
</body>
</html>"#;
if let Err(e) = std::fs::write("static/index.html", index_html_content) {
eprintln!("Warning: Failed to create index.html: {}", e);
} else {
println!("Created test index.html in static directory");
}
let message = "Database initialized with system collections";
println!("{}", message);
log_to_file(message);
Ok(())
}
/// Запуск сервера с lock-free архитектурой
pub async fn run(&self) -> Result<()> {
// Определяем режим работы и имя кластера
let cluster_name = &self.config.cluster.name;
// ИЗМЕНЕНИЕ: Заменено "Mode" на "Run"
println!("Run: cluster (cluster: '{}')", cluster_name);
log_to_file("Futriix Database Server started");
log_to_file(&format!("Run: cluster (cluster: '{}')", cluster_name));
// ИЗМЕНЕНИЕ: Сначала запускаем HTTP серверы и ждем их запуска
if self.http_enabled {
self.start_http_servers().await?;
} else {
println!("HTTP/HTTPS servers disabled in configuration");
}
// Добавляем пустую строку после информации о серверах
println!();
let mut lua_shell = LuaShell::new(
self.lua_engine.clone(),
self.database.clone(),
self.sharding_manager.clone(),
self.csv_manager.clone(),
);
// Запуск интерактивной оболочки - ЭТО ОСНОВНОЙ ПОТОК ВЫПОЛНЕНИЯ
lua_shell.run().await?;
Ok(())
}
/// ИЗМЕНЕНИЕ: Запуск HTTP/HTTPS серверов с ожиданием их готовности
async fn start_http_servers(&self) -> Result<()> {
let static_config = self::http::StaticFilesConfig::default();
let acl_config = self::http::AclConfig {
enabled: self.config.acl.enabled,
allowed_ips: self.config.acl.allowed_ips.clone(),
denied_ips: self.config.acl.denied_ips.clone(),
};
let mut http_server_handles = Vec::new();
// Запуск HTTP сервера, если настроен и включен
if let Some(http_port) = self.config.server.http_port {
if self.config.server.http {
let http_addr = format!("{}:{}", self.config.server.host, http_port);
let http_config = self::http::HttpConfig {
enabled: true,
port: http_port,
http2_enabled: self.config.server.http2_enabled.unwrap_or(false),
};
let db_clone = self.database.clone();
let static_config_clone = static_config.clone();
let acl_config_clone = acl_config.clone();
println!("Starting HTTP server on {}", http_addr);
// ЗАПУСКАЕМ В ФОНОВОЙ ЗАДАЧЕ
let handle = tokio::spawn(async move {
match self::http::start_http_server(&http_addr, db_clone, static_config_clone, http_config, acl_config_clone).await {
Ok(_) => {
let message = format!("HTTP server started on {}", http_addr);
println!("{}", message);
log_to_file(&message);
}
Err(e) => {
let message = format!("Failed to start HTTP server: {}", e);
eprintln!("{}", message);
log_to_file(&message);
}
}
});
http_server_handles.push(handle);
} else {
println!("HTTP server disabled in configuration");
}
}
// Запуск HTTPS сервера, если настроен и включен
if let Some(https_port) = self.config.server.https_port {
if self.config.server.https && self.config.tls.enabled {
let https_addr = format!("{}:{}", self.config.server.host, https_port);
let tls_config = self::http::TlsConfig {
enabled: self.config.tls.enabled,
cert_path: self.config.tls.cert_path.clone(),
key_path: self.config.tls.key_path.clone(),
};
let db_clone = self.database.clone();
let static_config_clone = static_config.clone();
let acl_config_clone = acl_config.clone();
println!("Starting HTTPS server on {}", https_addr);
let handle = tokio::spawn(async move {
match self::http::start_https_server(&https_addr, db_clone, static_config_clone, tls_config, acl_config_clone).await {
Ok(_) => {
let message = format!("HTTPS server started on {}", https_addr);
println!("{}", message);
log_to_file(&message);
}
Err(e) => {
let message = format!("Failed to start HTTPS server: {}", e);
eprintln!("{}", message);
log_to_file(&message);
}
}
});
http_server_handles.push(handle);
} else {
if !self.config.tls.enabled {
println!("HTTPS disabled: TLS not enabled in configuration");
} else {
println!("HTTPS server disabled in configuration");
}
}
}
// Ждем небольшое время, чтобы серверы успели стартовать
if !http_server_handles.is_empty() {
println!("Waiting for HTTP servers to start...");
// Небольшая задержка для гарантии запуска
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
}
Ok(())
}
/// Получение менеджера шардинга (для тестов и расширений)
#[allow(dead_code)]
pub fn get_sharding_manager(&self) -> Arc<sharding::ShardingManager> {
self.sharding_manager.clone()
}
/// Получение менеджера CSV (для тестов и расширений)
#[allow(dead_code)]
pub fn get_csv_manager(&self) -> Arc<csv_import_export::CsvManager> {
self.csv_manager.clone()
}
/// Получение базы данных (для тестов и расширений)
#[allow(dead_code)]
pub fn get_database(&self) -> Arc<database::Database> {
self.database.clone()
}
/// Получение Lua движка (для тестов и расширений)
#[allow(dead_code)]
pub fn get_lua_engine(&self) -> lua_engine::LuaEngine {
self.lua_engine.clone()
}
}