Files
futriix/cmd/futriis/main.go

186 lines
6.0 KiB
Go
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.
/*
* 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 := cfg.API.Port
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, httpPort)
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, httpPort 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 (for curl or wget utils only): http://localhost:" + fmt.Sprintf("%d", httpPort) + "/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))
}