Upload files to "src/server"

This commit is contained in:
Григорий Сафронов 2025-12-06 19:30:23 +00:00
parent d27e84d723
commit eed8b66810

824
src/server/mod.rs Normal file
View File

@ -0,0 +1,824 @@
// src/server/mod.rs
//! Сервер Futriix - полностью lock-free документо-ориентированная БД
//!
//! Основной модуль сервера, который инициализирует все компоненты системы:
//! - Базу данных с lock-free архитектурой
//! - Lua движок для выполнения скриптов
//! - Менеджер шардинга и репликации
//! - HTTP/HTTPS серверы
//! - CSV менеджер для импорта/экспорта данных
//!
//! Архитектурные особенности:
//! - Все операции выполняются без блокировок (lock-free)
//! - Используются атомарные структуры данных
//! - Поддержка транзакций через Software Transactional Memory
//! - Интеграция с Lua для кастомной логики
#![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;
/// Функция для логирования в файл
/// Используется для отладки и аудита работы сервера
/// Все сообщения записываются в файл futriix.log с временными метками
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 цветом
/// Использует escape-последовательности для цветного форматирования вывода
#[allow(dead_code)]
fn print_colored(text: &str, ansi_color: &str) {
println!("{}{}\x1b[0m", ansi_color, text);
}
/// Конвертация HEX цвета в ANSI escape code
/// Поддерживает формат #RRGGBB для установки произвольных цветов
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>, // Lock-free база данных
lua_engine: lua_engine::LuaEngine, // Встроенный Lua интерпретатор
sharding_manager: Arc<sharding::ShardingManager>, // Менеджер шардинга и репликации
http_enabled: bool, // Флаг включения HTTP сервера
csv_manager: Arc<csv_import_export::CsvManager>, // Менеджер CSV операций
}
impl FutriixServer {
/// Создание нового сервера с lock-free архитектурой
/// Инициализирует все компоненты системы на основе конфигурации
pub async fn new(config_path: &str) -> Result<Self> {
// Загрузка конфигурации из файла TOML
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 скрипты с доступом к базе данных
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);
}
// ИЗМЕНЕНИЕ: Создаем простой default.html для тестирования вместо index.html
// Используем default.html как точку входа для веб-интерфейса Futriix
let default_html_content = r#"<!DOCTYPE html>
<html>
<head>
<title>Futriix Database Server</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #0f2027, #203a43, #2c5364);
color: #ffffff;
min-height: 100vh;
line-height: 1.6;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
header {
text-align: center;
margin-bottom: 3rem;
padding: 2rem;
background: rgba(255, 255, 255, 0.1);
border-radius: 15px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.logo {
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
margin-bottom: 1.5rem;
}
.logo-icon {
font-size: 3rem;
color: #00bfff;
}
h1 {
font-size: 3rem;
background: linear-gradient(45deg, #00bfff, #0080ff);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
margin-bottom: 0.5rem;
}
.tagline {
font-size: 1.2rem;
color: #a0d2ff;
margin-bottom: 1rem;
}
.version {
display: inline-block;
background: rgba(0, 191, 255, 0.2);
padding: 0.5rem 1rem;
border-radius: 20px;
font-size: 0.9rem;
color: #00bfff;
}
.status-panel {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
margin-bottom: 3rem;
}
.status-card {
background: rgba(255, 255, 255, 0.08);
padding: 1.5rem;
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.status-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 30px rgba(0, 191, 255, 0.2);
border-color: rgba(0, 191, 255, 0.3);
}
.status-icon {
font-size: 2rem;
margin-bottom: 1rem;
color: #00bfff;
}
.status-title {
font-size: 1.2rem;
margin-bottom: 0.5rem;
color: #a0d2ff;
}
.status-value {
font-size: 2rem;
font-weight: bold;
color: #ffffff;
}
.status-subtext {
font-size: 0.9rem;
color: #88aacc;
margin-top: 0.5rem;
}
.info-panel {
background: rgba(255, 255, 255, 0.05);
padding: 2rem;
border-radius: 12px;
margin-bottom: 2rem;
}
.info-title {
font-size: 1.5rem;
margin-bottom: 1rem;
color: #00bfff;
}
.info-content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
}
.info-item {
margin-bottom: 1rem;
}
.info-label {
font-weight: bold;
color: #a0d2ff;
margin-bottom: 0.3rem;
}
.info-value {
color: #ffffff;
}
.features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin-bottom: 2rem;
}
.feature {
background: rgba(0, 191, 255, 0.1);
padding: 1rem;
border-radius: 8px;
text-align: center;
border: 1px solid rgba(0, 191, 255, 0.2);
}
.feature-icon {
font-size: 1.5rem;
margin-bottom: 0.5rem;
color: #00bfff;
}
.action-buttons {
display: flex;
gap: 1rem;
flex-wrap: wrap;
justify-content: center;
margin-top: 2rem;
}
.btn {
padding: 1rem 2rem;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
display: inline-block;
}
.btn-primary {
background: linear-gradient(45deg, #00bfff, #0080ff);
color: white;
}
.btn-primary:hover {
background: linear-gradient(45deg, #0080ff, #00bfff);
transform: scale(1.05);
box-shadow: 0 5px 15px rgba(0, 191, 255, 0.4);
}
.btn-secondary {
background: rgba(255, 255, 255, 0.1);
color: white;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.btn-secondary:hover {
background: rgba(255, 255, 255, 0.2);
transform: scale(1.05);
}
.server-time {
text-align: center;
margin-top: 2rem;
padding-top: 2rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
color: #88aacc;
}
.time-display {
font-size: 1.2rem;
color: #00bfff;
font-weight: bold;
margin-top: 0.5rem;
}
footer {
text-align: center;
margin-top: 3rem;
padding-top: 2rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
color: #88aacc;
font-size: 0.9rem;
}
@media (max-width: 768px) {
.container {
padding: 1rem;
}
h1 {
font-size: 2rem;
}
.status-panel {
grid-template-columns: 1fr;
}
.action-buttons {
flex-direction: column;
}
.btn {
width: 100%;
text-align: center;
}
}
.status-online {
color: #33d17a;
}
.status-offline {
color: #ff6b6b;
}
.status-loading {
color: #ffa500;
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<div class="logo">
<div class="logo-icon"></div>
<div>
<h1>Futriix Database Server</h1>
<div class="tagline">Lock-Free Document Database with Lua Scripting</div>
</div>
</div>
<div class="version">v1.0.0 futriix 3i²</div>
</header>
<div class="status-panel">
<div class="status-card">
<div class="status-icon">🚀</div>
<div class="status-title">Server Status</div>
<div class="status-value status-online" id="server-status">Online</div>
<div class="status-subtext" id="connection-info">Ready to accept connections</div>
</div>
<div class="status-card">
<div class="status-icon">🗄</div>
<div class="status-title">Database</div>
<div class="status-value" id="db-status">Active</div>
<div class="status-subtext">Lock-free architecture</div>
</div>
<div class="status-card">
<div class="status-icon"></div>
<div class="status-title">Lua Engine</div>
<div class="status-value status-online" id="lua-status">Running</div>
<div class="status-subtext">Script execution enabled</div>
</div>
<div class="status-card">
<div class="status-icon">🔗</div>
<div class="status-title">Cluster Mode</div>
<div class="status-value status-loading" id="cluster-status">Checking...</div>
<div class="status-subtext">Sharding & Replication</div>
</div>
</div>
<div class="info-panel">
<div class="info-title">System Information</div>
<div class="info-content">
<div class="info-item">
<div class="info-label">Server Version</div>
<div class="info-value">Futriix 1.0.0</div>
</div>
<div class="info-item">
<div class="info-label">Architecture</div>
<div class="info-value">Wait-Free / Lock-Free</div>
</div>
<div class="info-item">
<div class="info-label">Protocol Support</div>
<div class="info-value">HTTP/1.1, HTTP/2, HTTPS</div>
</div>
<div class="info-item">
<div class="info-label">Default Ports</div>
<div class="info-value">HTTP: 9090, HTTPS: 8443</div>
</div>
</div>
<div class="features">
<div class="feature">
<div class="feature-icon">📊</div>
<div>Real-time Analytics</div>
</div>
<div class="feature">
<div class="feature-icon">🔒</div>
<div>Atomic Transactions</div>
</div>
<div class="feature">
<div class="feature-icon">🚀</div>
<div>High Performance</div>
</div>
<div class="feature">
<div class="feature-icon">📝</div>
<div>Lua Scripting</div>
</div>
<div class="feature">
<div class="feature-icon">🔄</div>
<div>Auto-Sharding</div>
</div>
<div class="feature">
<div class="feature-icon">💾</div>
<div>CSV Import/Export</div>
</div>
</div>
</div>
<div class="action-buttons">
<a href="/api/status" class="btn btn-primary" target="_blank">API Status</a>
<a href="/api/collections" class="btn btn-secondary" target="_blank">Collections</a>
<a href="/docs" class="btn btn-secondary" target="_blank">Documentation</a>
<a href="https://github.com/futriix" class="btn btn-secondary" target="_blank">GitHub</a>
</div>
<div class="server-time">
<div>Server Time</div>
<div class="time-display" id="current-time">Loading...</div>
</div>
<footer>
<p>© 2025 Futriix Database Server. All rights reserved.</p>
<p>Built with Rust Lock-Free Architecture Enterprise Ready</p>
</footer>
</div>
<script>
// Обновляем текущее время
function updateTime() {
const now = new Date();
const timeString = now.toLocaleString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZoneName: 'short'
});
document.getElementById('current-time').textContent = timeString;
}
// Проверяем статус кластера
function checkClusterStatus() {
fetch('/api/status')
.then(response => {
if (response.ok) {
return response.json();
}
throw new Error('Network response was not ok');
})
.then(data => {
const clusterStatus = document.getElementById('cluster-status');
clusterStatus.textContent = 'Standalone';
clusterStatus.className = 'status-value status-online';
})
.catch(error => {
console.log('Cluster status check failed:', error);
const clusterStatus = document.getElementById('cluster-status');
clusterStatus.textContent = 'Standalone';
clusterStatus.className = 'status-value status-online';
});
}
// Инициализация
document.addEventListener('DOMContentLoaded', () => {
// Начальное обновление времени
updateTime();
// Обновляем время каждую секунду
setInterval(updateTime, 1000);
// Проверяем статус кластера
setTimeout(checkClusterStatus, 2000);
// Анимация загрузки
setTimeout(() => {
document.getElementById('cluster-status').classList.remove('status-loading');
}, 3000);
// Отслеживание онлайн-статуса
window.addEventListener('online', () => {
document.getElementById('server-status').textContent = 'Online';
document.getElementById('server-status').className = 'status-value status-online';
document.getElementById('connection-info').textContent = 'Ready to accept connections';
});
window.addEventListener('offline', () => {
document.getElementById('server-status').textContent = 'Offline';
document.getElementById('server-status').className = 'status-value status-offline';
document.getElementById('connection-info').textContent = 'No network connection';
});
// Отображаем информацию о браузере в консоли
console.log('Futriix Database Server default.html loaded successfully');
console.log('Server is running with lock-free architecture');
console.log('Available at: ' + window.location.origin);
});
</script>
</body>
</html>"#;
// ИЗМЕНЕНИЕ: Сохраняем как default.html вместо index.html
// Это обеспечивает лучшую совместимость и избегает конфликтов с другими системами
if let Err(e) = std::fs::write("static/default.html", default_html_content) {
eprintln!("Warning: Failed to create default.html: {}", e);
} else {
println!("Created enhanced default.html in static directory");
println!("Web interface will be available at: http://localhost:9090/default.html");
}
let message = "Database initialized with system collections";
println!("{}", message);
log_to_file(message);
Ok(())
}
/// Запуск сервера с lock-free архитектурой
/// Координирует запуск HTTP серверов и интерактивной оболочки
pub async fn run(&self) -> Result<()> {
// Определяем режим работы и имя кластера
let cluster_name = &self.config.cluster.name;
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()
}
}