diff --git a/README.md b/README.md
index c4c0ec5..e16a116 100644
--- a/README.md
+++ b/README.md
@@ -48,9 +48,9 @@
Кластеризация и шардинг
Ограничения
Импорт-Экспорт
- Lua-плагины
- Контроль доступа
HTTP API
+ Контроль доступа
+ Lua-плагины
Триггеры
Сжатие данных
Графический интерфейс
@@ -1263,6 +1263,101 @@ Created database 'company_restore'
(К началу)
+## HTTP API
+
+ СУБД Futriix предусмотрен удобный сетевой интерфейс для интеграции с веб‑приложениями — это RESTful API, который покрывает все основные операции над данными и служебными объектами системы.
+
+ API спроектирован с учётом требований современной веб‑разработки:
+
+ * Поддержка CORS позволяет выполнять запросы к СУБД из браузерных приложений, размещённых на других доменах, без проблем с политикой безопасности браузеров.
+ * Аутентификация по X‑Session‑ID даёт простой и надёжный механизм управления сессиями: клиент получает идентификатор сессии после авторизации, а затем передаёт его в заголовке X-Session-ID для подтверждения прав на выполнение операций. Такой подход хорошо ложится на привычные схемы работы с сессионными токенами и легко встраивается в существующие стеки.
+
+ * **Доступны следующие группы endpoints, каждая из которых отвечает за свою область управления:**
+
+ * **CRUD‑операции над коллекциями** `(/api/db/{db}/{collection})` — полный набор действий для работы с данными: создание, чтение, обновление и удаление документов. Шаблоны URL позволяют адресовать конкретную базу данных и коллекцию, что удобно при мультитенантной архитектуре или при работе с несколькими логическими пространствами данных.
+
+ * **Управление индексами** `(/api/index/)` — инструменты для создания, изменения и удаления индексов, чтобы гибко настраивать производительность выборки под разные типы запросов.
+
+ * **Контроль доступа (ACL)** `(/api/acl/)` — настройка правил доступа к объектам СУБД: можно разграничивать права на уровне баз, коллекций, отдельных операций или даже по условиям над данными.
+
+ * **Работа с ограничениями** `(/api/constraint/)` — управление декларативными ограничениями целостности (например, enum‑списками, диапазонами, уникальностью и т. п.), которые помогают поддерживать корректность данных на уровне СУБД.
+
+ * **Администрирование кластера** `(/api/cluster/)` — операции по управлению топологией кластера: добавление и удаление узлов, перераспределение шардов, мониторинг состояния реплик и консенсуса (в том числе на базе Raft). Это особенно важно при динамическом масштабировании и обслуживании распределённой системы.
+
+```sh
+# Аутентификация
+curl -X POST http://localhost:8080/api/auth/login \
+ -H "Content-Type: application/json" \
+ -d '{"username":"admin","password":"admin"}'
+# Response: {"success":true,"data":{"session_id":"abc123"}}
+
+# Вставка документа
+curl -X POST http://localhost:8080/api/db/company/employees \
+ -H "Content-Type: application/json" \
+ -H "X-Session-ID: abc123" \
+ -d '{"name":"API User","position":"Integrator","age":28}'
+# Response: {"success":true,"data":{"status":"inserted"}}
+
+# Получение документа по ID
+curl -X GET "http://localhost:8080/api/db/company/employees/550e8400-e29b-41d4-a716-446655440000" \
+ -H "X-Session-ID: abc123"
+# Response: {"success":true,"data":{"_id":"550e8400-...","fields":{...}}}
+
+# Получение всех документов с пагинацией
+curl -X GET "http://localhost:8080/api/db/company/employees?limit=10&offset=0" \
+ -H "X-Session-ID: abc123"
+
+# Поиск по индексу
+curl -X GET "http://localhost:8080/api/db/company/employees?index=name_idx&value=John%20Doe" \
+ -H "X-Session-ID: abc123"
+
+# Обновление документа
+curl -X PUT http://localhost:8080/api/db/company/employees/550e8400-e29b-41d4-a716-446655440000 \
+ -H "Content-Type: application/json" \
+ -H "X-Session-ID: abc123" \
+ -d '{"age":31,"position":"Senior Developer"}'
+# Response: {"success":true,"data":{"status":"updated"}}
+
+# Удаление документа
+curl -X DELETE "http://localhost:8080/api/db/company/employees/550e8400-e29b-41d4-a716-446655440000" \
+ -H "X-Session-ID: abc123"
+# Response: {"success":true,"data":{"status":"deleted"}}
+
+# Создание индекса через API
+curl -X POST http://localhost:8080/api/index/company/employees/create \
+ -H "Content-Type: application/json" \
+ -H "X-Session-ID: abc123" \
+ -d '{"name":"email_idx","fields":["email"],"unique":true}'
+
+# Просмотр индексов
+curl -X GET "http://localhost:8080/api/index/company/employees/list" \
+ -H "X-Session-ID: abc123"
+
+# Статус кластера через API
+curl -X GET "http://localhost:8080/api/cluster/status" \
+ -H "X-Session-ID: abc123"
+
+# Создание пользователя через API
+curl -X POST http://localhost:8080/api/acl/user/newuser \
+ -H "Content-Type: application/json" \
+ -H "X-Session-ID: abc123" \
+ -d '{"password":"secret","roles":["reader"]}'
+
+# Назначение прав через API
+curl -X POST "http://localhost:8080/api/acl/grant/reader/rw" \
+ -H "X-Session-ID: abc123"
+
+# Создание триггера через API
+curl -X POST http://localhost:8080/api/trigger/company/employees/create \
+ -H "Content-Type: application/json" \
+ -H "X-Session-ID: abc123" \
+ -d '{"name":"audit","event":"AFTER_INSERT","action":"log"}'
+```
+
+(К началу)
+
+
+
## Lua-плагины
Для расширения функциональных возможностей субд **без изменения её исходного кода**, в futriix была реализована система расширения функциональности через Lua-скрипты с изолированным окружением. Плагины имеют доступ к БД, транзакциям, триггерам, могут логировать события и взаимодействовать через событийную шину, а также они доступны в веб-интерфейсе.
@@ -1645,97 +1740,343 @@ futriix:~> acl grant employees admin rwda
(К началу)
-## HTTP API
+## Lua-плагины
- СУБД Futriix предусмотрен удобный сетевой интерфейс для интеграции с веб‑приложениями — это RESTful API, который покрывает все основные операции над данными и служебными объектами системы.
-
- API спроектирован с учётом требований современной веб‑разработки:
-
- * Поддержка CORS позволяет выполнять запросы к СУБД из браузерных приложений, размещённых на других доменах, без проблем с политикой безопасности браузеров.
- * Аутентификация по X‑Session‑ID даёт простой и надёжный механизм управления сессиями: клиент получает идентификатор сессии после авторизации, а затем передаёт его в заголовке X-Session-ID для подтверждения прав на выполнение операций. Такой подход хорошо ложится на привычные схемы работы с сессионными токенами и легко встраивается в существующие стеки.
-
- * **Доступны следующие группы endpoints, каждая из которых отвечает за свою область управления:**
-
- * **CRUD‑операции над коллекциями** `(/api/db/{db}/{collection})` — полный набор действий для работы с данными: создание, чтение, обновление и удаление документов. Шаблоны URL позволяют адресовать конкретную базу данных и коллекцию, что удобно при мультитенантной архитектуре или при работе с несколькими логическими пространствами данных.
-
- * **Управление индексами** `(/api/index/)` — инструменты для создания, изменения и удаления индексов, чтобы гибко настраивать производительность выборки под разные типы запросов.
-
- * **Контроль доступа (ACL)** `(/api/acl/)` — настройка правил доступа к объектам СУБД: можно разграничивать права на уровне баз, коллекций, отдельных операций или даже по условиям над данными.
-
- * **Работа с ограничениями** `(/api/constraint/)` — управление декларативными ограничениями целостности (например, enum‑списками, диапазонами, уникальностью и т. п.), которые помогают поддерживать корректность данных на уровне СУБД.
-
- * **Администрирование кластера** `(/api/cluster/)` — операции по управлению топологией кластера: добавление и удаление узлов, перераспределение шардов, мониторинг состояния реплик и консенсуса (в том числе на базе Raft). Это особенно важно при динамическом масштабировании и обслуживании распределённой системы.
+Для расширения функциональных возможностей субд **без изменения её исходного кода**, в futriix была реализована система расширения функциональности через Lua-скрипты с изолированным окружением. Плагины имеют доступ к БД, транзакциям, триггерам, могут логировать события и взаимодействовать через событийную шину, а также они доступны в веб-интерфейсе.
+А кроме того плагины могут использоваться для написания движков для субд, без изменения её исходного кода, на языке lua.
```sh
-# Аутентификация
-curl -X POST http://localhost:8080/api/auth/login \
- -H "Content-Type: application/json" \
- -d '{"username":"admin","password":"admin"}'
-# Response: {"success":true,"data":{"session_id":"abc123"}}
+# Просмотр информации о системе плагинов
+futriix:~> plugin status
+=== Plugin System Status ===
+ Enabled: true
+ Plugins Directory: ./plugins
+ Loaded Plugins: 3
+ Total Executions: 125
-# Вставка документа
-curl -X POST http://localhost:8080/api/db/company/employees \
- -H "Content-Type: application/json" \
- -H "X-Session-ID: abc123" \
- -d '{"name":"API User","position":"Integrator","age":28}'
-# Response: {"success":true,"data":{"status":"inserted"}}
+# Список загруженных плагинов
+futriix:~> plugin list
+=== Loaded Plugins ===
+ validation (v1.0.0) by admin - Document validation rules
+ Status: RUNNING
+ audit (v2.1.0) by security - Audit trail logger
+ Status: RUNNING
+ notify (v1.2.0) by devops - Email and webhook notifications
+ Status: RUNNING
-# Получение документа по ID
-curl -X GET "http://localhost:8080/api/db/company/employees/550e8400-e29b-41d4-a716-446655440000" \
- -H "X-Session-ID: abc123"
-# Response: {"success":true,"data":{"_id":"550e8400-...","fields":{...}}}
+# Загрузка плагина из файла
+futriix:~> plugin load email_notifier ./plugins/email_notifier.lua
+✓ Plugin 'email_notifier' loaded successfully
+ Version: 1.0.0
+ Author: admin
+ Description: Send email notifications on database events
-# Получение всех документов с пагинацией
-curl -X GET "http://localhost:8080/api/db/company/employees?limit=10&offset=0" \
- -H "X-Session-ID: abc123"
+# Запуск/остановка плагина
+futriix:~> plugin start email_notifier
+✓ Plugin 'email_notifier' started
-# Поиск по индексу
-curl -X GET "http://localhost:8080/api/db/company/employees?index=name_idx&value=John%20Doe" \
- -H "X-Session-ID: abc123"
+futriix:~> plugin stop email_notifier
+✓ Plugin 'email_notifier' stopped
-# Обновление документа
-curl -X PUT http://localhost:8080/api/db/company/employees/550e8400-e29b-41d4-a716-446655440000 \
- -H "Content-Type: application/json" \
- -H "X-Session-ID: abc123" \
- -d '{"age":31,"position":"Senior Developer"}'
-# Response: {"success":true,"data":{"status":"updated"}}
-
-# Удаление документа
-curl -X DELETE "http://localhost:8080/api/db/company/employees/550e8400-e29b-41d4-a716-446655440000" \
- -H "X-Session-ID: abc123"
-# Response: {"success":true,"data":{"status":"deleted"}}
-
-# Создание индекса через API
-curl -X POST http://localhost:8080/api/index/company/employees/create \
- -H "Content-Type: application/json" \
- -H "X-Session-ID: abc123" \
- -d '{"name":"email_idx","fields":["email"],"unique":true}'
-
-# Просмотр индексов
-curl -X GET "http://localhost:8080/api/index/company/employees/list" \
- -H "X-Session-ID: abc123"
-
-# Статус кластера через API
-curl -X GET "http://localhost:8080/api/cluster/status" \
- -H "X-Session-ID: abc123"
-
-# Создание пользователя через API
-curl -X POST http://localhost:8080/api/acl/user/newuser \
- -H "Content-Type: application/json" \
- -H "X-Session-ID: abc123" \
- -d '{"password":"secret","roles":["reader"]}'
-
-# Назначение прав через API
-curl -X POST "http://localhost:8080/api/acl/grant/reader/rw" \
- -H "X-Session-ID: abc123"
-
-# Создание триггера через API
-curl -X POST http://localhost:8080/api/trigger/company/employees/create \
- -H "Content-Type: application/json" \
- -H "X-Session-ID: abc123" \
- -d '{"name":"audit","event":"AFTER_INSERT","action":"log"}'
+# Выгрузка плагина
+futriix:~> plugin unload email_notifier
+✓ Plugin 'email_notifier' unloaded
```
+**Пример плагина валидации документов**
+
+В директорию `plugins` добавляем файл `validation.lua`, следдующего содержания:
+
+```sh
+-- Метаданные плагина
+version = "1.0.0"
+author = "admin"
+description = "Document validation rules for employees collection"
+
+-- Функция инициализации
+function on_load()
+ plugin_log("info", "Validation plugin loaded")
+ return true
+end
+
+-- Функция запуска
+function on_start()
+ plugin_log("info", "Validation plugin started")
+ return true
+end
+
+-- Функция остановки
+function on_stop()
+ plugin_log("info", "Validation plugin stopped")
+ return true
+end
+
+-- Функция выгрузки
+function on_unload()
+ plugin_log("info", "Validation plugin unloaded")
+ return true
+end
+
+-- Обработчик событий
+function on_event(event)
+ plugin_log("debug", "Received event: " .. event.type)
+
+ if event.type == "BEFORE_INSERT" then
+ return validate_document(event.data)
+ end
+
+ return true
+end
+
+-- Функция валидации документа
+function validate_document(doc)
+ -- Проверка обязательных полей
+ if doc.name == nil or doc.name == "" then
+ plugin_log("error", "Document missing required field: name")
+ return false, "Field 'name' is required"
+ end
+
+ -- Проверка возраста
+ if doc.age ~= nil then
+ if doc.age < 18 then
+ plugin_log("warn", "Age validation failed: " .. doc.age)
+ return false, "Employee must be at least 18 years old"
+ end
+ if doc.age > 65 then
+ plugin_log("warn", "Age validation failed: " .. doc.age)
+ return false, "Employee cannot be older than 65 years"
+ end
+ end
+
+ -- Проверка email
+ if doc.email ~= nil then
+ if string.match(doc.email, "^[%w._-]+@[%w._-]+%.[%w]+$") == nil then
+ plugin_log("error", "Invalid email format: " .. doc.email)
+ return false, "Invalid email format"
+ end
+ end
+
+ -- Проверка зарплаты
+ if doc.salary ~= nil then
+ if doc.salary < 30000 then
+ plugin_log("warn", "Salary below minimum: " .. doc.salary)
+ return false, "Salary must be at least 30000"
+ end
+ end
+
+ plugin_log("info", "Document validation passed for: " .. doc.name)
+ return true
+end
+
+-- Пользовательская функция для массовой валидации
+function validate_collection(collection_name)
+ local coll = get_collection("company", collection_name)
+ if coll == nil then
+ plugin_log("error", "Collection not found: " .. collection_name)
+ return 0
+ end
+
+ -- Здесь можно реализовать массовую валидацию
+ plugin_log("info", "Validating collection: " .. collection_name)
+ return 0
+end
+```
+
+**Использование плагина валидации документов**
+
+```sh
+# Создание базы данных и коллекции
+futriix:~> create database company
+✓ Database 'company' created
+
+futriix:~> use company
+✓ Switched to database 'company'
+
+futriix:~> create collection employees
+✓ Collection 'employees' created in database 'company'
+
+# Загрузка и запуск плагина валидации
+futriix:~> plugin load validation ./plugins/validation.lua
+✓ Plugin 'validation' loaded successfully
+ Version: 1.0.0
+ Author: admin
+ Description: Document validation rules for employees collection
+
+futriix:~> plugin start validation
+✓ Plugin 'validation' started
+
+# Вставка валидного документа
+futriix:~> insert employees name=John Doe,age=25,email=john@company.com,salary=45000
+✓ Document inserted with ID: emp_001
+
+# Вставка невалидного документа (возраст < 18)
+futriix:~> insert employees name=Jane Smith,age=16,email=jane@company.com,salary=20000
+Error: Employee must be at least 18 years old
+
+# Вставка невалидного документа (некорректный email)
+futriix:~> insert employees name=Bob Johnson,age=30,email=invalid-email,salary=50000
+Error: Invalid email format
+
+# Выполнение пользовательской функции плагина
+futriix:~> plugin call validation validate_collection employees
+✓ Function returned: 0
+```
+
+**Управление плагинами через HTTP API**
+
+```sh
+# Получение списка плагинов через API
+curl -X GET "http://localhost:8080/api/plugin/list" \
+ -H "X-Session-ID: abc123"
+
+# Загрузка плагина через API
+curl -X POST http://localhost:8080/api/plugin/load \
+ -H "Content-Type: application/json" \
+ -H "X-Session-ID: abc123" \
+ -d '{"name":"validation","path":"./plugins/validation.lua"}'
+
+# Запуск плагина через API
+curl -X POST "http://localhost:8080/api/plugin/start/validation" \
+ -H "X-Session-ID: abc123"
+
+# Выполнение функции плагина через API
+curl -X POST http://localhost:8080/api/plugin/call \
+ -H "Content-Type: application/json" \
+ -H "X-Session-ID: abc123" \
+ -d '{"plugin":"validation","function":"validate_collection","args":["employees"]}'
+```
+
+**Плагины как инструмент написания движков для futriix**
+
+Для написания нового движка (LSM-дерева, key-value или time-series) необходимо два файла: файл с названием самого движка с расширением **.lua**
+и файл **Манифест-плагина** с расширением **.json**
+
+**Манифест плагина-движка** — это JSON-файл, который сопровождает Lua-скрипт плагина и предоставляет системе метаданные о плагине. Манифест необходим для корректной загрузки, регистрации и управления плагинами, реализующими кастомные движки хранения данных.
+Расположение и именование
+
+Манифест должен располагаться в той же директории, что и Lua-скрипт плагина (по умолчанию `/futriix/plugins`), и иметь идентичное имя файла с расширением **.json**. Например:
+
+```sh
+plugins/
+├── timescale_engine.lua # Lua-скрипт движка
+└── timescale_engine.json # Манифест движка
+```
+
+**Структура манифеста**
+
+```sh
+{
+ "name": "timescale_engine",
+ "version": "1.0.0",
+ "author": "Example Corp",
+ "description": "Time-series storage engine with automatic partitioning",
+ "api_version": "1.0",
+ "engine_type": "timescale",
+ "min_go_version": "1.21",
+ "dependencies": [
+ {
+ "name": "base_engine",
+ "version": "2.0.0",
+ "min_version": "2.0.0",
+ "max_version": "3.0.0",
+ "optional": false
+ }
+ ],
+ "entry_point": "create_engine",
+ "created_at": 1704067200000,
+ "updated_at": 1704153600000
+}
+
+```
+
+**Поля манифеста**
+
+| Поле | Тип | Обязательное | Описание |
+|------|-----|--------------|----------|
+| `name` | string | ✅ | Уникальное имя плагина. Должно совпадать с именем Lua-файла (без расширения) |
+| `version` | string | ✅ | Версия плагина в формате Semantic Versioning (X.Y.Z) |
+| `author` | string | ❌ | Автор или организация-разработчик плагина |
+| `description` | string | ❌ | Краткое описание функциональности плагина |
+| `api_version` | string | ✅ | Версия API СУБД, с которой совместим плагин |
+| `engine_type` | string | ✅ | **Ключевое поле**. Определяет, что плагин является движком хранения. Значение используется как идентификатор движка при создании коллекций |
+| `min_go_version` | string | ❌ | Минимальная версия Go, необходимая для работы плагина |
+| `dependencies` | array | ❌ | Список зависимостей от других плагинов |
+| `entry_point` | string | ❌ | Имя Lua-функции-фабрики, создающей экземпляр движка. По умолчанию: `create_engine` |
+| `created_at` | int64 | ❌ | Время создания манифеста (Unix timestamp в миллисекундах) |
+| `updated_at` | int64 | ❌ | Время последнего обновления манифеста |
+
+
+
+**Структура зависимости**
+
+Каждая зависимость в массиве `dependencies` имеет следующую структуру:
+
+
+| Поле | Тип | Обязательное | Описание |
+|------|-----|--------------|----------|
+| `name` | string | ✅ | Имя зависимого плагина |
+| `version` | string | ❌ | Точная версия зависимого плагина (если указана, требует точного соответствия) |
+| `min_version` | string | ❌ | Минимальная допустимая версия (включительно) |
+| `max_version` | string | ❌ | Максимальная допустимая версия (исключительно) |
+| `optional` | boolean | ❌ | Является ли зависимость опциональной. По умолчанию: `false` |
+
+
+
+**Процесс загрузки плагина с манифестом**
+
+1. Обнаружение: Система сканирует директорию плагинов и находит Lua-файлы
+2. Чтение манифеста: При наличии JSON-файла с тем же именем система загружает и парсит его.
+
+ Валидация:
+
+ * Проверяется наличие обязательных полей (name, version, api_version)
+ * Проверяется, что engine_type не пуст (для движков)
+ * Проверяется совместимость версий зависимостей
+
+3. Регистрация движка: Если engine_type указан, система автоматически регистрирует плагин в EngineRegistry под этим именем
+4. Загрузка Lua-скрипта: Выполняется Lua-скрипт, который должен экспортировать фабричную функцию (по умолчанию create_engine)
+5. Вызов фабрики: При создании экземпляра движка для конкретной коллекции вызывается фабричная функция с передачей конфигурации
+
+**Пример использования**
+
+**Создание манифеста для Time-Series движка:**
+
+```sh
+{
+ "name": "ts_engine",
+ "version": "1.2.0",
+ "author": "Futriix Team",
+ "description": "High-performance time-series storage engine with automatic downsampling",
+ "api_version": "1.0",
+ "engine_type": "timeseries",
+ "dependencies": [
+ {
+ "name": "compression_plugin",
+ "min_version": "1.0.0",
+ "optional": false
+ }
+ ],
+ "entry_point": "new_timeseries_engine",
+ "created_at": 1704067200000,
+ "updated_at": 1704153600000
+}
+```
+
+**Рекомендации по версионированию**
+
+* Используйте Semantic Versioning (MAJOR.MINOR.PATCH)
+* Увеличивайте MAJOR-версию при несовместимых изменениях API движка
+* Увеличивайте MINOR-версию при добавлении новой функциональности
+* Увеличивайте PATCH-версию при исправлении ошибок
+
+
+**Обработка ошибок**
+
+При отсутствии манифеста система пытается загрузить плагин в "упрощённом режиме", используя значения по умолчанию. Однако для плагинов-движков манифест является обязательным, так как поле engine_type необходимо для корректной регистрации.
+
+Ошибки при разборе манифеста логируются, но не препятствуют загрузке Lua-скрипта (если это не плагин-движок). При критических ошибках (отсутствие engine_type у движка) загрузка прерывается с соответствующим сообщением.
+
+
(К началу)