config rewrite
This commit is contained in:
parent
3f86c31c9f
commit
ac646bbad8
3
go.mod
3
go.mod
@ -4,6 +4,7 @@ go 1.24.1
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
git.sharkk.net/Go/LRU v1.0.0
|
git.sharkk.net/Go/LRU v1.0.0
|
||||||
|
git.sharkk.net/Sharkk/Fin v1.2.0
|
||||||
git.sharkk.net/Sky/LuaJIT-to-Go v0.4.1
|
git.sharkk.net/Sky/LuaJIT-to-Go v0.4.1
|
||||||
github.com/VictoriaMetrics/fastcache v1.12.4
|
github.com/VictoriaMetrics/fastcache v1.12.4
|
||||||
github.com/alexedwards/argon2id v1.0.0
|
github.com/alexedwards/argon2id v1.0.0
|
||||||
@ -26,7 +27,7 @@ require (
|
|||||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
golang.org/x/crypto v0.38.0 // indirect
|
golang.org/x/crypto v0.38.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
|
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b // indirect
|
||||||
golang.org/x/sys v0.33.0 // indirect
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
modernc.org/libc v1.65.8 // indirect
|
modernc.org/libc v1.65.8 // indirect
|
||||||
modernc.org/mathutil v1.7.1 // indirect
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
|
6
go.sum
6
go.sum
@ -1,5 +1,7 @@
|
|||||||
git.sharkk.net/Go/LRU v1.0.0 h1:/KqdRVhHldi23aVfQZ4ss6vhCWZqA3vFiQyf1MJPpQc=
|
git.sharkk.net/Go/LRU v1.0.0 h1:/KqdRVhHldi23aVfQZ4ss6vhCWZqA3vFiQyf1MJPpQc=
|
||||||
git.sharkk.net/Go/LRU v1.0.0/go.mod h1:8tdTyl85mss9a+KKwo+Wj9gKHOizhfLfpJhz1ltYz50=
|
git.sharkk.net/Go/LRU v1.0.0/go.mod h1:8tdTyl85mss9a+KKwo+Wj9gKHOizhfLfpJhz1ltYz50=
|
||||||
|
git.sharkk.net/Sharkk/Fin v1.2.0 h1:axhme8vHRYoaB3us7PNfXzXxKOxhpS5BMuNpN8ESe6U=
|
||||||
|
git.sharkk.net/Sharkk/Fin v1.2.0/go.mod h1:ca0Ej9yCM/vHh1o3YMvBZspme3EtbwoEL2UXN5UPXMo=
|
||||||
git.sharkk.net/Sky/LuaJIT-to-Go v0.4.1 h1:CAYt+C6Vgo4JxK876j0ApQ2GDFFvy9FKO0OoZBVD18k=
|
git.sharkk.net/Sky/LuaJIT-to-Go v0.4.1 h1:CAYt+C6Vgo4JxK876j0ApQ2GDFFvy9FKO0OoZBVD18k=
|
||||||
git.sharkk.net/Sky/LuaJIT-to-Go v0.4.1/go.mod h1:HQz+D7AFxOfNbTIogjxP+shEBtz1KKrLlLucU+w07c8=
|
git.sharkk.net/Sky/LuaJIT-to-Go v0.4.1/go.mod h1:HQz+D7AFxOfNbTIogjxP+shEBtz1KKrLlLucU+w07c8=
|
||||||
github.com/VictoriaMetrics/fastcache v1.12.4 h1:2xvmwZBW+9QtHsXggfzAZRs1FZWCsBs8QDg22bMidf0=
|
github.com/VictoriaMetrics/fastcache v1.12.4 h1:2xvmwZBW+9QtHsXggfzAZRs1FZWCsBs8QDg22bMidf0=
|
||||||
@ -52,8 +54,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
|
|||||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
|
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b h1:QoALfVG9rhQ/M7vYDScfPdWjGL9dlsVVM5VGh7aKoAA=
|
||||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
|
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||||
|
@ -3,6 +3,7 @@ package http
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -32,14 +33,12 @@ type Server struct {
|
|||||||
staticFS *fasthttp.FS
|
staticFS *fasthttp.FS
|
||||||
luaRunner *runner.Runner
|
luaRunner *runner.Runner
|
||||||
fasthttpServer *fasthttp.Server
|
fasthttpServer *fasthttp.Server
|
||||||
loggingEnabled bool
|
|
||||||
debugMode bool
|
debugMode bool
|
||||||
config *config.Config
|
cfg *config.Config
|
||||||
sessionManager *sessions.SessionManager
|
sessionManager *sessions.SessionManager
|
||||||
errorConfig utils.ErrorPageConfig
|
errorConfig utils.ErrorPageConfig
|
||||||
ctxPool sync.Pool
|
ctxPool sync.Pool
|
||||||
paramsPool sync.Pool
|
paramsPool sync.Pool
|
||||||
staticDir string
|
|
||||||
staticPrefix string
|
staticPrefix string
|
||||||
staticPrefixBytes []byte
|
staticPrefixBytes []byte
|
||||||
|
|
||||||
@ -49,34 +48,25 @@ type Server struct {
|
|||||||
errorCacheMu sync.RWMutex
|
errorCacheMu sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(luaRouter *router.LuaRouter, staticDir string,
|
func New(luaRouter *router.LuaRouter, runner *runner.Runner, cfg *config.Config, debugMode bool) *Server {
|
||||||
runner *runner.Runner, loggingEnabled bool, debugMode bool,
|
staticPrefix := cfg.Server.StaticPrefix
|
||||||
overrideDir string, config *config.Config) *Server {
|
if !strings.HasPrefix(staticPrefix, "/") {
|
||||||
|
|
||||||
staticPrefix := config.Server.StaticPrefix
|
|
||||||
if staticPrefix == "" {
|
|
||||||
staticPrefix = "/static/"
|
|
||||||
}
|
|
||||||
|
|
||||||
if staticPrefix[0] != '/' {
|
|
||||||
staticPrefix = "/" + staticPrefix
|
staticPrefix = "/" + staticPrefix
|
||||||
}
|
}
|
||||||
if staticPrefix[len(staticPrefix)-1] != '/' {
|
if !strings.HasSuffix(staticPrefix, "/") {
|
||||||
staticPrefix = staticPrefix + "/"
|
staticPrefix = staticPrefix + "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
s := &Server{
|
s := &Server{
|
||||||
luaRouter: luaRouter,
|
luaRouter: luaRouter,
|
||||||
luaRunner: runner,
|
luaRunner: runner,
|
||||||
loggingEnabled: loggingEnabled,
|
|
||||||
debugMode: debugMode,
|
debugMode: debugMode,
|
||||||
config: config,
|
cfg: cfg,
|
||||||
sessionManager: sessions.GlobalSessionManager,
|
sessionManager: sessions.GlobalSessionManager,
|
||||||
staticDir: staticDir,
|
|
||||||
staticPrefix: staticPrefix,
|
staticPrefix: staticPrefix,
|
||||||
staticPrefixBytes: []byte(staticPrefix),
|
staticPrefixBytes: []byte(staticPrefix),
|
||||||
errorConfig: utils.ErrorPageConfig{
|
errorConfig: utils.ErrorPageConfig{
|
||||||
OverrideDir: overrideDir,
|
OverrideDir: cfg.Dirs.Override,
|
||||||
DebugMode: debugMode,
|
DebugMode: debugMode,
|
||||||
},
|
},
|
||||||
ctxPool: sync.Pool{
|
ctxPool: sync.Pool{
|
||||||
@ -96,9 +86,9 @@ func New(luaRouter *router.LuaRouter, staticDir string,
|
|||||||
s.cached500 = []byte(utils.InternalErrorPage(s.errorConfig, "", "Internal Server Error"))
|
s.cached500 = []byte(utils.InternalErrorPage(s.errorConfig, "", "Internal Server Error"))
|
||||||
|
|
||||||
// Setup static file serving
|
// Setup static file serving
|
||||||
if staticDir != "" {
|
if cfg.Dirs.Static != "" {
|
||||||
s.staticFS = &fasthttp.FS{
|
s.staticFS = &fasthttp.FS{
|
||||||
Root: staticDir,
|
Root: cfg.Dirs.Static,
|
||||||
IndexNames: []string{"index.html"},
|
IndexNames: []string{"index.html"},
|
||||||
GenerateIndexPages: false,
|
GenerateIndexPages: false,
|
||||||
AcceptByteRange: true,
|
AcceptByteRange: true,
|
||||||
@ -144,25 +134,22 @@ func (s *Server) handleRequest(ctx *fasthttp.RequestCtx) {
|
|||||||
methodBytes := ctx.Method()
|
methodBytes := ctx.Method()
|
||||||
pathBytes := ctx.Path()
|
pathBytes := ctx.Path()
|
||||||
|
|
||||||
// Fast path for debug stats
|
|
||||||
if s.debugMode && bytes.Equal(pathBytes, debugPath) {
|
if s.debugMode && bytes.Equal(pathBytes, debugPath) {
|
||||||
s.handleDebugStats(ctx)
|
s.handleDebugStats(ctx)
|
||||||
if s.loggingEnabled {
|
if s.cfg.Server.HTTPLogging {
|
||||||
logger.LogRequest(ctx.Response.StatusCode(), string(methodBytes), string(pathBytes), time.Since(start))
|
logger.LogRequest(ctx.Response.StatusCode(), string(methodBytes), string(pathBytes), time.Since(start))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fast path for static files
|
|
||||||
if s.staticHandler != nil && bytes.HasPrefix(pathBytes, s.staticPrefixBytes) {
|
if s.staticHandler != nil && bytes.HasPrefix(pathBytes, s.staticPrefixBytes) {
|
||||||
s.staticHandler(ctx)
|
s.staticHandler(ctx)
|
||||||
if s.loggingEnabled {
|
if s.cfg.Server.HTTPLogging {
|
||||||
logger.LogRequest(ctx.Response.StatusCode(), string(methodBytes), string(pathBytes), time.Since(start))
|
logger.LogRequest(ctx.Response.StatusCode(), string(methodBytes), string(pathBytes), time.Since(start))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lua route lookup - only allocate params if found
|
|
||||||
bytecode, scriptPath, routeErr, params, found := s.luaRouter.GetRouteInfo(methodBytes, pathBytes)
|
bytecode, scriptPath, routeErr, params, found := s.luaRouter.GetRouteInfo(methodBytes, pathBytes)
|
||||||
|
|
||||||
if found {
|
if found {
|
||||||
@ -175,7 +162,7 @@ func (s *Server) handleRequest(ctx *fasthttp.RequestCtx) {
|
|||||||
s.send404(ctx, pathBytes)
|
s.send404(ctx, pathBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.loggingEnabled {
|
if s.cfg.Server.HTTPLogging {
|
||||||
logger.LogRequest(ctx.Response.StatusCode(), string(methodBytes), string(pathBytes), time.Since(start))
|
logger.LogRequest(ctx.Response.StatusCode(), string(methodBytes), string(pathBytes), time.Since(start))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -310,7 +297,7 @@ func (s *Server) sendError(ctx *fasthttp.RequestCtx, status int, pathBytes []byt
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleDebugStats(ctx *fasthttp.RequestCtx) {
|
func (s *Server) handleDebugStats(ctx *fasthttp.RequestCtx) {
|
||||||
stats := utils.CollectSystemStats(s.config)
|
stats := utils.CollectSystemStats(s.cfg)
|
||||||
routeCount, bytecodeBytes := s.luaRouter.GetRouteStats()
|
routeCount, bytecodeBytes := s.luaRouter.GetRouteStats()
|
||||||
stats.Components = utils.ComponentStats{
|
stats.Components = utils.ComponentStats{
|
||||||
RouteCount: routeCount,
|
RouteCount: routeCount,
|
||||||
|
39
main.go
39
main.go
@ -21,6 +21,8 @@ import (
|
|||||||
"Moonshark/utils/logger"
|
"Moonshark/utils/logger"
|
||||||
"Moonshark/utils/metadata"
|
"Moonshark/utils/metadata"
|
||||||
"Moonshark/watchers"
|
"Moonshark/watchers"
|
||||||
|
|
||||||
|
fin "git.sharkk.net/Sharkk/Fin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Moonshark represents the server and all its dependencies
|
// Moonshark represents the server and all its dependencies
|
||||||
@ -34,7 +36,7 @@ type Moonshark struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
configPath := flag.String("config", "config.lua", "Path to configuration file")
|
configPath := flag.String("config", "config", "Path to configuration file")
|
||||||
debugFlag := flag.Bool("debug", false, "Enable debug mode")
|
debugFlag := flag.Bool("debug", false, "Enable debug mode")
|
||||||
scriptPath := flag.String("script", "", "Path to Lua script to execute once")
|
scriptPath := flag.String("script", "", "Path to Lua script to execute once")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
@ -42,26 +44,20 @@ func main() {
|
|||||||
|
|
||||||
color.SetColors(color.DetectShellColors())
|
color.SetColors(color.DetectShellColors())
|
||||||
banner(scriptMode)
|
banner(scriptMode)
|
||||||
|
cfg := config.New(readConfig(*configPath))
|
||||||
|
|
||||||
// Load config
|
|
||||||
cfg, err := config.Load(*configPath)
|
|
||||||
if err != nil {
|
|
||||||
logger.Warning("Config load failed: %v, using defaults", color.Red(err.Error()))
|
|
||||||
cfg = config.New()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup logging with debug mode
|
|
||||||
if *debugFlag || cfg.Server.Debug {
|
if *debugFlag || cfg.Server.Debug {
|
||||||
logger.EnableDebug()
|
logger.EnableDebug()
|
||||||
logger.Debug("Debug logging enabled")
|
logger.Debug("Debug logging enabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
var moonshark *Moonshark
|
var moonshark *Moonshark
|
||||||
|
var err error
|
||||||
|
|
||||||
if scriptMode {
|
if scriptMode {
|
||||||
moonshark, err = initScriptMode(cfg)
|
moonshark, err = initScriptMode(cfg)
|
||||||
} else {
|
} else {
|
||||||
moonshark, err = initServerMode(cfg, *debugFlag)
|
moonshark, err = initServerMode(cfg, *debugFlag || cfg.Server.Debug)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -156,12 +152,9 @@ func initServerMode(cfg *config.Config, debug bool) (*Moonshark, error) {
|
|||||||
|
|
||||||
moonshark.HTTPServer = http.New(
|
moonshark.HTTPServer = http.New(
|
||||||
moonshark.LuaRouter,
|
moonshark.LuaRouter,
|
||||||
staticDir,
|
|
||||||
moonshark.LuaRunner,
|
moonshark.LuaRunner,
|
||||||
cfg.Server.HTTPLogging,
|
|
||||||
cfg.Server.Debug,
|
|
||||||
cfg.Dirs.Override,
|
|
||||||
cfg,
|
cfg,
|
||||||
|
debug,
|
||||||
)
|
)
|
||||||
|
|
||||||
// For development, disable caching. For production, enable it
|
// For development, disable caching. For production, enable it
|
||||||
@ -387,3 +380,21 @@ func banner(scriptMode bool) {
|
|||||||
`
|
`
|
||||||
fmt.Println(color.Blue(fmt.Sprintf(banner, metadata.Version)))
|
fmt.Println(color.Blue(fmt.Sprintf(banner, metadata.Version)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readConfig attempts to read config data from a Fin file
|
||||||
|
func readConfig(path string) *fin.Data {
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("Failed to open config file %s", color.Yellow(path))
|
||||||
|
cfg, _ := fin.Load(nil)
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
cfg, err := fin.Load(file)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warning("Config load failed: %v, using defaults", color.Red(err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
@ -24,8 +24,7 @@ const (
|
|||||||
|
|
||||||
var useColors = true
|
var useColors = true
|
||||||
|
|
||||||
// Color function. Makes a call to makeColorFunc with the associated
|
// Color function; makes a call to makeColorFunc with the associated color code
|
||||||
// color code.
|
|
||||||
var (
|
var (
|
||||||
Reset = makeColorFunc(resetCode)
|
Reset = makeColorFunc(resetCode)
|
||||||
Red = makeColorFunc(redCode)
|
Red = makeColorFunc(redCode)
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
fin "git.sharkk.net/Sharkk/Fin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config represents a configuration loaded from a Lua file
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
// Server settings
|
// Server settings
|
||||||
Server struct {
|
Server struct {
|
||||||
@ -35,256 +30,29 @@ type Config struct {
|
|||||||
Libs []string
|
Libs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Raw values map for custom values
|
// Raw fin data struct for custom data
|
||||||
values map[string]any
|
data *fin.Data
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConfig creates a new configuration with default values
|
// NewConfig creates a new configuration with default values
|
||||||
func New() *Config {
|
func New(data *fin.Data) *Config {
|
||||||
config := &Config{
|
config := &Config{
|
||||||
// Initialize values map
|
data: data,
|
||||||
values: make(map[string]any),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server defaults
|
config.Server.Port = data.GetOr("server.port", 3117).(int)
|
||||||
config.Server.Port = 3117
|
config.Server.Debug = data.GetOr("server.debug", false).(bool)
|
||||||
config.Server.Debug = false
|
config.Server.HTTPLogging = data.GetOr("server.http_logging", true).(bool)
|
||||||
config.Server.HTTPLogging = false
|
config.Server.StaticPrefix = data.GetOr("server.static_prefix", "public").(string)
|
||||||
config.Server.StaticPrefix = "static/"
|
|
||||||
|
|
||||||
// Runner defaults
|
config.Runner.PoolSize = data.GetOr("runner.pool_size", runtime.GOMAXPROCS(0)).(int)
|
||||||
config.Runner.PoolSize = runtime.GOMAXPROCS(0)
|
|
||||||
|
|
||||||
// Dirs defaults
|
config.Dirs.Routes = data.GetOr("dirs.routes", "routes").(string)
|
||||||
config.Dirs.Routes = "routes"
|
config.Dirs.Static = data.GetOr("dirs.static", "public").(string)
|
||||||
config.Dirs.Static = "public"
|
config.Dirs.FS = data.GetOr("dirs.fs", "fs").(string)
|
||||||
config.Dirs.FS = "fs"
|
config.Dirs.Data = data.GetOr("dirs.data", "data").(string)
|
||||||
config.Dirs.Data = "data"
|
config.Dirs.Override = data.GetOr("dirs.override", "override").(string)
|
||||||
config.Dirs.Override = "override"
|
config.Dirs.Libs = data.GetOr("dirs.libs", []string{"libs"}).([]string)
|
||||||
config.Dirs.Libs = []string{"libs"}
|
|
||||||
|
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load loads configuration from a Lua file
|
|
||||||
func Load(filePath string) (*Config, error) {
|
|
||||||
// Create Lua state
|
|
||||||
state := luajit.New(true)
|
|
||||||
if state == nil {
|
|
||||||
return nil, errors.New("failed to create Lua state")
|
|
||||||
}
|
|
||||||
defer state.Close()
|
|
||||||
defer state.Cleanup()
|
|
||||||
|
|
||||||
// Create config with default values
|
|
||||||
config := New()
|
|
||||||
|
|
||||||
// Execute the config file
|
|
||||||
if err := state.DoFile(filePath); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to load config file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store values directly to the config
|
|
||||||
config.values = make(map[string]any)
|
|
||||||
|
|
||||||
// Extract top-level tables
|
|
||||||
tables := []string{"server", "runner", "dirs"}
|
|
||||||
for _, table := range tables {
|
|
||||||
state.GetGlobal(table)
|
|
||||||
if state.IsTable(-1) {
|
|
||||||
tableMap, err := state.ToTable(-1)
|
|
||||||
if err == nil {
|
|
||||||
config.values[table] = tableMap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state.Pop(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply configuration values
|
|
||||||
applyConfig(config, config.values)
|
|
||||||
|
|
||||||
return config, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// applyConfig applies configuration values from the globals map
|
|
||||||
func applyConfig(config *Config, values map[string]any) {
|
|
||||||
// Apply server settings
|
|
||||||
if serverTable, ok := values["server"].(map[string]any); ok {
|
|
||||||
if v, ok := serverTable["port"].(float64); ok {
|
|
||||||
config.Server.Port = int(v)
|
|
||||||
}
|
|
||||||
if v, ok := serverTable["debug"].(bool); ok {
|
|
||||||
config.Server.Debug = v
|
|
||||||
}
|
|
||||||
if v, ok := serverTable["http_logging"].(bool); ok {
|
|
||||||
config.Server.HTTPLogging = v
|
|
||||||
}
|
|
||||||
if v, ok := serverTable["static_prefix"].(string); ok {
|
|
||||||
config.Server.StaticPrefix = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply runner settings
|
|
||||||
if runnerTable, ok := values["runner"].(map[string]any); ok {
|
|
||||||
if v, ok := runnerTable["pool_size"].(float64); ok && v != 0 {
|
|
||||||
config.Runner.PoolSize = int(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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// extractStringArray extracts a string array from a Lua table
|
|
||||||
func extractStringArray(value any) []string {
|
|
||||||
// Direct array case
|
|
||||||
if arr, ok := value.([]any); ok {
|
|
||||||
result := make([]string, 0, len(arr))
|
|
||||||
for _, v := range arr {
|
|
||||||
if str, ok := v.(string); ok {
|
|
||||||
result = append(result, str)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map with numeric keys case
|
|
||||||
if tableMap, ok := value.(map[string]any); ok {
|
|
||||||
result := make([]string, 0, len(tableMap))
|
|
||||||
for _, v := range tableMap {
|
|
||||||
if str, ok := v.(string); ok {
|
|
||||||
result = append(result, str)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCustomValue returns any custom configuration value by key
|
|
||||||
// Key can be a dotted path like "server.port"
|
|
||||||
func (c *Config) GetCustomValue(key string) any {
|
|
||||||
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
|
|
||||||
func (c *Config) GetCustomString(key string, defaultValue string) string {
|
|
||||||
value := c.GetCustomValue(key)
|
|
||||||
if value == nil {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to string
|
|
||||||
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.GetCustomValue(key)
|
|
||||||
if value == nil {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to int
|
|
||||||
switch v := value.(type) {
|
|
||||||
case int:
|
|
||||||
return v
|
|
||||||
case float64:
|
|
||||||
return int(v)
|
|
||||||
case string:
|
|
||||||
if i, err := strconv.Atoi(v); err == nil {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
case bool:
|
|
||||||
if v {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
default:
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCustomBool returns a custom boolean configuration value
|
|
||||||
func (c *Config) GetCustomBool(key string, defaultValue bool) bool {
|
|
||||||
value := c.GetCustomValue(key)
|
|
||||||
if value == nil {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to bool
|
|
||||||
switch v := value.(type) {
|
|
||||||
case bool:
|
|
||||||
return v
|
|
||||||
case string:
|
|
||||||
switch v {
|
|
||||||
case "true", "yes", "1":
|
|
||||||
return true
|
|
||||||
case "false", "no", "0", "":
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
case int:
|
|
||||||
return v != 0
|
|
||||||
case float64:
|
|
||||||
return v != 0
|
|
||||||
default:
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user