package main import ( "context" "errors" "fmt" "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/sessions" "git.sharkk.net/Sky/Moonshark/core/utils" "git.sharkk.net/Sky/Moonshark/core/watchers" ) // Moonshark represents the server and all its dependencies type Moonshark struct { Config *config.Config LuaRouter *routers.LuaRouter StaticRouter *routers.StaticRouter LuaRunner *runner.LuaRunner HTTPServer *http.Server // Clean-up functions for watchers cleanupFuncs []func() error } // NewMoonshark creates a new Moonshark server instance func NewMoonshark(configPath string) (*Moonshark, error) { // Initialize server struct server := &Moonshark{} // Load configuration if err := server.loadConfig(configPath); err != nil { return nil, err } // Configure logging server.setupLogging() // Initialize routers if err := server.initRouters(); err != nil { return nil, err } // Initialize Lua runner if err := server.initRunner(); err != nil { return nil, err } // Set up file watchers if err := server.setupWatchers(); err != nil { logger.Warning("Error setting up watchers: %v", err) } // Create HTTP server server.HTTPServer = http.New( server.LuaRouter, server.StaticRouter, server.LuaRunner, server.Config.HTTPLoggingEnabled, server.Config.Debug, server.Config.OverrideDir, server.Config, ) return server, nil } // loadConfig loads the configuration file func (s *Moonshark) loadConfig(configPath string) error { var err error // Load configuration from file s.Config, err = config.Load(configPath) if err != nil { logger.Warning("Failed to load config file: %v", err) logger.Info("Using default configuration") s.Config = config.New() } return nil } // setupLogging configures the logger based on config settings func (s *Moonshark) setupLogging() { // Set log level from config switch s.Config.LogLevel { case "warn": logger.SetLevel(logger.LevelWarning) case "error": logger.SetLevel(logger.LevelError) case "server": logger.SetLevel(logger.LevelServer) case "fatal": logger.SetLevel(logger.LevelFatal) default: logger.SetLevel(logger.LevelInfo) } // Set debug mode if configured if s.Config.Debug { logger.EnableDebug() // Force debug logs regardless of level logger.Debug("Debug mode enabled") } } // initRouters initializes the Lua and static routers func (s *Moonshark) initRouters() error { // Ensure directories exist if err := utils.EnsureDir(s.Config.RoutesDir); err != nil { return fmt.Errorf("routes directory doesn't exist and could not be created: %v", err) } if err := utils.EnsureDir(s.Config.StaticDir); err != nil { return fmt.Errorf("static directory doesn't exist and could not be created: %v", err) } // Initialize Lua router for dynamic routes var err error s.LuaRouter, err = routers.NewLuaRouter(s.Config.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 logger.Warning("Some Lua routes failed to compile. Check logs for details.") // Log details about each failed route if failedRoutes := s.LuaRouter.ReportFailedRoutes(); len(failedRoutes) > 0 { for _, re := range failedRoutes { logger.Error("Route %s %s failed to compile: %v", re.Method, re.Path, re.Err) } } } else { // More serious error that prevents router initialization return fmt.Errorf("failed to initialize Lua router: %v", err) } } logger.Info("Lua router initialized with routes from %s", s.Config.RoutesDir) // Initialize static file router s.StaticRouter, err = routers.NewStaticRouter(s.Config.StaticDir) if err != nil { return fmt.Errorf("failed to initialize static router: %v", err) } logger.Info("Static router initialized with files from %s", s.Config.StaticDir) s.StaticRouter.EnableDebugLog() return nil } // initRunner initializes the Lua runner func (s *Moonshark) initRunner() error { // Ensure override directory exists if err := utils.EnsureDir(s.Config.OverrideDir); err != nil { logger.Warning("Override directory doesn't exist and could not be created: %v", err) s.Config.OverrideDir = "" // Disable overrides if directory can't be created } // Ensure lib directories exist for _, dir := range s.Config.LibDirs { if err := utils.EnsureDir(dir); err != nil { logger.Warning("Lib directory doesn't exist and could not be created: %v", err) } } // Initialize session manager sessionManager := sessions.GlobalSessionManager // Configure session cookies sessionManager.SetCookieOptions( "MSESSID", // name "/", // path "", // domain false, // secure true, // httpOnly 86400, // maxAge (1 day) ) // Set up runner options runnerOpts := []runner.RunnerOption{ runner.WithPoolSize(s.Config.PoolSize), runner.WithLibDirs(s.Config.LibDirs...), runner.WithSessionManager(sessionManager), runner.WithCSRFProtection(), } // Add debug option conditionally if s.Config.Debug { runnerOpts = append(runnerOpts, runner.WithDebugEnabled()) logger.Debug("Debug logging enabled for Lua runner") } // Initialize the runner var err error s.LuaRunner, err = runner.NewRunner(runnerOpts...) if err != nil { return fmt.Errorf("failed to initialize Lua runner: %v", err) } logger.Server("Lua runner initialized with pool size %d", s.Config.PoolSize) return nil } // setupWatchers initializes and starts all configured file watchers func (s *Moonshark) setupWatchers() error { // Set up watcher for Lua routes if s.Config.Watchers.Routes { luaRouterWatcher, err := watchers.WatchLuaRouter(s.LuaRouter, s.LuaRunner, s.Config.RoutesDir) if err != nil { logger.Warning("Failed to watch routes directory: %v", err) } else { s.cleanupFuncs = append(s.cleanupFuncs, luaRouterWatcher.Close) } } // Set up watcher for static files if s.Config.Watchers.Static { staticWatcher, err := watchers.WatchStaticRouter(s.StaticRouter, s.Config.StaticDir) if err != nil { logger.Warning("Failed to watch static directory: %v", err) } else { s.cleanupFuncs = append(s.cleanupFuncs, staticWatcher.Close) } } // Set up watchers for Lua modules libraries if s.Config.Watchers.Modules && len(s.Config.LibDirs) > 0 { moduleWatchers, err := watchers.WatchLuaModules(s.LuaRunner, s.Config.LibDirs) if err != nil { logger.Warning("Failed to watch Lua module directories: %v", err) } else { for _, watcher := range moduleWatchers { w := watcher // Capture variable for closure s.cleanupFuncs = append(s.cleanupFuncs, w.Close) } logger.Info("File watchers active for %d Lua module directories", len(moduleWatchers)) } } return nil } // Start starts the HTTP server func (s *Moonshark) Start() error { logger.Server("Starting server on port %d", s.Config.Port) // Log HTTP logging status if s.Config.HTTPLoggingEnabled { logger.Info("HTTP logging is enabled") } else { logger.Info("HTTP logging is disabled") } // Start the server in a non-blocking way go func() { if err := s.HTTPServer.ListenAndServe(fmt.Sprintf(":%d", s.Config.Port)); err != nil { if err.Error() != "http: Server closed" { logger.Error("Server error: %v", err) } } }() return nil } // Shutdown gracefully shuts down the server func (s *Moonshark) Shutdown() error { logger.Server("Shutting down server...") // Shutdown HTTP server with timeout ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := s.HTTPServer.Shutdown(ctx); err != nil { logger.Error("Server shutdown error: %v", err) return err } // Run cleanup functions for _, cleanup := range s.cleanupFuncs { if err := cleanup(); err != nil { logger.Warning("Cleanup error: %v", err) } } // Close Lua runner s.LuaRunner.Close() logger.Server("Server stopped") return nil }