Moonshark/Moonshark.go
2025-04-02 22:22:03 -05:00

296 lines
8.0 KiB
Go

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
}