Compare commits
2 Commits
6fadf26bea
...
2a2ffc9cc5
Author | SHA1 | Date | |
---|---|---|---|
2a2ffc9cc5 | |||
8a3515ed06 |
379
Moonshark.go
379
Moonshark.go
|
@ -4,10 +4,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"runtime"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.sharkk.net/Sky/Moonshark/core/config"
|
"git.sharkk.net/Sky/Moonshark/core/config"
|
||||||
|
@ -19,115 +15,79 @@ import (
|
||||||
"git.sharkk.net/Sky/Moonshark/core/watchers"
|
"git.sharkk.net/Sky/Moonshark/core/watchers"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WatcherConfig holds the configuration for which watchers to enable
|
// Moonshark represents the server and all its dependencies
|
||||||
type WatcherConfig struct {
|
type Moonshark struct {
|
||||||
Routes bool
|
Config *config.Config
|
||||||
Static bool
|
LuaRouter *routers.LuaRouter
|
||||||
Modules bool
|
StaticRouter *routers.StaticRouter
|
||||||
|
LuaRunner *runner.LuaRunner
|
||||||
|
HTTPServer *http.Server
|
||||||
|
|
||||||
|
// Clean-up functions for watchers
|
||||||
|
cleanupFuncs []func() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// initRouters sets up the Lua and static routers
|
// NewMoonshark creates a new Moonshark server instance
|
||||||
func initRouters(routesDir, staticDir string) (*routers.LuaRouter, *routers.StaticRouter, error) {
|
func NewMoonshark(configPath string) (*Moonshark, error) {
|
||||||
// Ensure directories exist
|
// Initialize server struct
|
||||||
if err := utils.EnsureDir(routesDir); err != nil {
|
server := &Moonshark{}
|
||||||
return nil, nil, fmt.Errorf("routes directory doesn't exist, and could not create it: %v", err)
|
|
||||||
}
|
// Load configuration
|
||||||
if err := utils.EnsureDir(staticDir); err != nil {
|
if err := server.loadConfig(configPath); err != nil {
|
||||||
return nil, nil, fmt.Errorf("static directory doesn't exist, and could not create it: %v", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize Lua router for dynamic routes
|
// Configure logging
|
||||||
luaRouter, err := routers.NewLuaRouter(routesDir)
|
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 {
|
if err != nil {
|
||||||
// Check if this is a compilation warning or a more serious error
|
logger.Warning("Failed to load config file: %v", err)
|
||||||
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")
|
logger.Info("Using default configuration")
|
||||||
cfg = config.New()
|
s.Config = config.New()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setupLogging configures the logger based on config settings
|
||||||
|
func (s *Moonshark) setupLogging() {
|
||||||
// Set log level from config
|
// Set log level from config
|
||||||
switch cfg.GetString("log_level", "info") {
|
switch s.Config.LogLevel {
|
||||||
case "warn":
|
case "warn":
|
||||||
logger.SetLevel(logger.LevelWarning)
|
logger.SetLevel(logger.LevelWarning)
|
||||||
case "error":
|
case "error":
|
||||||
|
@ -140,143 +100,180 @@ func main() {
|
||||||
logger.SetLevel(logger.LevelInfo)
|
logger.SetLevel(logger.LevelInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get debug mode setting
|
// Set debug mode if configured
|
||||||
debugMode := cfg.GetBool("debug", false)
|
if s.Config.Debug {
|
||||||
if debugMode {
|
|
||||||
logger.EnableDebug() // Force debug logs regardless of level
|
logger.EnableDebug() // Force debug logs regardless of level
|
||||||
logger.Debug("Debug mode enabled")
|
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")
|
|
||||||
poolSize := cfg.GetInt("pool_size", runtime.NumCPU())
|
|
||||||
|
|
||||||
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
|
// initRouters initializes the Lua and static routers
|
||||||
var libDirs []string
|
func (s *Moonshark) initRouters() error {
|
||||||
libDirsConfig := cfg.GetStringArray("lib_dirs")
|
// Ensure directories exist
|
||||||
if len(libDirsConfig) > 0 {
|
if err := utils.EnsureDir(s.Config.RoutesDir); err != nil {
|
||||||
libDirs = libDirsConfig
|
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 {
|
} else {
|
||||||
// Default lib directory
|
// More serious error that prevents router initialization
|
||||||
libDirs = []string{"./libs"}
|
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
|
// Ensure lib directories exist
|
||||||
for _, dir := range libDirs {
|
for _, dir := range s.Config.LibDirs {
|
||||||
if err := utils.EnsureDir(dir); err != nil {
|
if err := utils.EnsureDir(dir); err != nil {
|
||||||
logger.Warning("Lib directory doesn't exist, and could not create it: %v", err)
|
logger.Warning("Lib directory doesn't exist and could not be created: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize routers
|
// Set up runner options
|
||||||
luaRouter, staticRouter, err := initRouters(routesDir, staticDir)
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal("Router initialization failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize Lua runner with options
|
|
||||||
runnerOpts := []runner.RunnerOption{
|
runnerOpts := []runner.RunnerOption{
|
||||||
runner.WithPoolSize(poolSize),
|
runner.WithPoolSize(s.Config.PoolSize),
|
||||||
runner.WithLibDirs(libDirs...),
|
runner.WithLibDirs(s.Config.LibDirs...),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add debug option conditionally
|
// Add debug option conditionally
|
||||||
if debugMode {
|
if s.Config.Debug {
|
||||||
runnerOpts = append(runnerOpts, runner.WithDebugEnabled())
|
runnerOpts = append(runnerOpts, runner.WithDebugEnabled())
|
||||||
logger.Debug("Debug logging enabled for Lua runner")
|
logger.Debug("Debug logging enabled for Lua runner")
|
||||||
}
|
}
|
||||||
|
|
||||||
luaRunner, err := runner.NewRunner(runnerOpts...)
|
// Initialize the runner
|
||||||
|
var err error
|
||||||
|
s.LuaRunner, err = runner.NewRunner(runnerOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal("Failed to initialize Lua runner: %v", err)
|
return fmt.Errorf("failed to initialize Lua runner: %v", err)
|
||||||
}
|
|
||||||
logger.Server("Lua runner initialized with pool size %d", poolSize)
|
|
||||||
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
|
logger.Server("Lua runner initialized with pool size %d", s.Config.PoolSize)
|
||||||
watchersMap := cfg.GetMap("watchers")
|
return nil
|
||||||
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
|
// setupWatchers initializes and starts all configured file watchers
|
||||||
cleanupFuncs, err = setupWatchers(luaRouter, staticRouter, luaRunner,
|
func (s *Moonshark) setupWatchers() error {
|
||||||
routesDir, staticDir, libDirs, watcherConfig)
|
// 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 {
|
if err != nil {
|
||||||
logger.Warning("Error setting up watchers: %v", err)
|
logger.Warning("Failed to watch routes directory: %v", err)
|
||||||
|
} else {
|
||||||
|
s.cleanupFuncs = append(s.cleanupFuncs, luaRouterWatcher.Close)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register cleanup functions
|
// Set up watcher for static files
|
||||||
defer func() {
|
if s.Config.Watchers.Static {
|
||||||
for _, cleanup := range cleanupFuncs {
|
staticWatcher, err := watchers.WatchStaticRouter(s.StaticRouter, s.Config.StaticDir)
|
||||||
if err := cleanup(); err != nil {
|
if err != nil {
|
||||||
logger.Warning("Cleanup error: %v", err)
|
logger.Warning("Failed to watch static directory: %v", err)
|
||||||
|
} else {
|
||||||
|
s.cleanupFuncs = append(s.cleanupFuncs, staticWatcher.Close)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
|
|
||||||
httpLoggingEnabled := cfg.GetBool("http_logging_enabled", true)
|
// Set up watchers for Lua modules libraries
|
||||||
if httpLoggingEnabled {
|
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")
|
logger.Info("HTTP logging is enabled")
|
||||||
} else {
|
} else {
|
||||||
logger.Info("HTTP logging is disabled")
|
logger.Info("HTTP logging is disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create HTTP server
|
// Start the server in a non-blocking way
|
||||||
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() {
|
go func() {
|
||||||
if err := server.ListenAndServe(fmt.Sprintf(":%d", port)); err != nil {
|
if err := s.HTTPServer.ListenAndServe(fmt.Sprintf(":%d", s.Config.Port)); err != nil {
|
||||||
if err.Error() != "http: Server closed" {
|
if err.Error() != "http: Server closed" {
|
||||||
logger.Error("Server error: %v", err)
|
logger.Error("Server error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
logger.Server("Server started on port %d", port)
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for interrupt signal
|
// Shutdown gracefully shuts down the server
|
||||||
<-stop
|
func (s *Moonshark) Shutdown() error {
|
||||||
logger.Server("Shutdown signal received")
|
logger.Server("Shutting down server...")
|
||||||
|
|
||||||
// Gracefully shut down the server
|
// Shutdown HTTP server with timeout
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if err := server.Shutdown(ctx); err != nil {
|
if err := s.HTTPServer.Shutdown(ctx); err != nil {
|
||||||
logger.Error("Server shutdown error: %v", err)
|
logger.Error("Server shutdown error: %v", err)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Server("Server stopped")
|
// 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
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
||||||
)
|
)
|
||||||
|
@ -22,14 +23,17 @@ type Config struct {
|
||||||
LibDirs []string
|
LibDirs []string
|
||||||
|
|
||||||
// Performance settings
|
// Performance settings
|
||||||
BufferSize int
|
PoolSize int
|
||||||
PoolSize int // Number of Lua states in the pool
|
|
||||||
|
|
||||||
// Feature flags
|
// Feature flags
|
||||||
HTTPLoggingEnabled bool
|
HTTPLoggingEnabled bool
|
||||||
Watchers map[string]bool
|
Watchers struct {
|
||||||
|
Routes bool
|
||||||
|
Static bool
|
||||||
|
Modules bool
|
||||||
|
}
|
||||||
|
|
||||||
// Raw values map for backward compatibility and custom values
|
// Raw values map for custom values
|
||||||
values map[string]any
|
values map[string]any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,16 +51,11 @@ func New() *Config {
|
||||||
OverrideDir: "./override",
|
OverrideDir: "./override",
|
||||||
LibDirs: []string{"./libs"},
|
LibDirs: []string{"./libs"},
|
||||||
|
|
||||||
// Performance defaults
|
// Performance defaults - respect GOMAXPROCS
|
||||||
PoolSize: runtime.NumCPU(),
|
PoolSize: runtime.GOMAXPROCS(0),
|
||||||
|
|
||||||
// Feature flag defaults
|
// Feature flag defaults
|
||||||
HTTPLoggingEnabled: true,
|
HTTPLoggingEnabled: true,
|
||||||
Watchers: map[string]bool{
|
|
||||||
"routes": false,
|
|
||||||
"static": false,
|
|
||||||
"modules": false,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Initialize values map
|
// Initialize values map
|
||||||
values: make(map[string]any),
|
values: make(map[string]any),
|
||||||
|
@ -65,8 +64,8 @@ func New() *Config {
|
||||||
|
|
||||||
// Load loads configuration from a Lua file
|
// Load loads configuration from a Lua file
|
||||||
func Load(filePath string) (*Config, error) {
|
func Load(filePath string) (*Config, error) {
|
||||||
// Create a new Lua state
|
// For simple configs, use standard libraries to ensure proper loading
|
||||||
state := luajit.New()
|
state := luajit.New(true)
|
||||||
if state == nil {
|
if state == nil {
|
||||||
return nil, errors.New("failed to create Lua state")
|
return nil, errors.New("failed to create Lua state")
|
||||||
}
|
}
|
||||||
|
@ -75,193 +74,110 @@ func Load(filePath string) (*Config, error) {
|
||||||
// Create config with default values
|
// Create config with default values
|
||||||
config := New()
|
config := New()
|
||||||
|
|
||||||
// Execute the Lua file
|
// Execute the config file
|
||||||
if err := state.DoFile(filePath); err != nil {
|
if err := state.DoFile(filePath); err != nil {
|
||||||
return nil, fmt.Errorf("failed to load config file: %w", err)
|
return nil, fmt.Errorf("failed to load config file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract values from the Lua state
|
// Extract configuration values
|
||||||
if err := extractGlobals(state, config); err != nil {
|
extractConfig(state, config)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractGlobals extracts global variables from the Lua state
|
// extractConfig extracts configuration values directly by name
|
||||||
func extractGlobals(state *luajit.State, config *Config) error {
|
func extractConfig(state *luajit.State, config *Config) {
|
||||||
// Get the globals table (_G)
|
// Process known config values directly by key
|
||||||
state.GetGlobal("_G")
|
if value := extractValue(state, "debug"); value != nil {
|
||||||
if !state.IsTable(-1) {
|
|
||||||
state.Pop(1)
|
|
||||||
return errors.New("failed to get globals table")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pre-populate with standard globals for reference checking
|
|
||||||
stdGlobals := map[string]bool{
|
|
||||||
"_G": true, "_VERSION": true, "assert": true, "collectgarbage": true,
|
|
||||||
"coroutine": true, "debug": true, "dofile": true, "error": true,
|
|
||||||
"getmetatable": true, "io": true, "ipairs": true, "load": true,
|
|
||||||
"loadfile": true, "loadstring": true, "math": true, "next": true,
|
|
||||||
"os": true, "package": true, "pairs": true, "pcall": true,
|
|
||||||
"print": true, "rawequal": true, "rawget": true, "rawset": true,
|
|
||||||
"require": true, "select": true, "setmetatable": true, "string": true,
|
|
||||||
"table": true, "tonumber": true, "tostring": true, "type": true,
|
|
||||||
"unpack": true, "xpcall": true,
|
|
||||||
// LuaJIT specific globals
|
|
||||||
"jit": true, "bit": true, "ffi": true, "bit32": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// First, let's get the original globals to compare with user globals
|
|
||||||
originalGlobals := make(map[string]bool)
|
|
||||||
|
|
||||||
// Execute empty Lua state to get standard globals
|
|
||||||
emptyState := luajit.New()
|
|
||||||
if emptyState != nil {
|
|
||||||
defer emptyState.Close()
|
|
||||||
|
|
||||||
emptyState.GetGlobal("_G")
|
|
||||||
emptyState.PushNil() // Start iteration
|
|
||||||
for emptyState.Next(-2) {
|
|
||||||
if emptyState.IsString(-2) {
|
|
||||||
key := emptyState.ToString(-2)
|
|
||||||
originalGlobals[key] = true
|
|
||||||
}
|
|
||||||
emptyState.Pop(1) // Pop value, leave key for next iteration
|
|
||||||
}
|
|
||||||
emptyState.Pop(1) // Pop _G
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate through the globals table
|
|
||||||
state.PushNil() // Start iteration
|
|
||||||
for state.Next(-2) {
|
|
||||||
// Stack now has key at -2 and value at -1
|
|
||||||
|
|
||||||
// Get key as string
|
|
||||||
if !state.IsString(-2) {
|
|
||||||
state.Pop(1) // Pop value, leave key for next iteration
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
key := state.ToString(-2)
|
|
||||||
|
|
||||||
// Skip standard Lua globals, but only if they're not overridden by user
|
|
||||||
// (standard globals will be functions or tables, user values usually aren't)
|
|
||||||
valueType := state.GetType(-1)
|
|
||||||
|
|
||||||
// Skip functions, userdata, and threads regardless of origin
|
|
||||||
if valueType == luajit.TypeFunction || valueType == luajit.TypeUserData || valueType == luajit.TypeThread {
|
|
||||||
state.Pop(1)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// For known Lua globals, we need to see if they're the original or user-defined
|
|
||||||
if stdGlobals[key] {
|
|
||||||
// For simple value types, assume user-defined
|
|
||||||
if valueType == luajit.TypeBoolean || valueType == luajit.TypeNumber || valueType == luajit.TypeString {
|
|
||||||
// These are probably user values with standard names
|
|
||||||
} else if originalGlobals[key] {
|
|
||||||
// If it's in the original globals and not a simple type, skip it
|
|
||||||
state.Pop(1)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process based on key and type
|
|
||||||
processConfigValue(state, config, key, valueType)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pop the globals table
|
|
||||||
state.Pop(1)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// processConfigValue processes a specific config value from Lua
|
|
||||||
func processConfigValue(state *luajit.State, config *Config, key string, valueType luajit.LuaType) {
|
|
||||||
// Store in the values map first (for backward compatibility)
|
|
||||||
var value any
|
|
||||||
|
|
||||||
// Extract the value based on its type
|
|
||||||
switch valueType {
|
|
||||||
case luajit.TypeBoolean:
|
|
||||||
value = state.ToBoolean(-1)
|
|
||||||
case luajit.TypeNumber:
|
|
||||||
value = state.ToNumber(-1)
|
|
||||||
case luajit.TypeString:
|
|
||||||
value = state.ToString(-1)
|
|
||||||
case luajit.TypeTable:
|
|
||||||
// For tables, use the existing conversion logic
|
|
||||||
if table, err := state.ToTable(-1); err == nil {
|
|
||||||
value = table
|
|
||||||
|
|
||||||
// Special case for watchers table
|
|
||||||
if key == "watchers" {
|
|
||||||
processWatchersTable(config, table)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// Skip unsupported types
|
|
||||||
state.Pop(1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store in the values map
|
|
||||||
config.values[key] = value
|
|
||||||
|
|
||||||
// Now set specific struct fields based on key
|
|
||||||
switch key {
|
|
||||||
case "log_level":
|
|
||||||
if strVal, ok := value.(string); ok {
|
|
||||||
config.LogLevel = strVal
|
|
||||||
}
|
|
||||||
case "port":
|
|
||||||
if numVal, ok := value.(float64); ok {
|
|
||||||
config.Port = int(numVal)
|
|
||||||
}
|
|
||||||
case "debug":
|
|
||||||
if boolVal, ok := value.(bool); ok {
|
if boolVal, ok := value.(bool); ok {
|
||||||
config.Debug = boolVal
|
config.Debug = boolVal
|
||||||
}
|
}
|
||||||
case "routes_dir":
|
}
|
||||||
|
|
||||||
|
if value := extractValue(state, "log_level"); value != nil {
|
||||||
|
if strVal, ok := value.(string); ok {
|
||||||
|
config.LogLevel = strVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if value := extractValue(state, "port"); value != nil {
|
||||||
|
if numVal, ok := value.(float64); ok {
|
||||||
|
config.Port = int(numVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if value := extractValue(state, "routes_dir"); value != nil {
|
||||||
if strVal, ok := value.(string); ok {
|
if strVal, ok := value.(string); ok {
|
||||||
config.RoutesDir = strVal
|
config.RoutesDir = strVal
|
||||||
}
|
}
|
||||||
case "static_dir":
|
}
|
||||||
|
|
||||||
|
if value := extractValue(state, "static_dir"); value != nil {
|
||||||
if strVal, ok := value.(string); ok {
|
if strVal, ok := value.(string); ok {
|
||||||
config.StaticDir = strVal
|
config.StaticDir = strVal
|
||||||
}
|
}
|
||||||
case "override_dir":
|
}
|
||||||
|
|
||||||
|
if value := extractValue(state, "override_dir"); value != nil {
|
||||||
if strVal, ok := value.(string); ok {
|
if strVal, ok := value.(string); ok {
|
||||||
config.OverrideDir = strVal
|
config.OverrideDir = strVal
|
||||||
}
|
}
|
||||||
case "pool_size":
|
}
|
||||||
|
|
||||||
|
if value := extractValue(state, "pool_size"); value != nil {
|
||||||
if numVal, ok := value.(float64); ok {
|
if numVal, ok := value.(float64); ok {
|
||||||
config.PoolSize = int(numVal)
|
config.PoolSize = int(numVal)
|
||||||
}
|
}
|
||||||
case "http_logging_enabled":
|
}
|
||||||
|
|
||||||
|
if value := extractValue(state, "http_logging_enabled"); value != nil {
|
||||||
if boolVal, ok := value.(bool); ok {
|
if boolVal, ok := value.(bool); ok {
|
||||||
config.HTTPLoggingEnabled = boolVal
|
config.HTTPLoggingEnabled = boolVal
|
||||||
}
|
}
|
||||||
case "lib_dirs":
|
}
|
||||||
|
|
||||||
|
// Handle watchers table
|
||||||
|
if value := extractValue(state, "watchers"); value != nil {
|
||||||
|
if table, ok := value.(map[string]any); ok {
|
||||||
|
if routes, ok := table["routes"].(bool); ok {
|
||||||
|
config.Watchers.Routes = routes
|
||||||
|
}
|
||||||
|
if static, ok := table["static"].(bool); ok {
|
||||||
|
config.Watchers.Static = static
|
||||||
|
}
|
||||||
|
if modules, ok := table["modules"].(bool); ok {
|
||||||
|
config.Watchers.Modules = modules
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle lib_dirs array
|
// Handle lib_dirs array
|
||||||
processLibDirs(config, value)
|
if value := extractValue(state, "lib_dirs"); value != nil {
|
||||||
}
|
if dirs := extractStringArray(value); len(dirs) > 0 {
|
||||||
|
config.LibDirs = dirs
|
||||||
state.Pop(1) // Pop value, leave key for next iteration
|
|
||||||
}
|
|
||||||
|
|
||||||
// processWatchersTable processes the watchers table configuration
|
|
||||||
func processWatchersTable(config *Config, watchersTable map[string]any) {
|
|
||||||
for key, value := range watchersTable {
|
|
||||||
if boolVal, ok := value.(bool); ok {
|
|
||||||
config.Watchers[key] = boolVal
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// processLibDirs processes the lib_dirs array configuration
|
// extractValue extracts a specific global value from the Lua state
|
||||||
func processLibDirs(config *Config, value any) {
|
func extractValue(state *luajit.State, key string) any {
|
||||||
|
state.GetGlobal(key)
|
||||||
|
defer state.Pop(1)
|
||||||
|
|
||||||
|
if state.IsNil(-1) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := state.ToValue(-1)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractStringArray extracts a string array from various possible formats
|
||||||
|
func extractStringArray(value any) []string {
|
||||||
// Check if it's a direct array
|
// Check if it's a direct array
|
||||||
if arr, ok := value.([]any); ok {
|
if arr, ok := value.([]any); ok {
|
||||||
result := make([]string, 0, len(arr))
|
result := make([]string, 0, len(arr))
|
||||||
|
@ -270,32 +186,21 @@ func processLibDirs(config *Config, value any) {
|
||||||
result = append(result, str)
|
result = append(result, str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(result) > 0 {
|
return result
|
||||||
config.LibDirs = result
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if it's in our special array format (map with empty key)
|
// Check if it's in special array format (map with empty key)
|
||||||
valueMap, ok := value.(map[string]any)
|
valueMap, ok := value.(map[string]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
arr, ok := valueMap[""]
|
arr, ok := valueMap[""]
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle array format
|
// Handle different array types
|
||||||
if strArray := extractStringArray(arr); len(strArray) > 0 {
|
|
||||||
config.LibDirs = strArray
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// extractStringArray extracts a string array from various possible formats
|
|
||||||
func extractStringArray(arr any) []string {
|
|
||||||
// Check different possible array formats
|
|
||||||
switch arr := arr.(type) {
|
switch arr := arr.(type) {
|
||||||
case []string:
|
case []string:
|
||||||
return arr
|
return arr
|
||||||
|
@ -308,319 +213,100 @@ func extractStringArray(arr any) []string {
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
case []float64:
|
case []float64:
|
||||||
// Unlikely but handle numeric arrays too
|
|
||||||
result := make([]string, 0, len(arr))
|
result := make([]string, 0, len(arr))
|
||||||
for _, v := range arr {
|
for _, v := range arr {
|
||||||
result = append(result, fmt.Sprintf("%g", v))
|
result = append(result, fmt.Sprintf("%g", v))
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns a configuration value by key
|
// GetCustomValue returns any custom configuration value by key
|
||||||
func (c *Config) Get(key string) any {
|
func (c *Config) GetCustomValue(key string) any {
|
||||||
return c.values[key]
|
return c.values[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetString returns a string configuration value
|
// GetCustomString returns a custom string configuration value
|
||||||
func (c *Config) GetString(key string, defaultValue string) string {
|
func (c *Config) GetCustomString(key string, defaultValue string) string {
|
||||||
// Check for specific struct fields first
|
value := c.values[key]
|
||||||
switch key {
|
if value == nil {
|
||||||
case "log_level":
|
|
||||||
return c.LogLevel
|
|
||||||
case "routes_dir":
|
|
||||||
return c.RoutesDir
|
|
||||||
case "static_dir":
|
|
||||||
return c.StaticDir
|
|
||||||
case "override_dir":
|
|
||||||
return c.OverrideDir
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fall back to values map for other keys
|
|
||||||
value, ok := c.values[key]
|
|
||||||
if !ok {
|
|
||||||
return defaultValue
|
return defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
str, ok := value.(string)
|
// Convert to string
|
||||||
if !ok {
|
switch v := value.(type) {
|
||||||
|
case string:
|
||||||
|
return v
|
||||||
|
case float64:
|
||||||
|
return fmt.Sprintf("%g", v)
|
||||||
|
case int:
|
||||||
|
return strconv.Itoa(v)
|
||||||
|
case bool:
|
||||||
|
if v {
|
||||||
|
return "true"
|
||||||
|
}
|
||||||
|
return "false"
|
||||||
|
default:
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCustomInt returns a custom integer configuration value
|
||||||
|
func (c *Config) GetCustomInt(key string, defaultValue int) int {
|
||||||
|
value := c.values[key]
|
||||||
|
if value == nil {
|
||||||
return defaultValue
|
return defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
return str
|
// Convert to int
|
||||||
}
|
|
||||||
|
|
||||||
// GetInt returns an integer configuration value
|
|
||||||
func (c *Config) GetInt(key string, defaultValue int) int {
|
|
||||||
// Check for specific struct fields first
|
|
||||||
switch key {
|
|
||||||
case "port":
|
|
||||||
return c.Port
|
|
||||||
case "buffer_size":
|
|
||||||
return c.BufferSize
|
|
||||||
case "pool_size":
|
|
||||||
return c.PoolSize
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fall back to values map for other keys
|
|
||||||
value, ok := c.values[key]
|
|
||||||
if !ok {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle both int and float64 (which is what Lua numbers become in Go)
|
|
||||||
switch v := value.(type) {
|
switch v := value.(type) {
|
||||||
case int:
|
case int:
|
||||||
return v
|
return v
|
||||||
case float64:
|
case float64:
|
||||||
return int(v)
|
return int(v)
|
||||||
|
case string:
|
||||||
|
if i, err := strconv.Atoi(v); err == nil {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
case bool:
|
||||||
|
if v {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
default:
|
default:
|
||||||
return defaultValue
|
return defaultValue
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// GetFloat returns a float configuration value
|
|
||||||
func (c *Config) GetFloat(key string, defaultValue float64) float64 {
|
|
||||||
value, ok := c.values[key]
|
|
||||||
if !ok {
|
|
||||||
return defaultValue
|
return defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle both float64 and int
|
// GetCustomBool returns a custom boolean configuration value
|
||||||
|
func (c *Config) GetCustomBool(key string, defaultValue bool) bool {
|
||||||
|
value := c.values[key]
|
||||||
|
if value == nil {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to bool
|
||||||
switch v := value.(type) {
|
switch v := value.(type) {
|
||||||
case float64:
|
case bool:
|
||||||
return v
|
return v
|
||||||
|
case string:
|
||||||
|
switch v {
|
||||||
|
case "true", "yes", "1":
|
||||||
|
return true
|
||||||
|
case "false", "no", "0", "":
|
||||||
|
return false
|
||||||
|
}
|
||||||
case int:
|
case int:
|
||||||
return float64(v)
|
return v != 0
|
||||||
|
case float64:
|
||||||
|
return v != 0
|
||||||
default:
|
default:
|
||||||
return defaultValue
|
return defaultValue
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// GetBool returns a boolean configuration value
|
|
||||||
func (c *Config) GetBool(key string, defaultValue bool) bool {
|
|
||||||
// Check for specific struct fields first
|
|
||||||
switch key {
|
|
||||||
case "debug":
|
|
||||||
return c.Debug
|
|
||||||
case "http_logging_enabled":
|
|
||||||
return c.HTTPLoggingEnabled
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special case for watcher settings
|
|
||||||
if key == "watchers.routes" {
|
|
||||||
return c.Watchers["routes"]
|
|
||||||
} else if key == "watchers.static" {
|
|
||||||
return c.Watchers["static"]
|
|
||||||
} else if key == "watchers.modules" {
|
|
||||||
return c.Watchers["modules"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fall back to values map for other keys
|
|
||||||
value, ok := c.values[key]
|
|
||||||
if !ok {
|
|
||||||
return defaultValue
|
return defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
boolValue, ok := value.(bool)
|
|
||||||
if !ok {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
return boolValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMap returns a map configuration value
|
|
||||||
func (c *Config) GetMap(key string) map[string]any {
|
|
||||||
// Special case for watchers
|
|
||||||
if key == "watchers" {
|
|
||||||
result := make(map[string]any)
|
|
||||||
for k, v := range c.Watchers {
|
|
||||||
result[k] = v
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
value, ok := c.values[key]
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
table, ok := value.(map[string]any)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return table
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetArray returns an array of values from a Lua array
|
|
||||||
func (c *Config) GetArray(key string) []any {
|
|
||||||
// Special case for lib_dirs
|
|
||||||
if key == "lib_dirs" {
|
|
||||||
result := make([]any, len(c.LibDirs))
|
|
||||||
for i, v := range c.LibDirs {
|
|
||||||
result[i] = v
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
value := c.Get(key)
|
|
||||||
if value == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Direct array
|
|
||||||
if arr, ok := value.([]any); ok {
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arrays in Lua might also be represented as maps with an empty string key
|
|
||||||
valueMap, ok := value.(map[string]any)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// The array data is stored with an empty key
|
|
||||||
arr, ok := valueMap[""]
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if it's a float64 array (common for Lua numeric arrays)
|
|
||||||
if floatArr, ok := arr.([]float64); ok {
|
|
||||||
// Convert to []any
|
|
||||||
result := make([]any, len(floatArr))
|
|
||||||
for i, v := range floatArr {
|
|
||||||
result[i] = v
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, try to return as is
|
|
||||||
anyArr, ok := arr.([]any)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return anyArr
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetIntArray returns an array of integers from a Lua array
|
|
||||||
func (c *Config) GetIntArray(key string) []int {
|
|
||||||
value := c.Get(key)
|
|
||||||
if value == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Direct array case
|
|
||||||
if arr, ok := value.([]any); ok {
|
|
||||||
result := make([]int, 0, len(arr))
|
|
||||||
for _, v := range arr {
|
|
||||||
if num, ok := v.(float64); ok {
|
|
||||||
result = append(result, int(num))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arrays in Lua might also be represented as maps with an empty string key
|
|
||||||
valueMap, ok := value.(map[string]any)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// The array data is stored with an empty key
|
|
||||||
arr, ok := valueMap[""]
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// For numeric arrays, LuaJIT returns []float64
|
|
||||||
floatArr, ok := arr.([]float64)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to int slice
|
|
||||||
result := make([]int, len(floatArr))
|
|
||||||
for i, v := range floatArr {
|
|
||||||
result[i] = int(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStringArray returns an array of strings from a Lua array
|
|
||||||
func (c *Config) GetStringArray(key string) []string {
|
|
||||||
// Special case for lib_dirs
|
|
||||||
if key == "lib_dirs" {
|
|
||||||
return c.LibDirs
|
|
||||||
}
|
|
||||||
|
|
||||||
arr := c.GetArray(key)
|
|
||||||
if arr == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
result := make([]string, 0, len(arr))
|
|
||||||
for _, v := range arr {
|
|
||||||
if str, ok := v.(string); ok {
|
|
||||||
result = append(result, str)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Values returns all configuration values
|
|
||||||
// Note: The returned map should not be modified
|
|
||||||
func (c *Config) Values() map[string]any {
|
|
||||||
return c.values
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets a configuration value
|
|
||||||
func (c *Config) Set(key string, value any) {
|
|
||||||
c.values[key] = value
|
|
||||||
|
|
||||||
// Also update the struct field if applicable
|
|
||||||
switch key {
|
|
||||||
case "log_level":
|
|
||||||
if strVal, ok := value.(string); ok {
|
|
||||||
c.LogLevel = strVal
|
|
||||||
}
|
|
||||||
case "port":
|
|
||||||
if numVal, ok := value.(float64); ok {
|
|
||||||
c.Port = int(numVal)
|
|
||||||
} else if intVal, ok := value.(int); ok {
|
|
||||||
c.Port = intVal
|
|
||||||
}
|
|
||||||
case "debug":
|
|
||||||
if boolVal, ok := value.(bool); ok {
|
|
||||||
c.Debug = boolVal
|
|
||||||
}
|
|
||||||
case "routes_dir":
|
|
||||||
if strVal, ok := value.(string); ok {
|
|
||||||
c.RoutesDir = strVal
|
|
||||||
}
|
|
||||||
case "static_dir":
|
|
||||||
if strVal, ok := value.(string); ok {
|
|
||||||
c.StaticDir = strVal
|
|
||||||
}
|
|
||||||
case "override_dir":
|
|
||||||
if strVal, ok := value.(string); ok {
|
|
||||||
c.OverrideDir = strVal
|
|
||||||
}
|
|
||||||
case "pool_size":
|
|
||||||
if numVal, ok := value.(float64); ok {
|
|
||||||
c.PoolSize = int(numVal)
|
|
||||||
} else if intVal, ok := value.(int); ok {
|
|
||||||
c.PoolSize = intVal
|
|
||||||
}
|
|
||||||
case "http_logging_enabled":
|
|
||||||
if boolVal, ok := value.(bool); ok {
|
|
||||||
c.HTTPLoggingEnabled = boolVal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ func WithDebugEnabled() RunnerOption {
|
||||||
func NewRunner(options ...RunnerOption) (*LuaRunner, error) {
|
func NewRunner(options ...RunnerOption) (*LuaRunner, error) {
|
||||||
// Default configuration
|
// Default configuration
|
||||||
runner := &LuaRunner{
|
runner := &LuaRunner{
|
||||||
poolSize: runtime.GOMAXPROCS(0), // Default pool size to number of CPUs
|
poolSize: runtime.GOMAXPROCS(0),
|
||||||
debug: false,
|
debug: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
luajit
2
luajit
|
@ -1 +1 @@
|
||||||
Subproject commit 0756cabcaaf1e33f2b8eb535e5a24e448c2501a9
|
Subproject commit a2b4b1c9272f849d9c1c913366f822e0be904ba2
|
39
main.go
Normal file
39
main.go
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"git.sharkk.net/Sky/Moonshark/core/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Initialize global logger
|
||||||
|
logger.InitGlobalLogger(logger.LevelDebug, true)
|
||||||
|
logger.Server("Starting Moonshark server")
|
||||||
|
|
||||||
|
// Create and initialize server
|
||||||
|
server, err := NewMoonshark("config.lua")
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal("Failed to initialize server: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start server
|
||||||
|
if err := server.Start(); err != nil {
|
||||||
|
logger.Fatal("Failed to start server: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for interrupt signal
|
||||||
|
stop := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
|
||||||
|
<-stop
|
||||||
|
|
||||||
|
logger.Server("Shutdown signal received")
|
||||||
|
|
||||||
|
// Shutdown server
|
||||||
|
if err := server.Shutdown(); err != nil {
|
||||||
|
logger.Error("Error during shutdown: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user