first commit

This commit is contained in:
2026-04-19 16:42:41 +03:00
commit e82fb947be
37 changed files with 14591 additions and 0 deletions

185
cmd/futriis/main.go Normal file
View File

@@ -0,0 +1,185 @@
/*
* Copyright 2026 Safronov Grigorii
*
* Licensed under the CDDL, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
*
* You may obtain a copy of the License at
* https://opensource.org/licenses/CDDL-1.0
*/
// Файл: cmd/futriis/main.go
// Назначение: Точка входа в приложение СУБД futriis. Инициализирует все компоненты:
// конфигурацию, логгер, хранилище, Raft координатор, ACL менеджер, HTTP API, WebUI и REPL.
// Управляет жизненным циклом приложения.
package main
import (
"encoding/json"
"fmt"
"os"
"os/signal"
"syscall"
"time"
"futriis/internal/acl"
"futriis/internal/api"
"futriis/internal/cluster"
"futriis/internal/config"
"futriis/internal/log"
"futriis/internal/repl"
"futriis/internal/storage"
"futriis/pkg/utils"
)
func main() {
utils.SetColorEnabled(true)
cfg, err := config.LoadConfig("config.toml")
if err != nil {
utils.PrintError("Failed to load config: " + err.Error())
os.Exit(1)
}
logger, err := log.NewLogger(cfg.Log.LogFile, cfg.Log.LogLevel)
if err != nil {
utils.PrintError("Failed to initialize logger: " + err.Error())
os.Exit(1)
}
defer logger.Close()
logger.Info("futriis database starting...")
store := storage.NewStorage(cfg.Storage.PageSizeMB, logger)
// Инициализация ACL менеджера
aclManager := acl.NewACLManager()
logger.Info("ACL manager initialized")
raftCoordinator, err := cluster.NewRaftCoordinator(cfg, logger)
if err != nil {
logger.Error("Failed to start Raft coordinator: " + err.Error())
utils.PrintError("Failed to start Raft coordinator: " + err.Error())
os.Exit(1)
}
if cfg.Cluster.Bootstrap || len(cfg.Cluster.Nodes) <= 1 {
maxRetries := 10
for i := 0; i < maxRetries; i++ {
if raftCoordinator.IsLeader() {
break
}
time.Sleep(1 * time.Second)
}
}
node := cluster.NewNode(cfg.Cluster.NodeIP, cfg.Cluster.NodePort, store, logger)
maxRetries := 5
var registerErr error
for i := 0; i < maxRetries; i++ {
registerErr = raftCoordinator.RegisterNode(node)
if registerErr == nil {
break
}
if i < maxRetries-1 {
time.Sleep(2 * time.Second)
}
}
if registerErr != nil {
logger.Error("Failed to register node: " + registerErr.Error())
utils.PrintError("Failed to register node: " + registerErr.Error())
os.Exit(1)
}
// Запуск HTTP API сервера
httpPort := 8080
httpServer := api.NewHTTPServer(httpPort, store, raftCoordinator, aclManager, logger)
go func() {
if err := httpServer.Start(); err != nil {
logger.Error("HTTP server error: " + err.Error())
utils.PrintError("HTTP server error: " + err.Error())
}
}()
logger.Info(fmt.Sprintf("HTTP API server started on port %d", httpPort))
// Запуск WebUI сервера (если включён в конфигурации)
webUIPort := cfg.WebUI.Port
if webUIPort == 0 {
webUIPort = 8081
}
webUI := api.NewWebUIServer(webUIPort, cfg.WebUI.Enabled, store, raftCoordinator, aclManager, logger)
go func() {
if err := webUI.Start(); err != nil && cfg.WebUI.Enabled {
logger.Error("Web UI error: " + err.Error())
utils.PrintError("Web UI error: " + err.Error())
}
}()
if cfg.WebUI.Enabled {
logger.Info(fmt.Sprintf("Web UI started on port %d", webUIPort))
utils.PrintInfo(fmt.Sprintf("Web UI available at http://localhost:%d", webUIPort))
}
displayBanner(cfg.Cluster.Name, cfg.WebUI.Enabled, webUIPort)
replInstance := repl.NewRepl(store, raftCoordinator, logger, cfg)
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigChan
utils.Println("\nReceived shutdown signal")
logger.Info("Received shutdown signal")
httpServer.Stop()
webUI.Stop()
raftCoordinator.Stop()
node.Stop()
replInstance.Close()
utils.DisableColorMode()
os.Exit(0)
}()
if err := replInstance.Run(); err != nil {
logger.Error("REPL error: " + err.Error())
utils.PrintError("REPL error: " + err.Error())
os.Exit(1)
}
}
func displayBanner(clusterName string, webUIEnabled bool, webUIPort int) {
utils.Println("")
bannerLines := []string{
" futriix 3i²(by 02.04.2026) ",
" Distributed Document-Store in-memory database with support lua plugins ",
" Cluster status: enable (Raft consensus)",
" Cluster name: " + clusterName,
" HTTP API: http://localhost:8080/api/",
}
if webUIEnabled {
bannerLines = append(bannerLines, fmt.Sprintf(" Web UI: http://localhost:%d/", webUIPort))
}
bannerLines = append(bannerLines, []string{
" Type 'quit' or 'exit' to quit",
" Type 'status' to see cluster status",
" Type 'acl login <user> <pass>' to authenticate",
}...)
for _, line := range bannerLines {
utils.PrintInfo(line)
}
}
// Вспомогательная функция для форматирования JSON (если понадобится)
func printJSON(data interface{}) {
jsonData, err := json.MarshalIndent(data, "", " ")
if err != nil {
utils.PrintError("Failed to marshal JSON: " + err.Error())
return
}
fmt.Println(string(jsonData))
}