package main import ( "context" "errors" "fmt" "os" "os/signal" "syscall" "time" "git.sharkk.net/Sky/Moonshark/core/config" "git.sharkk.net/Sky/Moonshark/core/http" "git.sharkk.net/Sky/Moonshark/core/logger" "git.sharkk.net/Sky/Moonshark/core/routers" "git.sharkk.net/Sky/Moonshark/core/runner" "git.sharkk.net/Sky/Moonshark/core/utils" "git.sharkk.net/Sky/Moonshark/core/watchers" ) // WatcherConfig holds the configuration for which watchers to enable type WatcherConfig struct { Routes bool Static bool Modules bool } // initRouters sets up the Lua and static routers func initRouters(routesDir, staticDir string, log *logger.Logger) (*routers.LuaRouter, *routers.StaticRouter, error) { // Ensure directories exist if err := utils.EnsureDir(routesDir); err != nil { return nil, nil, fmt.Errorf("routes directory doesn't exist, and could not create it: %v", err) } if err := utils.EnsureDir(staticDir); err != nil { return nil, nil, fmt.Errorf("static directory doesn't exist, and could not create it: %v", err) } // Initialize Lua router for dynamic routes luaRouter, err := routers.NewLuaRouter(routesDir) if err != nil { // Check if this is a compilation warning or a more serious error if errors.Is(err, routers.ErrRoutesCompilationErrors) { // Some routes failed to compile, but router is still usable log.Warning("Some Lua routes failed to compile. Check logs for details.") // Log details about each failed route if failedRoutes := luaRouter.ReportFailedRoutes(); len(failedRoutes) > 0 { for _, re := range failedRoutes { log.Error("Route %s %s failed to compile: %v", re.Method, re.Path, re.Err) } } } else { // More serious error that prevents router initialization return nil, nil, fmt.Errorf("failed to initialize Lua router: %v", err) } } log.Info("Lua router initialized with routes from %s", routesDir) // Initialize static file router staticRouter, err := routers.NewStaticRouterWithLogger(staticDir, log) if err != nil { return nil, nil, fmt.Errorf("failed to initialize static router: %v", err) } log.Info("Static router initialized with files from %s", staticDir) staticRouter.EnableDebugLog() return luaRouter, staticRouter, nil } // setupWatchers initializes and starts all file watchers func setupWatchers(luaRouter *routers.LuaRouter, staticRouter *routers.StaticRouter, luaRunner *runner.LuaRunner, routesDir string, staticDir string, libDirs []string, log *logger.Logger, config WatcherConfig) ([]func() error, error) { var cleanupFuncs []func() error // Set up watcher for Lua routes if config.Routes { luaRouterWatcher, err := watchers.WatchLuaRouter(luaRouter, routesDir, log) if err != nil { log.Warning("Failed to watch routes directory: %v", err) } else { cleanupFuncs = append(cleanupFuncs, luaRouterWatcher.Close) } } // Set up watcher for static files if config.Static { staticWatcher, err := watchers.WatchStaticRouter(staticRouter, staticDir, log) if err != nil { log.Warning("Failed to watch static directory: %v", err) } else { cleanupFuncs = append(cleanupFuncs, staticWatcher.Close) } } // Set up watchers for Lua modules libraries if config.Modules && len(libDirs) > 0 { moduleWatchers, err := watchers.WatchLuaModules(luaRunner, libDirs, log) if err != nil { log.Warning("Failed to watch Lua module directories: %v", err) } else { for _, watcher := range moduleWatchers { w := watcher // Capture variable for closure cleanupFuncs = append(cleanupFuncs, w.Close) } log.Info("File watchers active for %d Lua module directories", len(moduleWatchers)) } } return cleanupFuncs, nil } func main() { // Initialize logger log := logger.New(logger.LevelDebug, true) log.Server("Starting Moonshark server") // Load configuration from config.lua cfg, err := config.Load("config.lua") if err != nil { log.Warning("Failed to load config.lua: %v", err) log.Info("Using default configuration") cfg = config.New() } // Set log level from config switch cfg.GetString("log_level", "info") { case "debug": log.SetLevel(logger.LevelDebug) case "warn": log.SetLevel(logger.LevelWarning) case "error": log.SetLevel(logger.LevelError) case "server": log.SetLevel(logger.LevelServer) case "fatal": log.SetLevel(logger.LevelFatal) default: log.SetLevel(logger.LevelInfo) } // Get configuration values port := cfg.GetInt("port", 3117) routesDir := cfg.GetString("routes_dir", "./routes") staticDir := cfg.GetString("static_dir", "./static") overrideDir := cfg.GetString("override_dir", "./override") bufferSize := cfg.GetInt("buffer_size", 20) if err := utils.EnsureDir(overrideDir); err != nil { log.Warning("Override directory doesn't exist, and could not create it: %v", err) overrideDir = "" // Disable overrides if directory can't be created } // Get library directories var libDirs []string libDirsConfig := cfg.GetStringArray("lib_dirs") if len(libDirsConfig) > 0 { libDirs = libDirsConfig } else { // Default lib directory libDirs = []string{"./libs"} } // Ensure lib directories exist for _, dir := range libDirs { if err := utils.EnsureDir(dir); err != nil { log.Warning("Lib directory doesn't exist, and could not create it: %v", err) } } // Initialize routers luaRouter, staticRouter, err := initRouters(routesDir, staticDir, log) if err != nil { log.Fatal("Router initialization failed: %v", err) } // Initialize Lua runner luaRunner, err := runner.NewRunner( runner.WithBufferSize(bufferSize), runner.WithLibDirs(libDirs...), ) if err != nil { log.Fatal("Failed to initialize Lua runner: %v", err) } log.Server("Lua runner initialized with buffer size %d", bufferSize) defer luaRunner.Close() // Set up file watchers if enabled var cleanupFuncs []func() error // Get watcher configuration (default all disabled) watcherConfig := WatcherConfig{ Routes: false, Static: false, Modules: false, } // Enable watchers based on map configuration watchersMap := cfg.GetMap("watchers") if watchersMap != nil { if routes, ok := watchersMap["routes"].(bool); ok && routes { watcherConfig.Routes = true } if static, ok := watchersMap["static"].(bool); ok && static { watcherConfig.Static = true } if modules, ok := watchersMap["modules"].(bool); ok && modules { watcherConfig.Modules = true } } // Setup enabled watchers cleanupFuncs, err = setupWatchers(luaRouter, staticRouter, luaRunner, routesDir, staticDir, libDirs, log, watcherConfig) if err != nil { log.Warning("Error setting up watchers: %v", err) } // Register cleanup functions defer func() { for _, cleanup := range cleanupFuncs { if err := cleanup(); err != nil { log.Warning("Cleanup error: %v", err) } } }() httpLoggingEnabled := cfg.GetBool("http_logging_enabled", true) if httpLoggingEnabled { log.Info("HTTP logging is enabled") } else { log.Info("HTTP logging is disabled") } debugMode := cfg.GetBool("debug", false) // Create HTTP server server := http.New(luaRouter, staticRouter, luaRunner, log, httpLoggingEnabled, debugMode, overrideDir) // Handle graceful shutdown stop := make(chan os.Signal, 1) signal.Notify(stop, os.Interrupt, syscall.SIGTERM) // Start server in a goroutine go func() { if err := server.ListenAndServe(fmt.Sprintf(":%d", port)); err != nil { if err.Error() != "http: Server closed" { log.Error("Server error: %v", err) } } }() log.Server("Server started on port %d", port) // Wait for interrupt signal <-stop log.Server("Shutdown signal received") // Gracefully shut down the server ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := server.Shutdown(ctx); err != nil { log.Error("Server shutdown error: %v", err) } log.Server("Server stopped") }