Upload files to "src/server"
This commit is contained in:
parent
d27e84d723
commit
eed8b66810
824
src/server/mod.rs
Normal file
824
src/server/mod.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user