refactor config, add example config

This commit is contained in:
Sky Johnson 2025-04-04 18:33:39 -05:00
parent 78556cea1d
commit d07673e088
3 changed files with 164 additions and 133 deletions

21
config-example.lua Normal file
View File

@ -0,0 +1,21 @@
server = {
port = 3117,
debug = false,
log_level = "info",
http_logging = false
}
runner = {
pool_size = GOMAXPROCS
}
dirs = {
routes = "./routes",
static = "./static",
fs = "./fs",
data = "./data",
override = "./override",
libs = {
"./libs"
}
}

View File

@ -61,9 +61,9 @@ func NewMoonshark(configPath string) (*Moonshark, error) {
server.LuaRouter, server.LuaRouter,
server.StaticRouter, server.StaticRouter,
server.LuaRunner, server.LuaRunner,
server.Config.HTTPLoggingEnabled, server.Config.Server.HTTPLogging,
server.Config.Debug, server.Config.Server.Debug,
server.Config.OverrideDir, server.Config.Dirs.Override,
server.Config, server.Config,
) )
@ -88,7 +88,7 @@ func (s *Moonshark) loadConfig(configPath string) error {
// setupLogging configures the logger based on config settings // setupLogging configures the logger based on config settings
func (s *Moonshark) setupLogging() { func (s *Moonshark) setupLogging() {
// Set log level from config // Set log level from config
switch s.Config.LogLevel { switch s.Config.Server.LogLevel {
case "warn": case "warn":
logger.SetLevel(logger.LevelWarning) logger.SetLevel(logger.LevelWarning)
case "error": case "error":
@ -102,7 +102,7 @@ func (s *Moonshark) setupLogging() {
} }
// Set debug mode if configured // Set debug mode if configured
if s.Config.Debug { if s.Config.Server.Debug {
logger.EnableDebug() // Force debug logs regardless of level logger.EnableDebug() // Force debug logs regardless of level
logger.Debug("Debug mode is ready to party, bro") logger.Debug("Debug mode is ready to party, bro")
} }
@ -111,17 +111,17 @@ func (s *Moonshark) setupLogging() {
// initRouters initializes the Lua and static routers // initRouters initializes the Lua and static routers
func (s *Moonshark) initRouters() error { func (s *Moonshark) initRouters() error {
// Ensure directories exist // Ensure directories exist
if err := utils.EnsureDir(s.Config.RoutesDir); err != nil { if err := utils.EnsureDir(s.Config.Dirs.Routes); err != nil {
return fmt.Errorf("routes directory doesn't exist and could not be created: %v", err) return fmt.Errorf("routes directory doesn't exist and could not be created: %v", err)
} }
if err := utils.EnsureDir(s.Config.StaticDir); err != nil { if err := utils.EnsureDir(s.Config.Dirs.Static); err != nil {
return fmt.Errorf("static directory doesn't exist and could not be created: %v", err) return fmt.Errorf("static directory doesn't exist and could not be created: %v", err)
} }
// Initialize Lua router for dynamic routes // Initialize Lua router for dynamic routes
var err error var err error
s.LuaRouter, err = routers.NewLuaRouter(s.Config.RoutesDir) s.LuaRouter, err = routers.NewLuaRouter(s.Config.Dirs.Routes)
if err != nil { if err != nil {
// Check if this is a compilation warning or a more serious error // Check if this is a compilation warning or a more serious error
if errors.Is(err, routers.ErrRoutesCompilationErrors) { if errors.Is(err, routers.ErrRoutesCompilationErrors) {
@ -139,14 +139,14 @@ func (s *Moonshark) initRouters() error {
return fmt.Errorf("failed to initialize Lua router: %v", err) return fmt.Errorf("failed to initialize Lua router: %v", err)
} }
} }
logger.Info("Lua router is stoked and riding routes from %s", s.Config.RoutesDir) logger.Info("Lua router is stoked and riding routes from %s", s.Config.Dirs.Routes)
// Initialize static file router // Initialize static file router
s.StaticRouter, err = routers.NewStaticRouter(s.Config.StaticDir) s.StaticRouter, err = routers.NewStaticRouter(s.Config.Dirs.Static)
if err != nil { if err != nil {
return fmt.Errorf("failed to initialize static router: %v", err) return fmt.Errorf("failed to initialize static router: %v", err)
} }
logger.Info("Static router catching waves with files from %s", s.Config.StaticDir) logger.Info("Static router catching waves with files from %s", s.Config.Dirs.Static)
s.StaticRouter.EnableDebugLog() s.StaticRouter.EnableDebugLog()
return nil return nil
@ -155,13 +155,13 @@ func (s *Moonshark) initRouters() error {
// initRunner initializes the Lua runner // initRunner initializes the Lua runner
func (s *Moonshark) initRunner() error { func (s *Moonshark) initRunner() error {
// Ensure override directory exists // Ensure override directory exists
if err := utils.EnsureDir(s.Config.OverrideDir); err != nil { if err := utils.EnsureDir(s.Config.Dirs.Override); err != nil {
logger.Warning("Override directory doesn't exist and could not be created: %v", err) 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 s.Config.Dirs.Override = "" // Disable overrides if directory can't be created
} }
// Ensure lib directories exist // Ensure lib directories exist
for _, dir := range s.Config.LibDirs { for _, dir := range s.Config.Dirs.Libs {
if err := utils.EnsureDir(dir); err != nil { if err := utils.EnsureDir(dir); err != nil {
logger.Warning("Lib directory doesn't exist and could not be created: %v", err) logger.Warning("Lib directory doesn't exist and could not be created: %v", err)
} }
@ -182,14 +182,14 @@ func (s *Moonshark) initRunner() error {
// Set up runner options // Set up runner options
runnerOpts := []runner.RunnerOption{ runnerOpts := []runner.RunnerOption{
runner.WithPoolSize(s.Config.PoolSize), runner.WithPoolSize(s.Config.Runner.PoolSize),
runner.WithLibDirs(s.Config.LibDirs...), runner.WithLibDirs(s.Config.Dirs.Libs...),
runner.WithSessionManager(sessionManager), runner.WithSessionManager(sessionManager),
runner.WithCSRFProtection(), runner.WithCSRFProtection(),
} }
// Add debug option conditionally // Add debug option conditionally
if s.Config.Debug { if s.Config.Server.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")
} }
@ -201,34 +201,30 @@ func (s *Moonshark) initRunner() error {
return fmt.Errorf("failed to initialize Lua runner: %v", err) return fmt.Errorf("failed to initialize Lua runner: %v", err)
} }
logger.Server("Lua runner waxing up with a pool size of %d", s.Config.PoolSize) logger.Server("Lua runner waxing up with a pool size of %d", s.Config.Runner.PoolSize)
return nil return nil
} }
// setupWatchers initializes and starts all configured file watchers // setupWatchers initializes and starts all configured file watchers
func (s *Moonshark) setupWatchers() error { func (s *Moonshark) setupWatchers() error {
// Set up watcher for Lua routes // Set up watcher for Lua routes
if s.Config.Watchers.Routes { luaRouterWatcher, err := watchers.WatchLuaRouter(s.LuaRouter, s.LuaRunner, s.Config.Dirs.Routes)
luaRouterWatcher, err := watchers.WatchLuaRouter(s.LuaRouter, s.LuaRunner, s.Config.RoutesDir) if err != nil {
if err != nil { logger.Warning("Failed to watch routes directory: %v", err)
logger.Warning("Failed to watch routes directory: %v", err) } else {
} else { s.cleanupFuncs = append(s.cleanupFuncs, luaRouterWatcher.Close)
s.cleanupFuncs = append(s.cleanupFuncs, luaRouterWatcher.Close)
}
} }
// Set up watchers for Lua modules libraries // 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.Dirs.Libs)
moduleWatchers, err := watchers.WatchLuaModules(s.LuaRunner, s.Config.LibDirs) if err != nil {
if err != nil { logger.Warning("Failed to watch Lua module directories: %v", err)
logger.Warning("Failed to watch Lua module directories: %v", err) } else {
} else { for _, watcher := range moduleWatchers {
for _, watcher := range moduleWatchers { w := watcher // Capture variable for closure
w := watcher // Capture variable for closure s.cleanupFuncs = append(s.cleanupFuncs, w.Close)
s.cleanupFuncs = append(s.cleanupFuncs, w.Close)
}
logger.Info("File watchers active for %d Lua module directories", len(moduleWatchers))
} }
logger.Info("File watchers active for %d Lua module directories", len(moduleWatchers))
} }
return nil return nil
@ -236,10 +232,10 @@ func (s *Moonshark) setupWatchers() error {
// Start starts the HTTP server // Start starts the HTTP server
func (s *Moonshark) Start() error { func (s *Moonshark) Start() error {
logger.Server("Surf's up on port %d!", s.Config.Port) logger.Server("Surf's up on port %d!", s.Config.Server.Port)
// Log HTTP logging status // Log HTTP logging status
if s.Config.HTTPLoggingEnabled { if s.Config.Server.HTTPLogging {
logger.ServerCont("HTTP logging is turned on - watch those waves") logger.ServerCont("HTTP logging is turned on - watch those waves")
} else { } else {
logger.ServerCont("HTTP logging is turned off - waves are flat bro") logger.ServerCont("HTTP logging is turned off - waves are flat bro")
@ -247,7 +243,7 @@ func (s *Moonshark) Start() error {
// Start the server in a non-blocking way // Start the server in a non-blocking way
go func() { go func() {
if err := s.HTTPServer.ListenAndServe(fmt.Sprintf(":%d", s.Config.Port)); err != nil { if err := s.HTTPServer.ListenAndServe(fmt.Sprintf(":%d", s.Config.Server.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)
} }

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"runtime" "runtime"
"strconv" "strconv"
"strings"
luajit "git.sharkk.net/Sky/LuaJIT-to-Go" luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
) )
@ -12,24 +13,26 @@ import (
// Config represents a configuration loaded from a Lua file // Config represents a configuration loaded from a Lua file
type Config struct { type Config struct {
// Server settings // Server settings
LogLevel string Server struct {
Port int Port int
Debug bool Debug bool
LogLevel string
HTTPLogging bool
}
// Runner settings
Runner struct {
PoolSize int
}
// Directory paths // Directory paths
RoutesDir string Dirs struct {
StaticDir string Routes string
OverrideDir string Static string
LibDirs []string FS string
Data string
// Performance settings Override string
PoolSize int Libs []string
// Feature flags
HTTPLoggingEnabled bool
Watchers struct {
Routes bool
Modules bool
} }
// Raw values map for custom values // Raw values map for custom values
@ -38,27 +41,29 @@ type Config struct {
// New creates a new configuration with default values // New creates a new configuration with default values
func New() *Config { func New() *Config {
return &Config{ config := &Config{
// Server defaults
LogLevel: "info",
Port: 3117,
Debug: false,
// Directory defaults
RoutesDir: "./routes",
StaticDir: "./static",
OverrideDir: "./override",
LibDirs: []string{"./libs"},
// Performance defaults - respect GOMAXPROCS
PoolSize: runtime.GOMAXPROCS(0),
// Feature flag defaults
HTTPLoggingEnabled: true,
// Initialize values map // Initialize values map
values: make(map[string]any), values: make(map[string]any),
} }
// Server defaults
config.Server.Port = 3117
config.Server.Debug = false
config.Server.LogLevel = "info"
config.Server.HTTPLogging = false
// Runner defaults
config.Runner.PoolSize = runtime.GOMAXPROCS(0)
// Dirs defaults
config.Dirs.Routes = "./routes"
config.Dirs.Static = "./static"
config.Dirs.FS = "./fs"
config.Dirs.Data = "./data"
config.Dirs.Override = "./override"
config.Dirs.Libs = []string{"./libs"}
return config
} }
// Load loads configuration from a Lua file // Load loads configuration from a Lua file
@ -82,19 +87,14 @@ func Load(filePath string) (*Config, error) {
// Store values directly to the config // Store values directly to the config
config.values = make(map[string]any) config.values = make(map[string]any)
// Extract all globals individually // Extract top-level tables
globalKeys := []string{ tables := []string{"server", "runner", "dirs"}
"debug", "log_level", "port", "routes_dir", "static_dir", for _, table := range tables {
"override_dir", "pool_size", "http_logging_enabled", state.GetGlobal(table)
"watchers", "lib_dirs", if state.IsTable(-1) {
} tableMap, err := state.ToTable(-1)
for _, key := range globalKeys {
state.GetGlobal(key)
if !state.IsNil(-1) {
value, err := state.ToValue(-1)
if err == nil { if err == nil {
config.values[key] = value config.values[table] = tableMap
} }
} }
state.Pop(1) state.Pop(1)
@ -107,54 +107,52 @@ func Load(filePath string) (*Config, error) {
} }
// applyConfig applies configuration values from the globals map // applyConfig applies configuration values from the globals map
func applyConfig(config *Config, globals map[string]any) { func applyConfig(config *Config, values map[string]any) {
// Server settings // Apply server settings
if v, ok := globals["debug"].(bool); ok { if serverTable, ok := values["server"].(map[string]any); ok {
config.Debug = v if v, ok := serverTable["port"].(float64); ok {
} config.Server.Port = int(v)
if v, ok := globals["log_level"].(string); ok { }
config.LogLevel = v if v, ok := serverTable["debug"].(bool); ok {
} config.Server.Debug = v
if v, ok := globals["port"].(float64); ok { }
config.Port = int(v) if v, ok := serverTable["log_level"].(string); ok {
} config.Server.LogLevel = v
}
// Directory paths if v, ok := serverTable["http_logging"].(bool); ok {
if v, ok := globals["routes_dir"].(string); ok { config.Server.HTTPLogging = v
config.RoutesDir = v
}
if v, ok := globals["static_dir"].(string); ok {
config.StaticDir = v
}
if v, ok := globals["override_dir"].(string); ok {
config.OverrideDir = v
}
// Performance settings
if v, ok := globals["pool_size"].(float64); ok {
config.PoolSize = int(v)
}
// Feature flags
if v, ok := globals["http_logging_enabled"].(bool); ok {
config.HTTPLoggingEnabled = v
}
// Handle lib_dirs array more efficiently
if libDirs, ok := globals["lib_dirs"]; ok {
if dirs := extractStringArray(libDirs); len(dirs) > 0 {
config.LibDirs = dirs
} }
} }
// Handle watchers table // Apply runner settings
if watchersVal, ok := globals["watchers"]; ok { if runnerTable, ok := values["runner"].(map[string]any); ok {
if watchers, ok := watchersVal.(map[string]any); ok { if v, ok := runnerTable["pool_size"].(float64); ok {
if v, ok := watchers["routes"].(bool); ok { config.Runner.PoolSize = int(v)
config.Watchers.Routes = v }
} }
if v, ok := watchers["modules"].(bool); ok {
config.Watchers.Modules = v // Apply dirs settings
if dirsTable, ok := values["dirs"].(map[string]any); ok {
if v, ok := dirsTable["routes"].(string); ok {
config.Dirs.Routes = v
}
if v, ok := dirsTable["static"].(string); ok {
config.Dirs.Static = v
}
if v, ok := dirsTable["fs"].(string); ok {
config.Dirs.FS = v
}
if v, ok := dirsTable["data"].(string); ok {
config.Dirs.Data = v
}
if v, ok := dirsTable["override"].(string); ok {
config.Dirs.Override = v
}
// Handle libs array
if libs, ok := dirsTable["libs"]; ok {
if libsArray := extractStringArray(libs); len(libsArray) > 0 {
config.Dirs.Libs = libsArray
} }
} }
} }
@ -188,13 +186,29 @@ func extractStringArray(value any) []string {
} }
// GetCustomValue returns any custom configuration value by key // GetCustomValue returns any custom configuration value by key
// Key can be a dotted path like "server.port"
func (c *Config) GetCustomValue(key string) any { func (c *Config) GetCustomValue(key string) any {
return c.values[key] parts := strings.Split(key, ".")
if len(parts) == 1 {
return c.values[key]
}
current := c.values
for _, part := range parts[:len(parts)-1] {
next, ok := current[part].(map[string]any)
if !ok {
return nil
}
current = next
}
return current[parts[len(parts)-1]]
} }
// GetCustomString returns a custom string configuration value // GetCustomString returns a custom string configuration value
func (c *Config) GetCustomString(key string, defaultValue string) string { func (c *Config) GetCustomString(key string, defaultValue string) string {
value := c.values[key] value := c.GetCustomValue(key)
if value == nil { if value == nil {
return defaultValue return defaultValue
} }
@ -219,7 +233,7 @@ func (c *Config) GetCustomString(key string, defaultValue string) string {
// GetCustomInt returns a custom integer configuration value // GetCustomInt returns a custom integer configuration value
func (c *Config) GetCustomInt(key string, defaultValue int) int { func (c *Config) GetCustomInt(key string, defaultValue int) int {
value := c.values[key] value := c.GetCustomValue(key)
if value == nil { if value == nil {
return defaultValue return defaultValue
} }
@ -248,7 +262,7 @@ func (c *Config) GetCustomInt(key string, defaultValue int) int {
// GetCustomBool returns a custom boolean configuration value // GetCustomBool returns a custom boolean configuration value
func (c *Config) GetCustomBool(key string, defaultValue bool) bool { func (c *Config) GetCustomBool(key string, defaultValue bool) bool {
value := c.values[key] value := c.GetCustomValue(key)
if value == nil { if value == nil {
return defaultValue return defaultValue
} }