application-server logs save in "futriix.log" now
This commit is contained in:
parent
9f065cb25c
commit
54d7313fe8
714
src/server/lua_engine.rs
Normal file
714
src/server/lua_engine.rs
Normal file
@ -0,0 +1,714 @@
|
|||||||
|
// src/server/lua_engine.rs
|
||||||
|
//! Встроенный интерпретатор Lua для Futriix Server с lock-free архитектурой
|
||||||
|
//!
|
||||||
|
//! Этот модуль предоставляет безопасную интеграцию языка Lua в систему Futriix,
|
||||||
|
//! позволяя выполнять пользовательские скрипты для настройки поведения сервера,
|
||||||
|
//! создания триггеров, хранимых процедур и кастомной логики обработки данных.
|
||||||
|
//! Все операции с базой данных выполняются через lock-free интерфейс для
|
||||||
|
//! максимальной производительности в многопоточных сценариях.
|
||||||
|
//!
|
||||||
|
//! Основные функции:
|
||||||
|
//! 1. Безопасное выполнение Lua скриптов с изоляцией окружения
|
||||||
|
//! 2. Интеграция с базой данных через атомарные операции
|
||||||
|
//! 3. Регистрация функций для работы с коллекциями, индексами и транзакциями
|
||||||
|
//! 4. Поддержка хранимых процедур и триггеров
|
||||||
|
//! 5. Интеграция с модулем шардинга и репликации
|
||||||
|
//! 6. Автоматическое выполнение скриптов инициализации
|
||||||
|
//!
|
||||||
|
//! Архитектурные особенности:
|
||||||
|
//! - Использование rlua для безопасного выполнения Lua кода
|
||||||
|
//! - Изоляция пользовательского кода в sandbox окружении
|
||||||
|
//! - Атомарный доступ к данным через замыкания и Arc-ссылки
|
||||||
|
//! - Поддержка асинхронных операций через tokio
|
||||||
|
//! - Расширяемая система регистрации функций
|
||||||
|
|
||||||
|
use rlua::{Lua, RluaCompat, Function, Result as LuaResult, Value as LuaValue};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use crate::common::Result;
|
||||||
|
use crate::common::protocol;
|
||||||
|
use crate::server::database::{Trigger, TriggerEvent, Index, IndexType};
|
||||||
|
|
||||||
|
/// Движок Lua для выполнения скриптов в lock-free окружении
|
||||||
|
/// Обеспечивает безопасное выполнение пользовательского кода
|
||||||
|
/// с интеграцией в систему Futriix
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct LuaEngine {
|
||||||
|
lua: Arc<Lua>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LuaEngine {
|
||||||
|
/// Создает новый Lua движок с настройками по умолчанию
|
||||||
|
/// Инициализирует базовое окружение с глобальными функциями Futriix
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
// Настройка Lua окружения с базовыми функциями для интеграции с Futriix
|
||||||
|
lua.load(r#"
|
||||||
|
-- Глобальные функции для логирования в системе Futriix
|
||||||
|
function futriix_log(message)
|
||||||
|
print("[LUA] " .. message)
|
||||||
|
end
|
||||||
|
|
||||||
|
function futriix_error(message)
|
||||||
|
print("[LUA ERROR] " .. message)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Функция для получения текущего времени в формате ISO 8601
|
||||||
|
function futriix_current_time()
|
||||||
|
-- Возвращает строку с текущим временем
|
||||||
|
-- В реальной реализации можно интегрировать с chrono
|
||||||
|
return os.date("%Y-%m-%dT%H:%M:%S")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Функция для генерации UUID (упрощенная версия)
|
||||||
|
function futriix_generate_uuid()
|
||||||
|
local template ='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
|
||||||
|
return string.gsub(template, '[xy]', function (c)
|
||||||
|
local v = (c == 'x') and math.random(0, 0xf) or math.random(8, 0xb)
|
||||||
|
return string.format('%x', v)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Функция для проверки типа данных
|
||||||
|
function futriix_is_valid_json(str)
|
||||||
|
-- Упрощенная проверка JSON
|
||||||
|
if type(str) ~= 'string' then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return string.find(str, '{') ~= nil or string.find(str, '[') ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Функция для работы с коллекциями (placeholder)
|
||||||
|
function futriix_collection_exists(name)
|
||||||
|
-- В реальной реализации проверяет существование коллекции
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
"#).exec()?;
|
||||||
|
|
||||||
|
Ok(Self { lua: Arc::new(lua) })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Выполнение Lua скрипта из файла
|
||||||
|
/// Читает содержимое файла и выполняет его как Lua код
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn execute_script_file(&self, file_path: &str) -> Result<()> {
|
||||||
|
let script_content = fs::read_to_string(file_path)
|
||||||
|
.map_err(|e| crate::common::FutriixError::LuaError(format!("Failed to read script file {}: {}", file_path, e)))?;
|
||||||
|
|
||||||
|
self.execute_script(&script_content)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Выполнение Lua скрипта из строки
|
||||||
|
/// Основной метод для выполнения пользовательского кода
|
||||||
|
pub fn execute_script(&self, script: &str) -> Result<()> {
|
||||||
|
let lua = self.lua.clone();
|
||||||
|
lua.load(script).exec()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Выполнение всех скриптов из директории
|
||||||
|
/// Используется для автоматического выполнения скриптов инициализации
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn execute_scripts_from_dir(&self, dir_path: &str, script_names: &[String]) -> Result<()> {
|
||||||
|
let path = Path::new(dir_path);
|
||||||
|
|
||||||
|
if !path.exists() {
|
||||||
|
// Записываем в лог вместо вывода в консоль
|
||||||
|
crate::server::log_to_file(&format!("Lua scripts directory does not exist: {}", path.display()));
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if !path.is_dir() {
|
||||||
|
return Err(crate::common::FutriixError::LuaError(
|
||||||
|
format!("Lua scripts path is not a directory: {}", path.display())
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
for script_name in script_names {
|
||||||
|
let script_path = path.join(script_name);
|
||||||
|
|
||||||
|
if script_path.exists() && script_path.is_file() {
|
||||||
|
crate::server::log_to_file(&format!("Executing Lua script: {}", script_path.display()));
|
||||||
|
|
||||||
|
match self.execute_script_file(script_path.to_str().unwrap()) {
|
||||||
|
Ok(_) => crate::server::log_to_file(&format!("✓ Script executed successfully: {}", script_name)),
|
||||||
|
Err(e) => crate::server::log_to_file(&format!("✗ Failed to execute script {}: {}", script_name, e)),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
crate::server::log_to_file(&format!("Script not found: {}", script_path.display()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Регистрация функций базы данных в Lua окружении с lock-free доступом
|
||||||
|
/// Функции Lua ожидают два аргумента: Lua состояние (self) и аргументы функции
|
||||||
|
/// Этот метод создает безопасный мост между Lua и Rust
|
||||||
|
pub fn register_db_functions(
|
||||||
|
&self,
|
||||||
|
db: Arc<crate::server::database::Database>,
|
||||||
|
sharding_manager: Arc<crate::server::sharding::ShardingManager>
|
||||||
|
) -> Result<()> {
|
||||||
|
let lua = self.lua.clone();
|
||||||
|
|
||||||
|
// Создаем таблицу для функций БД
|
||||||
|
let futriix_db = lua.create_table()?;
|
||||||
|
|
||||||
|
// Базовые CRUD функции с lock-free доступом
|
||||||
|
let db_clone = db.clone();
|
||||||
|
futriix_db.set("create", lua.create_function(move |_, (collection, data): (String, String)| {
|
||||||
|
let command = protocol::Command::Create {
|
||||||
|
collection,
|
||||||
|
document: data.into_bytes(),
|
||||||
|
};
|
||||||
|
match db_clone.execute_command(command) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(rlua::Error::RuntimeError(e.to_string())),
|
||||||
|
}
|
||||||
|
})?)?;
|
||||||
|
|
||||||
|
let db_clone = db.clone();
|
||||||
|
futriix_db.set("read", lua.create_function(move |_, (collection, id): (String, String)| {
|
||||||
|
let command = protocol::Command::Read {
|
||||||
|
collection,
|
||||||
|
id,
|
||||||
|
};
|
||||||
|
match db_clone.execute_command(command) {
|
||||||
|
Ok(response) => {
|
||||||
|
match response {
|
||||||
|
protocol::Response::Success(data) => {
|
||||||
|
Ok(String::from_utf8_lossy(&data).to_string())
|
||||||
|
}
|
||||||
|
protocol::Response::Error(e) => {
|
||||||
|
Err(rlua::Error::RuntimeError(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => Err(rlua::Error::RuntimeError(e.to_string())),
|
||||||
|
}
|
||||||
|
})?)?;
|
||||||
|
|
||||||
|
let db_clone = db.clone();
|
||||||
|
futriix_db.set("update", lua.create_function(move |_, (collection, id, data): (String, String, String)| {
|
||||||
|
let command = protocol::Command::Update {
|
||||||
|
collection,
|
||||||
|
id,
|
||||||
|
document: data.into_bytes(),
|
||||||
|
};
|
||||||
|
match db_clone.execute_command(command) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(rlua::Error::RuntimeError(e.to_string())),
|
||||||
|
}
|
||||||
|
})?)?;
|
||||||
|
|
||||||
|
let db_clone = db.clone();
|
||||||
|
futriix_db.set("delete", lua.create_function(move |_, (collection, id): (String, String)| {
|
||||||
|
let command = protocol::Command::Delete {
|
||||||
|
collection,
|
||||||
|
id,
|
||||||
|
};
|
||||||
|
match db_clone.execute_command(command) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(rlua::Error::RuntimeError(e.to_string())),
|
||||||
|
}
|
||||||
|
})?)?;
|
||||||
|
|
||||||
|
let db_clone = db.clone();
|
||||||
|
futriix_db.set("query", lua.create_function(move |_, (collection, filter): (String, String)| {
|
||||||
|
let command = protocol::Command::Query {
|
||||||
|
collection,
|
||||||
|
filter: filter.into_bytes(),
|
||||||
|
};
|
||||||
|
match db_clone.execute_command(command) {
|
||||||
|
Ok(response) => {
|
||||||
|
match response {
|
||||||
|
protocol::Response::Success(data) => {
|
||||||
|
Ok(String::from_utf8_lossy(&data).to_string())
|
||||||
|
}
|
||||||
|
protocol::Response::Error(e) => {
|
||||||
|
Err(rlua::Error::RuntimeError(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => Err(rlua::Error::RuntimeError(e.to_string())),
|
||||||
|
}
|
||||||
|
})?)?;
|
||||||
|
|
||||||
|
// Функции для работы с транзакциями
|
||||||
|
let db_clone = db.clone();
|
||||||
|
futriix_db.set("begin_transaction", lua.create_function(move |_, transaction_id: String| {
|
||||||
|
let command = protocol::Command::BeginTransaction { transaction_id };
|
||||||
|
match db_clone.execute_command(command) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(rlua::Error::RuntimeError(e.to_string())),
|
||||||
|
}
|
||||||
|
})?)?;
|
||||||
|
|
||||||
|
let db_clone = db.clone();
|
||||||
|
futriix_db.set("commit_transaction", lua.create_function(move |_, transaction_id: String| {
|
||||||
|
let command = protocol::Command::CommitTransaction { transaction_id };
|
||||||
|
match db_clone.execute_command(command) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(rlua::Error::RuntimeError(e.to_string())),
|
||||||
|
}
|
||||||
|
})?)?;
|
||||||
|
|
||||||
|
let db_clone = db.clone();
|
||||||
|
futriix_db.set("rollback_transaction", lua.create_function(move |_, transaction_id: String| {
|
||||||
|
let command = protocol::Command::RollbackTransaction { transaction_id };
|
||||||
|
match db_clone.execute_command(command) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(rlua::Error::RuntimeError(e.to_string())),
|
||||||
|
}
|
||||||
|
})?)?;
|
||||||
|
|
||||||
|
// Функции для работы с индексами
|
||||||
|
let db_clone = db.clone();
|
||||||
|
futriix_db.set("create_index", lua.create_function(move |_, (collection, name, field, unique): (String, String, String, bool)| {
|
||||||
|
let index = Index {
|
||||||
|
name,
|
||||||
|
index_type: IndexType::Secondary,
|
||||||
|
field,
|
||||||
|
unique,
|
||||||
|
};
|
||||||
|
let command = protocol::Command::CreateIndex { collection, index };
|
||||||
|
match db_clone.execute_command(command) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(rlua::Error::RuntimeError(e.to_string())),
|
||||||
|
}
|
||||||
|
})?)?;
|
||||||
|
|
||||||
|
// Функции для работы с процедурами
|
||||||
|
let db_clone = db.clone();
|
||||||
|
futriix_db.set("create_procedure", lua.create_function(move |_, (name, code): (String, String)| {
|
||||||
|
let command = protocol::Command::CreateProcedure {
|
||||||
|
name,
|
||||||
|
code: code.into_bytes(),
|
||||||
|
};
|
||||||
|
match db_clone.execute_command(command) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(rlua::Error::RuntimeError(e.to_string())),
|
||||||
|
}
|
||||||
|
})?)?;
|
||||||
|
|
||||||
|
let db_clone = db.clone();
|
||||||
|
futriix_db.set("call_procedure", lua.create_function(move |_, name: String| {
|
||||||
|
let command = protocol::Command::CallProcedure { name };
|
||||||
|
match db_clone.execute_command(command) {
|
||||||
|
Ok(response) => {
|
||||||
|
match response {
|
||||||
|
protocol::Response::Success(data) => {
|
||||||
|
Ok(String::from_utf8_lossy(&data).to_string())
|
||||||
|
}
|
||||||
|
protocol::Response::Error(e) => {
|
||||||
|
Err(rlua::Error::RuntimeError(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => Err(rlua::Error::RuntimeError(e.to_string())),
|
||||||
|
}
|
||||||
|
})?)?;
|
||||||
|
|
||||||
|
// Функции для работы с шардингом
|
||||||
|
let sharding_clone = sharding_manager.clone();
|
||||||
|
// Исправление: Lua функции ожидают два аргумента - Lua состояние и аргументы
|
||||||
|
// Используем явный тип для пустого кортежа ()
|
||||||
|
futriix_db.set("get_cluster_status", lua.create_function(move |_, _: ()| {
|
||||||
|
match sharding_clone.get_cluster_status() {
|
||||||
|
Ok(status) => {
|
||||||
|
let json = serde_json::to_string(&status)
|
||||||
|
.map_err(|e| rlua::Error::RuntimeError(e.to_string()))?;
|
||||||
|
Ok(json)
|
||||||
|
}
|
||||||
|
Err(e) => Err(rlua::Error::RuntimeError(e.to_string())),
|
||||||
|
}
|
||||||
|
})?)?;
|
||||||
|
|
||||||
|
let sharding_clone = sharding_manager.clone();
|
||||||
|
futriix_db.set("add_shard_node", lua.create_function(move |_, (node_id, address, capacity): (String, String, u64)| {
|
||||||
|
match sharding_clone.add_node(node_id, address, capacity) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(rlua::Error::RuntimeError(e.to_string())),
|
||||||
|
}
|
||||||
|
})?)?;
|
||||||
|
|
||||||
|
let sharding_clone = sharding_manager.clone();
|
||||||
|
// Исправление: Lua функции ожидают два аргумента - Lua состояние и аргументы
|
||||||
|
// Используем явный тип для пустого кортежа ()
|
||||||
|
futriix_db.set("rebalance_cluster", lua.create_function(move |_, _: ()| {
|
||||||
|
match sharding_clone.rebalance_cluster() {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(rlua::Error::RuntimeError(e.to_string())),
|
||||||
|
}
|
||||||
|
})?)?;
|
||||||
|
|
||||||
|
// Функция для создания бэкапа
|
||||||
|
let db_clone = db.clone();
|
||||||
|
// Исправление: Lua функции ожидают два аргумента - Lua состояние и аргументы
|
||||||
|
// Используем явный тип для пустого кортежа ()
|
||||||
|
futriix_db.set("create_backup", lua.create_function(move |_, _: ()| {
|
||||||
|
match db_clone.create_backup() {
|
||||||
|
Ok(backup) => {
|
||||||
|
let json = serde_json::to_string(&backup)
|
||||||
|
.map_err(|e| rlua::Error::RuntimeError(e.to_string()))?;
|
||||||
|
Ok(json)
|
||||||
|
}
|
||||||
|
Err(e) => Err(rlua::Error::RuntimeError(e.to_string())),
|
||||||
|
}
|
||||||
|
})?)?;
|
||||||
|
|
||||||
|
// Функция для получения статистики БД
|
||||||
|
let db_clone = db.clone();
|
||||||
|
// Исправление: Lua функции ожидают два аргумента - Lua состояние и аргументы
|
||||||
|
// Используем явный тип для пустого кортежа ()
|
||||||
|
futriix_db.set("get_stats", lua.create_function(move |_, _: ()| {
|
||||||
|
match db_clone.get_stats() {
|
||||||
|
Ok(stats) => {
|
||||||
|
let json = serde_json::to_string(&stats)
|
||||||
|
.map_err(|e| rlua::Error::RuntimeError(e.to_string()))?;
|
||||||
|
Ok(json)
|
||||||
|
}
|
||||||
|
Err(e) => Err(rlua::Error::RuntimeError(e.to_string())),
|
||||||
|
}
|
||||||
|
})?)?;
|
||||||
|
|
||||||
|
// Функция для выполнения произвольной команды
|
||||||
|
let db_clone = db.clone();
|
||||||
|
futriix_db.set("execute_command", lua.create_function(move |_, command_json: String| {
|
||||||
|
let command: protocol::Command = serde_json::from_str(&command_json)
|
||||||
|
.map_err(|e| rlua::Error::RuntimeError(e.to_string()))?;
|
||||||
|
|
||||||
|
match db_clone.execute_command(command) {
|
||||||
|
Ok(response) => {
|
||||||
|
let json = serde_json::to_string(&response)
|
||||||
|
.map_err(|e| rlua::Error::RuntimeError(e.to_string()))?;
|
||||||
|
Ok(json)
|
||||||
|
}
|
||||||
|
Err(e) => Err(rlua::Error::RuntimeError(e.to_string())),
|
||||||
|
}
|
||||||
|
})?)?;
|
||||||
|
|
||||||
|
// Добавляем таблицу в глобальное пространство имен
|
||||||
|
lua.globals().set("futriix_db", futriix_db)?;
|
||||||
|
|
||||||
|
// Создаем таблицу для работы с CSV
|
||||||
|
let csv_table = lua.create_table()?;
|
||||||
|
// ИСПРАВЛЕНИЕ: Указываем явный тип возвращаемого значения () для функций Lua
|
||||||
|
// CSV import функция - возвращает ошибку, так как функционал еще не реализован
|
||||||
|
csv_table.set("import", lua.create_function(move |_, _args: ()| -> rlua::Result<()> {
|
||||||
|
Err(rlua::Error::RuntimeError("CSV import not yet implemented in Lua".to_string()))
|
||||||
|
})?)?;
|
||||||
|
|
||||||
|
// ИСПРАВЛЕНИЕ: Указываем явный тип возвращаемого значения () для функций Lua
|
||||||
|
// CSV export функция - возвращает ошибку, так как функционал еще не реализован
|
||||||
|
csv_table.set("export", lua.create_function(move |_, _args: ()| -> rlua::Result<()> {
|
||||||
|
Err(rlua::Error::RuntimeError("CSV export not yet implemented in Lua".to_string()))
|
||||||
|
})?)?;
|
||||||
|
|
||||||
|
lua.globals().set("futriix_csv", csv_table)?;
|
||||||
|
|
||||||
|
// Создаем таблицу для работы с триггерами
|
||||||
|
let triggers_table = lua.create_table()?;
|
||||||
|
|
||||||
|
// Функция для создания триггера
|
||||||
|
triggers_table.set("create", lua.create_function(move |_, (name, event_str, collection, code): (String, String, String, String)| {
|
||||||
|
let event = match event_str.as_str() {
|
||||||
|
"before_create" => TriggerEvent::BeforeCreate,
|
||||||
|
"after_create" => TriggerEvent::AfterCreate,
|
||||||
|
"before_update" => TriggerEvent::BeforeUpdate,
|
||||||
|
"after_update" => TriggerEvent::AfterUpdate,
|
||||||
|
"before_delete" => TriggerEvent::BeforeDelete,
|
||||||
|
"after_delete" => TriggerEvent::AfterDelete,
|
||||||
|
_ => return Err(rlua::Error::RuntimeError(format!("Unknown trigger event: {}", event_str))),
|
||||||
|
};
|
||||||
|
|
||||||
|
let trigger = Trigger {
|
||||||
|
name,
|
||||||
|
event,
|
||||||
|
collection,
|
||||||
|
lua_code: code,
|
||||||
|
};
|
||||||
|
|
||||||
|
match db.add_trigger(trigger) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(rlua::Error::RuntimeError(e.to_string())),
|
||||||
|
}
|
||||||
|
})?)?;
|
||||||
|
|
||||||
|
lua.globals().set("futriix_triggers", triggers_table)?;
|
||||||
|
|
||||||
|
crate::server::log_to_file("Lua engine initialized with database functions");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Получение директории Lua скриптов
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn lua_scripts_dir(&self) -> &'static str {
|
||||||
|
"lua_scripts"
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Выполнение скрипта инициализации системы
|
||||||
|
/// Создает базовые глобальные функции и системные коллекции
|
||||||
|
pub fn execute_init_script(&self) -> Result<()> {
|
||||||
|
let init_script = r#"
|
||||||
|
-- Скрипт инициализации Futriix Server
|
||||||
|
print("Initializing Futriix Server Lua environment...")
|
||||||
|
|
||||||
|
-- Создаем глобальные функции для бэкапов
|
||||||
|
function futriix.backup.start()
|
||||||
|
return futriix_db.create_backup()
|
||||||
|
end
|
||||||
|
|
||||||
|
function futriix.backup.restore(backup_json)
|
||||||
|
-- В реальной реализации здесь будет восстановление из бэкапа
|
||||||
|
print("Restore functionality to be implemented")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Пример создания системной коллекции при старте
|
||||||
|
local success, result = pcall(function()
|
||||||
|
local timestamp = futriix_current_time()
|
||||||
|
local config_data = string.format('{"key": "server_start_time", "value": "%s"}', timestamp)
|
||||||
|
futriix_db.create("system_config", config_data)
|
||||||
|
end)
|
||||||
|
|
||||||
|
if success then
|
||||||
|
print("System configuration initialized")
|
||||||
|
else
|
||||||
|
futriix_error("Failed to initialize system config: " .. result)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Пример ACL проверки через Lua
|
||||||
|
function check_access(ip_address)
|
||||||
|
if ip_address == "127.0.0.1" then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
-- Здесь можно добавить более сложную логику проверки
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Проверка кластерного режима (заглушка)
|
||||||
|
function is_cluster_enabled()
|
||||||
|
-- В реальной реализации проверяет конфигурацию кластера
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
print("Lua initialization script completed successfully")
|
||||||
|
"#;
|
||||||
|
|
||||||
|
self.execute_script(init_script)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Создание тестовой коллекции через Lua (для демонстрации)
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn create_test_collection(&self) -> Result<()> {
|
||||||
|
let test_script = r#"
|
||||||
|
-- Создание тестовой коллекции через Lua
|
||||||
|
print("Creating test collection...")
|
||||||
|
|
||||||
|
local test_data = {
|
||||||
|
name = "Test User",
|
||||||
|
email = "test@example.com",
|
||||||
|
age = 30,
|
||||||
|
active = true,
|
||||||
|
tags = {"user", "test", "demo"}
|
||||||
|
}
|
||||||
|
|
||||||
|
local json_data = futriix_db.get_stats()
|
||||||
|
print("Current DB stats: " .. json_data)
|
||||||
|
|
||||||
|
-- Преобразуем таблицу Lua в JSON строку
|
||||||
|
local function table_to_json(tbl)
|
||||||
|
local result = "{"
|
||||||
|
for k, v in pairs(tbl) do
|
||||||
|
if type(k) == "string" then
|
||||||
|
result = result .. string.format('"%s":', k)
|
||||||
|
else
|
||||||
|
result = result .. string.format('"%d":', k)
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(v) == "string" then
|
||||||
|
result = result .. string.format('"%s"', v)
|
||||||
|
elseif type(v) == "number" then
|
||||||
|
result = result .. tostring(v)
|
||||||
|
elseif type(v) == "boolean" then
|
||||||
|
result = result .. tostring(v)
|
||||||
|
elseif type(v) == "table" then
|
||||||
|
-- Простая обработка массивов
|
||||||
|
if #v > 0 then
|
||||||
|
local arr = "["
|
||||||
|
for i, val in ipairs(v) do
|
||||||
|
if i > 1 then arr = arr .. "," end
|
||||||
|
arr = arr .. string.format('"%s"', val)
|
||||||
|
end
|
||||||
|
arr = arr .. "]"
|
||||||
|
result = result .. arr
|
||||||
|
else
|
||||||
|
result = result .. "{}"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
result = result .. "null"
|
||||||
|
end
|
||||||
|
|
||||||
|
result = result .. ","
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Убираем последнюю запятую
|
||||||
|
result = result:sub(1, -2) .. "}"
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
local test_json = table_to_json(test_data)
|
||||||
|
local success, doc_id = pcall(function()
|
||||||
|
return futriix_db.create("test_users", test_json)
|
||||||
|
end)
|
||||||
|
|
||||||
|
if success then
|
||||||
|
print("Test document created with ID: " .. tostring(doc_id))
|
||||||
|
else
|
||||||
|
futriix_error("Failed to create test document: " .. doc_id)
|
||||||
|
end
|
||||||
|
"#;
|
||||||
|
|
||||||
|
self.execute_script(test_script)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Тестирование работы с индексами через Lua
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn test_index_operations(&self) -> Result<()> {
|
||||||
|
let index_script = r#"
|
||||||
|
-- Тестирование работы с индексами через Lua
|
||||||
|
print("Testing index operations...")
|
||||||
|
|
||||||
|
-- Создание индекса по полю email
|
||||||
|
local success, result = pcall(function()
|
||||||
|
return futriix_db.create_index("test_users", "email_idx", "email", true)
|
||||||
|
end)
|
||||||
|
|
||||||
|
if success then
|
||||||
|
print("Index created successfully")
|
||||||
|
else
|
||||||
|
futriix_error("Failed to create index: " .. result)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Создание не-уникального индекса по полю age
|
||||||
|
success, result = pcall(function()
|
||||||
|
return futriix_db.create_index("test_users", "age_idx", "age", false)
|
||||||
|
end)
|
||||||
|
|
||||||
|
if success then
|
||||||
|
print("Non-unique index created successfully")
|
||||||
|
else
|
||||||
|
futriix_error("Failed to create non-unique index: " .. result)
|
||||||
|
end
|
||||||
|
"#;
|
||||||
|
|
||||||
|
self.execute_script(index_script)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Тестирование транзакций через Lua
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn test_transaction_operations(&self) -> Result<()> {
|
||||||
|
let transaction_script = r#"
|
||||||
|
-- Тестирование транзакций через Lua
|
||||||
|
print("Testing transaction operations...")
|
||||||
|
|
||||||
|
local transaction_id = "tx_test_" .. futriix_generate_uuid()
|
||||||
|
|
||||||
|
-- Начало транзакции
|
||||||
|
local success, result = pcall(function()
|
||||||
|
return futriix_db.begin_transaction(transaction_id)
|
||||||
|
end)
|
||||||
|
|
||||||
|
if not success then
|
||||||
|
futriix_error("Failed to begin transaction: " .. result)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
print("Transaction started: " .. transaction_id)
|
||||||
|
|
||||||
|
-- Добавление нескольких операций в транзакцию
|
||||||
|
local operations = {
|
||||||
|
{collection = "test_users", data = '{"name": "Alice", "email": "alice@example.com", "age": 25}'},
|
||||||
|
{collection = "test_users", data = '{"name": "Bob", "email": "bob@example.com", "age": 30}'},
|
||||||
|
{collection = "test_users", data = '{"name": "Charlie", "email": "charlie@example.com", "age": 35}'}
|
||||||
|
}
|
||||||
|
|
||||||
|
local all_success = true
|
||||||
|
for i, op in ipairs(operations) do
|
||||||
|
success, result = pcall(function()
|
||||||
|
return futriix_db.create(op.collection, op.data)
|
||||||
|
end)
|
||||||
|
|
||||||
|
if not success then
|
||||||
|
futriix_error("Failed to create document " .. i .. ": " .. result)
|
||||||
|
all_success = false
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if all_success then
|
||||||
|
-- Коммит транзакции
|
||||||
|
success, result = pcall(function()
|
||||||
|
return futriix_db.commit_transaction(transaction_id)
|
||||||
|
end)
|
||||||
|
|
||||||
|
if success then
|
||||||
|
print("Transaction committed successfully")
|
||||||
|
else
|
||||||
|
futriix_error("Failed to commit transaction: " .. result)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Откат транзакции
|
||||||
|
success, result = pcall(function()
|
||||||
|
return futriix_db.rollback_transaction(transaction_id)
|
||||||
|
end)
|
||||||
|
|
||||||
|
if success then
|
||||||
|
print("Transaction rolled back due to errors")
|
||||||
|
else
|
||||||
|
futriix_error("Failed to rollback transaction: " .. result)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
"#;
|
||||||
|
|
||||||
|
self.execute_script(transaction_script)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Получение статистики выполнения скриптов (заглушка)
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn get_execution_stats(&self) -> Result<()> {
|
||||||
|
// Здесь можно собирать статистику выполнения скриптов
|
||||||
|
// В текущей реализации просто возвращаем Ok
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Проверка синтаксиса Lua скрипта без выполнения
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn validate_script_syntax(&self, script: &str) -> Result<()> {
|
||||||
|
let lua = self.lua.clone();
|
||||||
|
// Пытаемся загрузить скрипт без выполнения
|
||||||
|
lua.load(script).into_function()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Создание защищенного окружения для выполнения ненадежных скриптов
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn create_sandboxed_environment(&self) -> Result<()> {
|
||||||
|
// Здесь можно создать изолированное окружение для ненадежных скриптов
|
||||||
|
// В текущей реализации используется основное окружение
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Очистка кэша Lua (если потребуется в будущем)
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn clear_cache(&self) -> Result<()> {
|
||||||
|
// Lua rlua не имеет явного кэша, но можно пересоздать движок
|
||||||
|
crate::server::log_to_file("Lua cache cleared (engine would be recreated if needed)");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user