config rewrite

This commit is contained in:
Sky Johnson 2025-05-31 13:02:16 -05:00
parent 3f86c31c9f
commit ac646bbad8
6 changed files with 62 additions and 294 deletions

3
go.mod
View File

@ -4,6 +4,7 @@ go 1.24.1
require (
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
github.com/VictoriaMetrics/fastcache v1.12.4
github.com/alexedwards/argon2id v1.0.0
@ -26,7 +27,7 @@ require (
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // 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
modernc.org/libc v1.65.8 // indirect
modernc.org/mathutil v1.7.1 // indirect

6
go.sum
View File

@ -1,5 +1,7 @@
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/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/go.mod h1:HQz+D7AFxOfNbTIogjxP+shEBtz1KKrLlLucU+w07c8=
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.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
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-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b h1:QoALfVG9rhQ/M7vYDScfPdWjGL9dlsVVM5VGh7aKoAA=
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.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=

View File

@ -3,6 +3,7 @@ package http
import (
"bytes"
"context"
"strings"
"sync"
"time"
@ -32,14 +33,12 @@ type Server struct {
staticFS *fasthttp.FS
luaRunner *runner.Runner
fasthttpServer *fasthttp.Server
loggingEnabled bool
debugMode bool
config *config.Config
cfg *config.Config
sessionManager *sessions.SessionManager
errorConfig utils.ErrorPageConfig
ctxPool sync.Pool
paramsPool sync.Pool
staticDir string
staticPrefix string
staticPrefixBytes []byte
@ -49,34 +48,25 @@ type Server struct {
errorCacheMu sync.RWMutex
}
func New(luaRouter *router.LuaRouter, staticDir string,
runner *runner.Runner, loggingEnabled bool, debugMode bool,
overrideDir string, config *config.Config) *Server {
staticPrefix := config.Server.StaticPrefix
if staticPrefix == "" {
staticPrefix = "/static/"
}
if staticPrefix[0] != '/' {
func New(luaRouter *router.LuaRouter, runner *runner.Runner, cfg *config.Config, debugMode bool) *Server {
staticPrefix := cfg.Server.StaticPrefix
if !strings.HasPrefix(staticPrefix, "/") {
staticPrefix = "/" + staticPrefix
}
if staticPrefix[len(staticPrefix)-1] != '/' {
if !strings.HasSuffix(staticPrefix, "/") {
staticPrefix = staticPrefix + "/"
}
s := &Server{
luaRouter: luaRouter,
luaRunner: runner,
loggingEnabled: loggingEnabled,
debugMode: debugMode,
config: config,
cfg: cfg,
sessionManager: sessions.GlobalSessionManager,
staticDir: staticDir,
staticPrefix: staticPrefix,
staticPrefixBytes: []byte(staticPrefix),
errorConfig: utils.ErrorPageConfig{
OverrideDir: overrideDir,
OverrideDir: cfg.Dirs.Override,
DebugMode: debugMode,
},
ctxPool: sync.Pool{
@ -96,9 +86,9 @@ func New(luaRouter *router.LuaRouter, staticDir string,
s.cached500 = []byte(utils.InternalErrorPage(s.errorConfig, "", "Internal Server Error"))
// Setup static file serving
if staticDir != "" {
if cfg.Dirs.Static != "" {
s.staticFS = &fasthttp.FS{
Root: staticDir,
Root: cfg.Dirs.Static,
IndexNames: []string{"index.html"},
GenerateIndexPages: false,
AcceptByteRange: true,
@ -144,25 +134,22 @@ func (s *Server) handleRequest(ctx *fasthttp.RequestCtx) {
methodBytes := ctx.Method()
pathBytes := ctx.Path()
// Fast path for debug stats
if s.debugMode && bytes.Equal(pathBytes, debugPath) {
s.handleDebugStats(ctx)
if s.loggingEnabled {
if s.cfg.Server.HTTPLogging {
logger.LogRequest(ctx.Response.StatusCode(), string(methodBytes), string(pathBytes), time.Since(start))
}
return
}
// Fast path for static files
if s.staticHandler != nil && bytes.HasPrefix(pathBytes, s.staticPrefixBytes) {
s.staticHandler(ctx)
if s.loggingEnabled {
if s.cfg.Server.HTTPLogging {
logger.LogRequest(ctx.Response.StatusCode(), string(methodBytes), string(pathBytes), time.Since(start))
}
return
}
// Lua route lookup - only allocate params if found
bytecode, scriptPath, routeErr, params, found := s.luaRouter.GetRouteInfo(methodBytes, pathBytes)
if found {
@ -175,7 +162,7 @@ func (s *Server) handleRequest(ctx *fasthttp.RequestCtx) {
s.send404(ctx, pathBytes)
}
if s.loggingEnabled {
if s.cfg.Server.HTTPLogging {
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) {
stats := utils.CollectSystemStats(s.config)
stats := utils.CollectSystemStats(s.cfg)
routeCount, bytecodeBytes := s.luaRouter.GetRouteStats()
stats.Components = utils.ComponentStats{
RouteCount: routeCount,

39
main.go
View File

@ -21,6 +21,8 @@ import (
"Moonshark/utils/logger"
"Moonshark/utils/metadata"
"Moonshark/watchers"
fin "git.sharkk.net/Sharkk/Fin"
)
// Moonshark represents the server and all its dependencies
@ -34,7 +36,7 @@ type Moonshark struct {
}
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")
scriptPath := flag.String("script", "", "Path to Lua script to execute once")
flag.Parse()
@ -42,26 +44,20 @@ func main() {
color.SetColors(color.DetectShellColors())
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 {
logger.EnableDebug()
logger.Debug("Debug logging enabled")
}
var moonshark *Moonshark
var err error
if scriptMode {
moonshark, err = initScriptMode(cfg)
} else {
moonshark, err = initServerMode(cfg, *debugFlag)
moonshark, err = initServerMode(cfg, *debugFlag || cfg.Server.Debug)
}
if err != nil {
@ -156,12 +152,9 @@ func initServerMode(cfg *config.Config, debug bool) (*Moonshark, error) {
moonshark.HTTPServer = http.New(
moonshark.LuaRouter,
staticDir,
moonshark.LuaRunner,
cfg.Server.HTTPLogging,
cfg.Server.Debug,
cfg.Dirs.Override,
cfg,
debug,
)
// 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)))
}
// 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
}

View File

@ -24,8 +24,7 @@ const (
var useColors = true
// Color function. Makes a call to makeColorFunc with the associated
// color code.
// Color function; makes a call to makeColorFunc with the associated color code
var (
Reset = makeColorFunc(resetCode)
Red = makeColorFunc(redCode)

View File

@ -1,16 +1,11 @@
package config
import (
"errors"
"fmt"
"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 {
// Server settings
Server struct {
@ -35,256 +30,29 @@ type Config struct {
Libs []string
}
// Raw values map for custom values
values map[string]any
// Raw fin data struct for custom data
data *fin.Data
}
// NewConfig creates a new configuration with default values
func New() *Config {
func New(data *fin.Data) *Config {
config := &Config{
// Initialize values map
values: make(map[string]any),
data: data,
}
// Server defaults
config.Server.Port = 3117
config.Server.Debug = false
config.Server.HTTPLogging = false
config.Server.StaticPrefix = "static/"
config.Server.Port = data.GetOr("server.port", 3117).(int)
config.Server.Debug = data.GetOr("server.debug", false).(bool)
config.Server.HTTPLogging = data.GetOr("server.http_logging", true).(bool)
config.Server.StaticPrefix = data.GetOr("server.static_prefix", "public").(string)
// Runner defaults
config.Runner.PoolSize = runtime.GOMAXPROCS(0)
config.Runner.PoolSize = data.GetOr("runner.pool_size", runtime.GOMAXPROCS(0)).(int)
// Dirs defaults
config.Dirs.Routes = "routes"
config.Dirs.Static = "public"
config.Dirs.FS = "fs"
config.Dirs.Data = "data"
config.Dirs.Override = "override"
config.Dirs.Libs = []string{"libs"}
config.Dirs.Routes = data.GetOr("dirs.routes", "routes").(string)
config.Dirs.Static = data.GetOr("dirs.static", "public").(string)
config.Dirs.FS = data.GetOr("dirs.fs", "fs").(string)
config.Dirs.Data = data.GetOr("dirs.data", "data").(string)
config.Dirs.Override = data.GetOr("dirs.override", "override").(string)
config.Dirs.Libs = data.GetOr("dirs.libs", []string{"libs"}).([]string)
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
}