Moonshark/Moonshark.go

275 lines
7.9 KiB
Go

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) (*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
logger.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 {
logger.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)
}
}
logger.Info("Lua router initialized with routes from %s", routesDir)
// Initialize static file router
staticRouter, err := routers.NewStaticRouter(staticDir)
if err != nil {
return nil, nil, fmt.Errorf("failed to initialize static router: %v", err)
}
logger.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, config WatcherConfig) ([]func() error, error) {
var cleanupFuncs []func() error
// Set up watcher for Lua routes
if config.Routes {
luaRouterWatcher, err := watchers.WatchLuaRouter(luaRouter, luaRunner, routesDir)
if err != nil {
logger.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)
if err != nil {
logger.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)
if err != nil {
logger.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)
}
logger.Info("File watchers active for %d Lua module directories", len(moduleWatchers))
}
}
return cleanupFuncs, nil
}
func main() {
// Initialize global logger with debug level
logger.InitGlobalLogger(logger.LevelDebug, true)
logger.Server("Starting Moonshark server")
// Load configuration from config.lua
cfg, err := config.Load("config.lua")
if err != nil {
logger.Warning("Failed to load config.lua: %v", err)
logger.Info("Using default configuration")
cfg = config.New()
}
// Set log level from config
switch cfg.GetString("log_level", "info") {
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)
}
// Get debug mode setting
debugMode := cfg.GetBool("debug", false)
if debugMode {
logger.EnableDebug() // Force debug logs regardless of level
logger.Debug("Debug mode enabled")
}
// 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 {
logger.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 {
logger.Warning("Lib directory doesn't exist, and could not create it: %v", err)
}
}
// Initialize routers
luaRouter, staticRouter, err := initRouters(routesDir, staticDir)
if err != nil {
logger.Fatal("Router initialization failed: %v", err)
}
// Initialize Lua runner
luaRunner, err := runner.NewRunner(
runner.WithBufferSize(bufferSize),
runner.WithLibDirs(libDirs...),
runner.WithDebugEnabled(),
)
if err != nil {
logger.Fatal("Failed to initialize Lua runner: %v", err)
}
logger.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, watcherConfig)
if err != nil {
logger.Warning("Error setting up watchers: %v", err)
}
// Register cleanup functions
defer func() {
for _, cleanup := range cleanupFuncs {
if err := cleanup(); err != nil {
logger.Warning("Cleanup error: %v", err)
}
}
}()
httpLoggingEnabled := cfg.GetBool("http_logging_enabled", true)
if httpLoggingEnabled {
logger.Info("HTTP logging is enabled")
} else {
logger.Info("HTTP logging is disabled")
}
// Create HTTP server
server := http.New(luaRouter, staticRouter, luaRunner, httpLoggingEnabled, debugMode, overrideDir, cfg)
// 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" {
logger.Error("Server error: %v", err)
}
}
}()
logger.Server("Server started on port %d", port)
// Wait for interrupt signal
<-stop
logger.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 {
logger.Error("Server shutdown error: %v", err)
}
logger.Server("Server stopped")
}