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

799 lines
28 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 документо-ориентированная БД
#![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;
/// Функция для логирования в файл
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);
}
// ИЗМЕНЕНИЕ: Создаем простой 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 архитектурой
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()
}
}