rewrite logger and main
This commit is contained in:
parent
ac646bbad8
commit
c0c6100c17
@ -121,7 +121,7 @@ func New(luaRouter *router.LuaRouter, runner *runner.Runner, cfg *config.Config,
|
||||
}
|
||||
|
||||
func (s *Server) ListenAndServe(addr string) error {
|
||||
logger.Info("Catch the swell at %s", color.Cyan("http://localhost"+addr))
|
||||
logger.Infof("Catch the swell at %s", color.Cyan("http://localhost"+addr))
|
||||
return s.fasthttpServer.ListenAndServe(addr)
|
||||
}
|
||||
|
||||
@ -137,7 +137,7 @@ func (s *Server) handleRequest(ctx *fasthttp.RequestCtx) {
|
||||
if s.debugMode && bytes.Equal(pathBytes, debugPath) {
|
||||
s.handleDebugStats(ctx)
|
||||
if s.cfg.Server.HTTPLogging {
|
||||
logger.LogRequest(ctx.Response.StatusCode(), string(methodBytes), string(pathBytes), time.Since(start))
|
||||
logger.Request(ctx.Response.StatusCode(), string(methodBytes), string(pathBytes), time.Since(start))
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -145,7 +145,7 @@ func (s *Server) handleRequest(ctx *fasthttp.RequestCtx) {
|
||||
if s.staticHandler != nil && bytes.HasPrefix(pathBytes, s.staticPrefixBytes) {
|
||||
s.staticHandler(ctx)
|
||||
if s.cfg.Server.HTTPLogging {
|
||||
logger.LogRequest(ctx.Response.StatusCode(), string(methodBytes), string(pathBytes), time.Since(start))
|
||||
logger.Request(ctx.Response.StatusCode(), string(methodBytes), string(pathBytes), time.Since(start))
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -163,7 +163,7 @@ func (s *Server) handleRequest(ctx *fasthttp.RequestCtx) {
|
||||
}
|
||||
|
||||
if s.cfg.Server.HTTPLogging {
|
||||
logger.LogRequest(ctx.Response.StatusCode(), string(methodBytes), string(pathBytes), time.Since(start))
|
||||
logger.Request(ctx.Response.StatusCode(), string(methodBytes), string(pathBytes), time.Since(start))
|
||||
}
|
||||
}
|
||||
|
||||
@ -233,7 +233,7 @@ func (s *Server) handleLuaRoute(ctx *fasthttp.RequestCtx, bytecode []byte, scrip
|
||||
luaCtx.Set("form", formData)
|
||||
} else {
|
||||
if s.debugMode {
|
||||
logger.Warning("Error parsing form: %v", err)
|
||||
logger.Warnf("Error parsing form: %v", err)
|
||||
}
|
||||
luaCtx.Set("form", emptyMap)
|
||||
}
|
||||
@ -243,7 +243,7 @@ func (s *Server) handleLuaRoute(ctx *fasthttp.RequestCtx, bytecode []byte, scrip
|
||||
|
||||
response, err := s.luaRunner.Run(bytecode, luaCtx, scriptPath)
|
||||
if err != nil {
|
||||
logger.Error("Lua execution error: %v", err)
|
||||
logger.Errorf("Lua execution error: %v", err)
|
||||
s.sendError(ctx, fasthttp.StatusInternalServerError, pathBytes, err)
|
||||
return
|
||||
}
|
||||
|
400
main.go
400
main.go
@ -1,400 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"Moonshark/http"
|
||||
"Moonshark/router"
|
||||
"Moonshark/runner"
|
||||
"Moonshark/sessions"
|
||||
"Moonshark/utils/color"
|
||||
"Moonshark/utils/config"
|
||||
"Moonshark/utils/logger"
|
||||
"Moonshark/utils/metadata"
|
||||
"Moonshark/watchers"
|
||||
|
||||
fin "git.sharkk.net/Sharkk/Fin"
|
||||
)
|
||||
|
||||
// Moonshark represents the server and all its dependencies
|
||||
type Moonshark struct {
|
||||
Config *config.Config
|
||||
LuaRouter *router.LuaRouter
|
||||
LuaRunner *runner.Runner
|
||||
HTTPServer *http.Server
|
||||
cleanupFuncs []func() error
|
||||
scriptMode bool
|
||||
}
|
||||
|
||||
func main() {
|
||||
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()
|
||||
scriptMode := *scriptPath != ""
|
||||
|
||||
color.SetColors(color.DetectShellColors())
|
||||
banner(scriptMode)
|
||||
cfg := config.New(readConfig(*configPath))
|
||||
|
||||
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 || cfg.Server.Debug)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.Fatal("Initialization failed: %v", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := moonshark.Shutdown(); err != nil {
|
||||
logger.Error("Error during shutdown: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
|
||||
if scriptMode {
|
||||
// Run the script and exit
|
||||
if err := moonshark.RunScript(*scriptPath); err != nil {
|
||||
logger.Fatal("Script execution failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Start the server
|
||||
if err := moonshark.Start(); err != nil {
|
||||
logger.Fatal("Failed to start server: %v", err)
|
||||
}
|
||||
|
||||
// Wait for shutdown signal
|
||||
stop := make(chan os.Signal, 1)
|
||||
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
|
||||
<-stop
|
||||
|
||||
fmt.Print("\n")
|
||||
logger.Info("Shutdown signal received")
|
||||
}
|
||||
|
||||
// initScriptMode initializes minimal components needed for script execution
|
||||
func initScriptMode(cfg *config.Config) (*Moonshark, error) {
|
||||
moonshark := &Moonshark{
|
||||
Config: cfg,
|
||||
scriptMode: true,
|
||||
}
|
||||
|
||||
// Only initialize the Lua runner with required paths
|
||||
runnerOpts := []runner.RunnerOption{
|
||||
runner.WithPoolSize(1), // Only need one state for script mode
|
||||
runner.WithLibDirs(cfg.Dirs.Libs...),
|
||||
runner.WithFsDir(cfg.Dirs.FS),
|
||||
runner.WithDataDir(cfg.Dirs.Data),
|
||||
}
|
||||
|
||||
var err error
|
||||
moonshark.LuaRunner, err = runner.NewRunner(runnerOpts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize Lua runner: %v", err)
|
||||
}
|
||||
|
||||
logger.Debug("Script mode initialized with minimized components")
|
||||
return moonshark, nil
|
||||
}
|
||||
|
||||
// initServerMode initializes all components needed for server operation
|
||||
func initServerMode(cfg *config.Config, debug bool) (*Moonshark, error) {
|
||||
moonshark := &Moonshark{
|
||||
Config: cfg,
|
||||
scriptMode: false,
|
||||
}
|
||||
|
||||
if debug {
|
||||
cfg.Server.Debug = true
|
||||
}
|
||||
|
||||
if err := initLuaRouter(moonshark); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := initRunner(moonshark); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := setupWatchers(moonshark); err != nil {
|
||||
logger.Warning("Watcher setup failed: %v", err)
|
||||
}
|
||||
|
||||
// Get static directory - empty string if it doesn't exist
|
||||
staticDir := ""
|
||||
if dirExists(cfg.Dirs.Static) {
|
||||
staticDir = cfg.Dirs.Static
|
||||
logger.Info("Static files enabled: %s", color.Yellow(staticDir))
|
||||
} else {
|
||||
logger.Warning("Static directory not found: %s", color.Yellow(cfg.Dirs.Static))
|
||||
}
|
||||
|
||||
moonshark.HTTPServer = http.New(
|
||||
moonshark.LuaRouter,
|
||||
moonshark.LuaRunner,
|
||||
cfg,
|
||||
debug,
|
||||
)
|
||||
|
||||
// For development, disable caching. For production, enable it
|
||||
if cfg.Server.Debug {
|
||||
moonshark.HTTPServer.SetStaticCaching(0) // No caching in debug mode
|
||||
} else {
|
||||
moonshark.HTTPServer.SetStaticCaching(1 * time.Hour) // Cache for 1 hour in production
|
||||
}
|
||||
|
||||
return moonshark, nil
|
||||
}
|
||||
|
||||
// RunScript executes a Lua script in the sandbox environment
|
||||
func (s *Moonshark) RunScript(scriptPath string) error {
|
||||
scriptPath, err := filepath.Abs(scriptPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to resolve script path: %v", err)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(scriptPath); os.IsNotExist(err) {
|
||||
return fmt.Errorf("script file not found: %s", scriptPath)
|
||||
}
|
||||
|
||||
logger.Info("Executing: %s", scriptPath)
|
||||
|
||||
resp, err := s.LuaRunner.RunScriptFile(scriptPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("execution failed: %v", err)
|
||||
}
|
||||
|
||||
if resp != nil && resp.Body != nil {
|
||||
logger.Info("Script result: %v", resp.Body)
|
||||
} else {
|
||||
logger.Info("Script executed successfully (no return value)")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start starts the HTTP server
|
||||
func (s *Moonshark) Start() error {
|
||||
if s.scriptMode {
|
||||
return errors.New("cannot start server in script mode")
|
||||
}
|
||||
|
||||
logger.Info("Surf's up on port %s!", color.Cyan(strconv.Itoa(s.Config.Server.Port)))
|
||||
|
||||
go func() {
|
||||
if err := s.HTTPServer.ListenAndServe(fmt.Sprintf(":%d", s.Config.Server.Port)); err != nil {
|
||||
if err.Error() != "http: Server closed" {
|
||||
logger.Error("Server error: %v", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown gracefully shuts down Moonshark
|
||||
func (s *Moonshark) Shutdown() error {
|
||||
logger.Info("Shutting down...")
|
||||
|
||||
if !s.scriptMode && s.HTTPServer != nil {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := s.HTTPServer.Shutdown(ctx); err != nil {
|
||||
logger.Error("HTTP server shutdown error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, cleanup := range s.cleanupFuncs {
|
||||
if err := cleanup(); err != nil {
|
||||
logger.Warning("Cleanup error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if s.LuaRunner != nil {
|
||||
s.LuaRunner.Close()
|
||||
}
|
||||
|
||||
if err := runner.CleanupEnv(); err != nil {
|
||||
logger.Warning("Environment cleanup failed: %v", err)
|
||||
}
|
||||
|
||||
logger.Info("Shutdown complete")
|
||||
return nil
|
||||
}
|
||||
|
||||
func dirExists(path string) bool {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return info.IsDir()
|
||||
}
|
||||
|
||||
func initLuaRouter(s *Moonshark) error {
|
||||
if !dirExists(s.Config.Dirs.Routes) {
|
||||
return fmt.Errorf("routes directory doesn't exist: %s", s.Config.Dirs.Routes)
|
||||
}
|
||||
|
||||
var err error
|
||||
s.LuaRouter, err = router.NewLuaRouter(s.Config.Dirs.Routes)
|
||||
if err != nil {
|
||||
if errors.Is(err, router.ErrRoutesCompilationErrors) {
|
||||
// Non-fatal, some routes failed
|
||||
logger.Warning("Some routes failed to compile")
|
||||
|
||||
if failedRoutes := s.LuaRouter.ReportFailedRoutes(); len(failedRoutes) > 0 {
|
||||
for _, re := range failedRoutes {
|
||||
logger.Error("Route %s %s: %v", re.Method, re.Path, re.Err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("lua router init failed: %v", err)
|
||||
}
|
||||
}
|
||||
logger.Info("LuaRouter is g2g! %s", color.Yellow(s.Config.Dirs.Routes))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func initRunner(s *Moonshark) error {
|
||||
if !dirExists(s.Config.Dirs.Override) {
|
||||
logger.Warning("Override directory not found... %s", color.Yellow(s.Config.Dirs.Override))
|
||||
s.Config.Dirs.Override = ""
|
||||
}
|
||||
|
||||
for _, dir := range s.Config.Dirs.Libs {
|
||||
if !dirExists(dir) {
|
||||
logger.Warning("Lib directory not found... %s", color.Yellow(dir))
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize environment manager
|
||||
if err := runner.InitEnv(s.Config.Dirs.Data); err != nil {
|
||||
logger.Warning("Environment initialization failed: %v", err)
|
||||
}
|
||||
|
||||
sessionManager := sessions.GlobalSessionManager
|
||||
sessionManager.SetCookieOptions(
|
||||
"MoonsharkSID",
|
||||
"/",
|
||||
"",
|
||||
false,
|
||||
true,
|
||||
86400,
|
||||
)
|
||||
|
||||
poolSize := s.Config.Runner.PoolSize
|
||||
if s.scriptMode {
|
||||
poolSize = 1 // Only need one state for script mode
|
||||
}
|
||||
|
||||
runnerOpts := []runner.RunnerOption{
|
||||
runner.WithPoolSize(poolSize),
|
||||
runner.WithLibDirs(s.Config.Dirs.Libs...),
|
||||
runner.WithFsDir(s.Config.Dirs.FS),
|
||||
runner.WithDataDir(s.Config.Dirs.Data),
|
||||
}
|
||||
|
||||
var err error
|
||||
s.LuaRunner, err = runner.NewRunner(runnerOpts...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("lua runner init failed: %v", err)
|
||||
}
|
||||
|
||||
logger.Info("LuaRunner is g2g with %s states!", color.Yellow(strconv.Itoa(poolSize)))
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupWatchers(s *Moonshark) error {
|
||||
manager := watchers.GetWatcherManager()
|
||||
|
||||
// Watch routes directory
|
||||
routeWatcher, err := watchers.WatchLuaRouter(s.LuaRouter, s.LuaRunner, s.Config.Dirs.Routes)
|
||||
if err != nil {
|
||||
logger.Warning("Routes directory watch failed: %v", err)
|
||||
} else {
|
||||
routesDir := routeWatcher.GetDir()
|
||||
s.cleanupFuncs = append(s.cleanupFuncs, func() error {
|
||||
return manager.UnwatchDirectory(routesDir)
|
||||
})
|
||||
}
|
||||
|
||||
// Watch module directories
|
||||
moduleWatchers, err := watchers.WatchLuaModules(s.LuaRunner, s.Config.Dirs.Libs)
|
||||
if err != nil {
|
||||
logger.Warning("Module directories watch failed: %v", err)
|
||||
} else {
|
||||
for _, watcher := range moduleWatchers {
|
||||
dirPath := watcher.GetDir()
|
||||
s.cleanupFuncs = append(s.cleanupFuncs, func() error {
|
||||
return manager.UnwatchDirectory(dirPath)
|
||||
})
|
||||
}
|
||||
plural := ""
|
||||
if len(moduleWatchers) == 1 {
|
||||
plural = "directory"
|
||||
} else {
|
||||
plural = "directories"
|
||||
}
|
||||
logger.Info("Watching %s module %s.", color.Yellow(strconv.Itoa(len(moduleWatchers))), plural)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func banner(scriptMode bool) {
|
||||
if scriptMode {
|
||||
fmt.Println(color.Blue(fmt.Sprintf("Moonshark %s << Script Mode >>", metadata.Version)))
|
||||
return
|
||||
}
|
||||
|
||||
banner := `
|
||||
_____ _________.__ __
|
||||
/ \ ____ ____ ____ / _____/| |__ _____ _______| | __
|
||||
/ \ / \ / _ \ / _ \ / \ \_____ \ | | \\__ \\_ __ \ |/ /
|
||||
/ Y ( <_> | <_> ) | \/ \| Y \/ __ \| | \/ <
|
||||
\____|__ /\____/ \____/|___| /_______ /|___| (____ /__| |__|_ \ %s
|
||||
\/ \/ \/ \/ \/ \/
|
||||
`
|
||||
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
|
||||
}
|
334
moonshark.go
Normal file
334
moonshark.go
Normal file
@ -0,0 +1,334 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"Moonshark/http"
|
||||
"Moonshark/router"
|
||||
"Moonshark/runner"
|
||||
"Moonshark/sessions"
|
||||
"Moonshark/utils/color"
|
||||
"Moonshark/utils/config"
|
||||
"Moonshark/utils/logger"
|
||||
"Moonshark/utils/metadata"
|
||||
"Moonshark/watchers"
|
||||
|
||||
fin "git.sharkk.net/Sharkk/Fin"
|
||||
)
|
||||
|
||||
type Moonshark struct {
|
||||
Config *config.Config
|
||||
LuaRouter *router.LuaRouter
|
||||
LuaRunner *runner.Runner
|
||||
HTTPServer *http.Server
|
||||
cleanupFuncs []func() error
|
||||
scriptMode bool
|
||||
}
|
||||
|
||||
func main() {
|
||||
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()
|
||||
|
||||
scriptMode := *scriptPath != ""
|
||||
color.SetColors(color.DetectShellColors())
|
||||
banner(scriptMode)
|
||||
|
||||
cfg := config.New(readConfig(*configPath))
|
||||
debug := *debugFlag || cfg.Server.Debug
|
||||
logger.Debug(debug)
|
||||
|
||||
if debug {
|
||||
logger.Debugf("Debug logging enabled")
|
||||
}
|
||||
|
||||
moonshark, err := newMoonshark(cfg, debug, scriptMode)
|
||||
if err != nil {
|
||||
logger.Fatalf("Initialization failed: %v", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := moonshark.Shutdown(); err != nil {
|
||||
logger.Errorf("Error during shutdown: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
|
||||
if scriptMode {
|
||||
if err := moonshark.RunScript(*scriptPath); err != nil {
|
||||
logger.Fatalf("Script execution failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err := moonshark.Start(); err != nil {
|
||||
logger.Fatalf("Failed to start server: %v", err)
|
||||
}
|
||||
|
||||
stop := make(chan os.Signal, 1)
|
||||
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
|
||||
<-stop
|
||||
|
||||
fmt.Print("\n")
|
||||
logger.Infof("Shutdown signal received")
|
||||
}
|
||||
|
||||
func newMoonshark(cfg *config.Config, debug, scriptMode bool) (*Moonshark, error) {
|
||||
s := &Moonshark{Config: cfg, scriptMode: scriptMode}
|
||||
|
||||
if debug {
|
||||
cfg.Server.Debug = true
|
||||
}
|
||||
|
||||
poolSize := cfg.Runner.PoolSize
|
||||
if scriptMode {
|
||||
poolSize = 1
|
||||
}
|
||||
|
||||
// Initialize runner first (needed for both modes)
|
||||
if err := s.initRunner(poolSize); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if scriptMode {
|
||||
logger.Debugf("Script mode initialized")
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Server mode: initialize router, watchers, and HTTP server
|
||||
if err := s.initRouter(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.setupWatchers()
|
||||
|
||||
s.HTTPServer = http.New(s.LuaRouter, s.LuaRunner, cfg, debug)
|
||||
|
||||
// Set caching based on debug mode
|
||||
if cfg.Server.Debug {
|
||||
s.HTTPServer.SetStaticCaching(0)
|
||||
} else {
|
||||
s.HTTPServer.SetStaticCaching(time.Hour)
|
||||
}
|
||||
|
||||
// Log static directory status
|
||||
if dirExists(cfg.Dirs.Static) {
|
||||
logger.Infof("Static files enabled: %s", color.Yellow(cfg.Dirs.Static))
|
||||
} else {
|
||||
logger.Warnf("Static directory not found: %s", color.Yellow(cfg.Dirs.Static))
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *Moonshark) initRunner(poolSize int) error {
|
||||
// Warn about missing directories but continue
|
||||
if !dirExists(s.Config.Dirs.Override) {
|
||||
logger.Warnf("Override directory not found... %s", color.Yellow(s.Config.Dirs.Override))
|
||||
s.Config.Dirs.Override = ""
|
||||
}
|
||||
|
||||
for _, dir := range s.Config.Dirs.Libs {
|
||||
if !dirExists(dir) {
|
||||
logger.Warnf("Lib directory not found... %s", color.Yellow(dir))
|
||||
}
|
||||
}
|
||||
|
||||
if err := runner.InitEnv(s.Config.Dirs.Data); err != nil {
|
||||
logger.Warnf("Environment initialization failed: %v", err)
|
||||
}
|
||||
|
||||
sessions.GlobalSessionManager.SetCookieOptions("MoonsharkSID", "/", "", false, true, 86400)
|
||||
|
||||
var err error
|
||||
s.LuaRunner, err = runner.NewRunner(
|
||||
runner.WithPoolSize(poolSize),
|
||||
runner.WithLibDirs(s.Config.Dirs.Libs...),
|
||||
runner.WithFsDir(s.Config.Dirs.FS),
|
||||
runner.WithDataDir(s.Config.Dirs.Data),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("lua runner init failed: %v", err)
|
||||
}
|
||||
|
||||
logger.Infof("LuaRunner is g2g with %s states!", color.Yellow(strconv.Itoa(poolSize)))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Moonshark) initRouter() error {
|
||||
if err := os.MkdirAll(s.Config.Dirs.Routes, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create routes directory: %w", err)
|
||||
}
|
||||
|
||||
var err error
|
||||
s.LuaRouter, err = router.NewLuaRouter(s.Config.Dirs.Routes)
|
||||
if err != nil {
|
||||
if errors.Is(err, router.ErrRoutesCompilationErrors) {
|
||||
logger.Warnf("Some routes failed to compile")
|
||||
if failedRoutes := s.LuaRouter.ReportFailedRoutes(); len(failedRoutes) > 0 {
|
||||
for _, re := range failedRoutes {
|
||||
logger.Errorf("Route %s %s: %v", re.Method, re.Path, re.Err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("lua router init failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
logger.Infof("LuaRouter is g2g! %s", color.Yellow(s.Config.Dirs.Routes))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Moonshark) setupWatchers() {
|
||||
manager := watchers.GetWatcherManager()
|
||||
|
||||
// Watch routes
|
||||
if routeWatcher, err := watchers.WatchLuaRouter(s.LuaRouter, s.LuaRunner, s.Config.Dirs.Routes); err != nil {
|
||||
logger.Warnf("Routes directory watch failed: %v", err)
|
||||
} else {
|
||||
routesDir := routeWatcher.GetDir()
|
||||
s.cleanupFuncs = append(s.cleanupFuncs, func() error {
|
||||
return manager.UnwatchDirectory(routesDir)
|
||||
})
|
||||
}
|
||||
|
||||
// Watch modules
|
||||
if moduleWatchers, err := watchers.WatchLuaModules(s.LuaRunner, s.Config.Dirs.Libs); err != nil {
|
||||
logger.Warnf("Module directories watch failed: %v", err)
|
||||
} else {
|
||||
for _, watcher := range moduleWatchers {
|
||||
dirPath := watcher.GetDir()
|
||||
s.cleanupFuncs = append(s.cleanupFuncs, func() error {
|
||||
return manager.UnwatchDirectory(dirPath)
|
||||
})
|
||||
}
|
||||
|
||||
plural := "directory"
|
||||
if len(moduleWatchers) != 1 {
|
||||
plural = "directories"
|
||||
}
|
||||
logger.Infof("Watching %s module %s.", color.Yellow(strconv.Itoa(len(moduleWatchers))), plural)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Moonshark) RunScript(scriptPath string) error {
|
||||
scriptPath, err := filepath.Abs(scriptPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to resolve script path: %v", err)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(scriptPath); os.IsNotExist(err) {
|
||||
return fmt.Errorf("script file not found: %s", scriptPath)
|
||||
}
|
||||
|
||||
logger.Infof("Executing: %s", scriptPath)
|
||||
|
||||
resp, err := s.LuaRunner.RunScriptFile(scriptPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("execution failed: %v", err)
|
||||
}
|
||||
|
||||
if resp != nil && resp.Body != nil {
|
||||
logger.Infof("Script result: %v", resp.Body)
|
||||
} else {
|
||||
logger.Infof("Script executed successfully (no return value)")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Moonshark) Start() error {
|
||||
if s.scriptMode {
|
||||
return errors.New("cannot start server in script mode")
|
||||
}
|
||||
|
||||
logger.Infof("Surf's up on port %s!", color.Cyan(strconv.Itoa(s.Config.Server.Port)))
|
||||
|
||||
go func() {
|
||||
if err := s.HTTPServer.ListenAndServe(fmt.Sprintf(":%d", s.Config.Server.Port)); err != nil {
|
||||
if err.Error() != "http: Server closed" {
|
||||
logger.Errorf("Server error: %v", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Moonshark) Shutdown() error {
|
||||
logger.Infof("Shutting down...")
|
||||
|
||||
if !s.scriptMode && s.HTTPServer != nil {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := s.HTTPServer.Shutdown(ctx); err != nil {
|
||||
logger.Errorf("HTTP server shutdown error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, cleanup := range s.cleanupFuncs {
|
||||
if err := cleanup(); err != nil {
|
||||
logger.Warnf("Cleanup error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if s.LuaRunner != nil {
|
||||
s.LuaRunner.Close()
|
||||
}
|
||||
|
||||
if err := runner.CleanupEnv(); err != nil {
|
||||
logger.Warnf("Environment cleanup failed: %v", err)
|
||||
}
|
||||
|
||||
logger.Infof("Shutdown complete")
|
||||
return nil
|
||||
}
|
||||
|
||||
func dirExists(path string) bool {
|
||||
info, err := os.Stat(path)
|
||||
return err == nil && info.IsDir()
|
||||
}
|
||||
|
||||
func banner(scriptMode bool) {
|
||||
if scriptMode {
|
||||
fmt.Println(color.Blue(fmt.Sprintf("Moonshark %s << Script Mode >>", metadata.Version)))
|
||||
return
|
||||
}
|
||||
|
||||
banner := `
|
||||
_____ _________.__ __
|
||||
/ \ ____ ____ ____ / _____/| |__ _____ _______| | __
|
||||
/ \ / \ / _ \ / _ \ / \ \_____ \ | | \\__ \\_ __ \ |/ /
|
||||
/ Y ( <_> | <_> ) | \/ \| Y \/ __ \| | \/ <
|
||||
\____|__ /\____/ \____/|___| /_______ /|___| (____ /__| |__|_ \ %s
|
||||
\/ \/ \/ \/ \/ \/
|
||||
`
|
||||
fmt.Println(color.Blue(fmt.Sprintf(banner, metadata.Version)))
|
||||
}
|
||||
|
||||
func readConfig(path string) *fin.Data {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to open config file %s, using defaults", color.Yellow(path))
|
||||
return fin.NewData()
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
cfg, err := fin.Load(file)
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to load config file %s, using defaults", color.Yellow(path))
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
@ -73,7 +73,7 @@ func precompileModule(m *ModuleInfo) {
|
||||
m.Once.Do(func() {
|
||||
tempState := luajit.New()
|
||||
if tempState == nil {
|
||||
logger.Fatal("Failed to create temp Lua state for %s module compilation", m.Name)
|
||||
logger.Fatalf("Failed to create temp Lua state for %s module compilation", m.Name)
|
||||
return
|
||||
}
|
||||
defer tempState.Close()
|
||||
@ -81,7 +81,7 @@ func precompileModule(m *ModuleInfo) {
|
||||
|
||||
code, err := tempState.CompileBytecode(m.Code, m.Name+".lua")
|
||||
if err != nil {
|
||||
logger.Error("Failed to compile %s module: %v", m.Name, err)
|
||||
logger.Errorf("Failed to compile %s module: %v", m.Name, err)
|
||||
return
|
||||
}
|
||||
|
||||
@ -89,7 +89,7 @@ func precompileModule(m *ModuleInfo) {
|
||||
copy(bytecode, code)
|
||||
m.Bytecode.Store(&bytecode)
|
||||
|
||||
logger.Debug("Successfully precompiled %s.lua to bytecode (%d bytes)", m.Name, len(code))
|
||||
logger.Debugf("Successfully precompiled %s.lua to bytecode (%d bytes)", m.Name, len(code))
|
||||
})
|
||||
}
|
||||
|
||||
@ -102,7 +102,7 @@ func loadModule(state *luajit.State, m *ModuleInfo, verbose bool) error {
|
||||
bytecode := m.Bytecode.Load()
|
||||
if bytecode != nil && len(*bytecode) > 0 {
|
||||
if verbose {
|
||||
logger.Debug("Loading %s.lua from precompiled bytecode", m.Name)
|
||||
logger.Debugf("Loading %s.lua from precompiled bytecode", m.Name)
|
||||
}
|
||||
|
||||
if err := state.LoadBytecode(*bytecode, m.Name+".lua"); err != nil {
|
||||
@ -124,7 +124,7 @@ func loadModule(state *luajit.State, m *ModuleInfo, verbose bool) error {
|
||||
} else {
|
||||
// Fallback to interpreting the source
|
||||
if verbose {
|
||||
logger.Warning("Using non-precompiled %s.lua", m.Name)
|
||||
logger.Warnf("Using non-precompiled %s.lua", m.Name)
|
||||
}
|
||||
|
||||
if err := state.DoString(m.Code); err != nil {
|
||||
@ -154,13 +154,13 @@ func loadSandboxIntoState(state *luajit.State, verbose bool) error {
|
||||
bytecode := sandbox.Bytecode.Load()
|
||||
if bytecode != nil && len(*bytecode) > 0 {
|
||||
if verbose {
|
||||
logger.Debug("Loading sandbox.lua from precompiled bytecode")
|
||||
logger.Debugf("Loading sandbox.lua from precompiled bytecode")
|
||||
}
|
||||
return state.LoadAndRunBytecode(*bytecode, "sandbox.lua")
|
||||
}
|
||||
|
||||
if verbose {
|
||||
logger.Warning("Using non-precompiled sandbox.lua")
|
||||
logger.Warnf("Using non-precompiled sandbox.lua")
|
||||
}
|
||||
return state.DoString(sandboxLuaCode)
|
||||
}
|
||||
|
@ -45,16 +45,16 @@ func InitEnv(dataDir string) error {
|
||||
|
||||
// Load existing .env file if it exists
|
||||
if err := globalEnvManager.load(); err != nil {
|
||||
logger.Warning("Failed to load .env file: %v", err)
|
||||
logger.Warnf("Failed to load .env file: %v", err)
|
||||
}
|
||||
|
||||
count := len(globalEnvManager.vars)
|
||||
if count > 0 {
|
||||
logger.Info("Environment loaded: %s vars from %s",
|
||||
logger.Infof("Environment loaded: %s vars from %s",
|
||||
color.Yellow(fmt.Sprintf("%d", count)),
|
||||
color.Yellow(envPath))
|
||||
} else {
|
||||
logger.Info("Environment initialized: %s", color.Yellow(envPath))
|
||||
logger.Infof("Environment initialized: %s", color.Yellow(envPath))
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -95,7 +95,7 @@ func (e *EnvManager) load() error {
|
||||
// Parse key=value
|
||||
parts := strings.SplitN(line, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
logger.Warning("Invalid .env line %d: %s", lineNum, line)
|
||||
logger.Warnf("Invalid .env line %d: %s", lineNum, line)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -166,7 +166,7 @@ func (e *EnvManager) Save() error {
|
||||
fmt.Fprintf(file, "%s=%s\n", key, strValue)
|
||||
}
|
||||
|
||||
logger.Debug("Environment saved: %d vars to %s", len(e.vars), e.envPath)
|
||||
logger.Debugf("Environment saved: %d vars to %s", len(e.vars), e.envPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ func InitFS(basePath string) error {
|
||||
// Initialize file cache with 2000 entries (reasonable for most use cases)
|
||||
fileCache = lru.NewLRUCache(2000)
|
||||
|
||||
logger.Info("Filesystem is g2g! %s", color.Yellow(fsBasePath))
|
||||
logger.Infof("Filesystem is g2g! %s", color.Yellow(fsBasePath))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ func InitFS(basePath string) error {
|
||||
func CleanupFS() {
|
||||
if fileCache != nil {
|
||||
fileCache.Clear()
|
||||
logger.Info(
|
||||
logger.Infof(
|
||||
"File cache cleared - %s hits, %s misses",
|
||||
color.Yellow(fmt.Sprintf("%d", stats.hits)),
|
||||
color.Red(fmt.Sprintf("%d", stats.misses)),
|
||||
|
@ -315,7 +315,7 @@ func generateToken(state *luajit.State) int {
|
||||
// Generate secure random bytes
|
||||
tokenBytes := make([]byte, length)
|
||||
if _, err := rand.Read(tokenBytes); err != nil {
|
||||
logger.Error("Failed to generate secure token: %v", err)
|
||||
logger.Errorf("Failed to generate secure token: %v", err)
|
||||
state.PushString("")
|
||||
return 1 // Return empty string on error
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ func (l *ModuleLoader) SetScriptDir(dir string) {
|
||||
// debugLog logs a message if debug mode is enabled
|
||||
func (l *ModuleLoader) debugLog(format string, args ...interface{}) {
|
||||
if l.debug {
|
||||
logger.Debug("ModuleLoader "+format, args...)
|
||||
logger.Debugf("ModuleLoader "+format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,7 +134,7 @@ func NewRunner(options ...RunnerOption) (*Runner, error) {
|
||||
|
||||
// initializeStates creates and initializes all states in the pool
|
||||
func (r *Runner) initializeStates() error {
|
||||
logger.Info("[LuaRunner] Creating %s states...", color.Yellow(strconv.Itoa(r.poolSize)))
|
||||
logger.Infof("[LuaRunner] Creating %s states...", color.Yellow(strconv.Itoa(r.poolSize)))
|
||||
|
||||
for i := range r.poolSize {
|
||||
state, err := r.createState(i)
|
||||
@ -153,7 +153,7 @@ func (r *Runner) initializeStates() error {
|
||||
func (r *Runner) createState(index int) (*State, error) {
|
||||
verbose := index == 0
|
||||
if verbose {
|
||||
logger.Debug("Creating Lua state %d", index)
|
||||
logger.Debugf("Creating Lua state %d", index)
|
||||
}
|
||||
|
||||
L := luajit.New()
|
||||
@ -185,7 +185,7 @@ func (r *Runner) createState(index int) (*State, error) {
|
||||
}
|
||||
|
||||
if verbose {
|
||||
logger.Debug("Lua state %d initialized successfully", index)
|
||||
logger.Debugf("Lua state %d initialized successfully", index)
|
||||
}
|
||||
|
||||
return &State{
|
||||
@ -291,7 +291,7 @@ waitForInUse:
|
||||
}
|
||||
|
||||
if time.Now().After(timeout) {
|
||||
logger.Warning("Timeout waiting for states to finish during shutdown, forcing close")
|
||||
logger.Warnf("Timeout waiting for states to finish during shutdown, forcing close")
|
||||
break
|
||||
}
|
||||
|
||||
@ -302,7 +302,7 @@ waitForInUse:
|
||||
for i, state := range r.states {
|
||||
if state != nil {
|
||||
if state.inUse.Load() {
|
||||
logger.Warning("Force closing state %d that is still in use", i)
|
||||
logger.Warnf("Force closing state %d that is still in use", i)
|
||||
}
|
||||
state.L.Cleanup()
|
||||
state.L.Close()
|
||||
@ -313,7 +313,7 @@ waitForInUse:
|
||||
CleanupFS()
|
||||
CleanupSQLite()
|
||||
|
||||
logger.Debug("Runner closed")
|
||||
logger.Debugf("Runner closed")
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -326,7 +326,7 @@ func (r *Runner) RefreshStates() error {
|
||||
return ErrRunnerClosed
|
||||
}
|
||||
|
||||
logger.Info("Runner is refreshing all states...")
|
||||
logger.Infof("Runner is refreshing all states...")
|
||||
|
||||
// Drain all states from the pool
|
||||
for {
|
||||
@ -354,7 +354,7 @@ waitForInUse:
|
||||
}
|
||||
|
||||
if time.Now().After(timeout) {
|
||||
logger.Warning("Timeout waiting for states to finish, forcing refresh")
|
||||
logger.Warnf("Timeout waiting for states to finish, forcing refresh")
|
||||
break
|
||||
}
|
||||
|
||||
@ -365,7 +365,7 @@ waitForInUse:
|
||||
for i, state := range r.states {
|
||||
if state != nil {
|
||||
if state.inUse.Load() {
|
||||
logger.Warning("Force closing state %d that is still in use", i)
|
||||
logger.Warnf("Force closing state %d that is still in use", i)
|
||||
}
|
||||
state.L.Cleanup()
|
||||
state.L.Close()
|
||||
@ -378,21 +378,21 @@ waitForInUse:
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Debug("All states refreshed successfully")
|
||||
logger.Debugf("All states refreshed successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
// NotifyFileChanged alerts the runner about file changes
|
||||
func (r *Runner) NotifyFileChanged(filePath string) bool {
|
||||
logger.Debug("Runner notified of file change: %s", filePath)
|
||||
logger.Debugf("Runner notified of file change: %s", filePath)
|
||||
|
||||
module, isModule := r.moduleLoader.GetModuleByPath(filePath)
|
||||
if isModule {
|
||||
logger.Debug("Refreshing module: %s", module)
|
||||
logger.Debugf("Refreshing module: %s", module)
|
||||
return r.RefreshModule(module)
|
||||
}
|
||||
|
||||
logger.Debug("File change noted but no refresh needed: %s", filePath)
|
||||
logger.Debugf("File change noted but no refresh needed: %s", filePath)
|
||||
return true
|
||||
}
|
||||
|
||||
@ -405,7 +405,7 @@ func (r *Runner) RefreshModule(moduleName string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
logger.Debug("Refreshing module: %s", moduleName)
|
||||
logger.Debugf("Refreshing module: %s", moduleName)
|
||||
|
||||
success := true
|
||||
for _, state := range r.states {
|
||||
@ -416,12 +416,12 @@ func (r *Runner) RefreshModule(moduleName string) bool {
|
||||
// Use the enhanced module loader refresh
|
||||
if err := r.moduleLoader.RefreshModule(state.L, moduleName); err != nil {
|
||||
success = false
|
||||
logger.Debug("Failed to refresh module %s in state %d: %v", moduleName, state.index, err)
|
||||
logger.Debugf("Failed to refresh module %s in state %d: %v", moduleName, state.index, err)
|
||||
}
|
||||
}
|
||||
|
||||
if success {
|
||||
logger.Debug("Successfully refreshed module: %s", moduleName)
|
||||
logger.Debugf("Successfully refreshed module: %s", moduleName)
|
||||
}
|
||||
|
||||
return success
|
||||
@ -436,7 +436,7 @@ func (r *Runner) RefreshModuleByPath(filePath string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
logger.Debug("Refreshing module by path: %s", filePath)
|
||||
logger.Debugf("Refreshing module by path: %s", filePath)
|
||||
|
||||
success := true
|
||||
for _, state := range r.states {
|
||||
@ -447,7 +447,7 @@ func (r *Runner) RefreshModuleByPath(filePath string) bool {
|
||||
// Use the enhanced module loader refresh by path
|
||||
if err := r.moduleLoader.RefreshModuleByPath(state.L, filePath); err != nil {
|
||||
success = false
|
||||
logger.Debug("Failed to refresh module at %s in state %d: %v", filePath, state.index, err)
|
||||
logger.Debugf("Failed to refresh module at %s in state %d: %v", filePath, state.index, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,31 +46,31 @@ func (s *Sandbox) AddModule(name string, module any) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.modules[name] = module
|
||||
logger.Debug("Added module: %s", name)
|
||||
logger.Debugf("Added module: %s", name)
|
||||
}
|
||||
|
||||
// Setup initializes the sandbox in a Lua state
|
||||
func (s *Sandbox) Setup(state *luajit.State, verbose bool) error {
|
||||
if verbose {
|
||||
logger.Debug("Setting up sandbox...")
|
||||
logger.Debugf("Setting up sandbox...")
|
||||
}
|
||||
|
||||
if err := loadSandboxIntoState(state, verbose); err != nil {
|
||||
logger.Error("Failed to load sandbox: %v", err)
|
||||
logger.Errorf("Failed to load sandbox: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.registerCoreFunctions(state); err != nil {
|
||||
logger.Error("Failed to register core functions: %v", err)
|
||||
logger.Errorf("Failed to register core functions: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
s.mu.RLock()
|
||||
for name, module := range s.modules {
|
||||
logger.Debug("Registering module: %s", name)
|
||||
logger.Debugf("Registering module: %s", name)
|
||||
if err := state.PushValue(module); err != nil {
|
||||
s.mu.RUnlock()
|
||||
logger.Error("Failed to register module %s: %v", name, err)
|
||||
logger.Errorf("Failed to register module %s: %v", name, err)
|
||||
return err
|
||||
}
|
||||
state.SetGlobal(name)
|
||||
@ -78,7 +78,7 @@ func (s *Sandbox) Setup(state *luajit.State, verbose bool) error {
|
||||
s.mu.RUnlock()
|
||||
|
||||
if verbose {
|
||||
logger.Debug("Sandbox setup complete")
|
||||
logger.Debugf("Sandbox setup complete")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ var (
|
||||
// InitSQLite initializes the SQLite subsystem
|
||||
func InitSQLite(dir string) {
|
||||
dataDir = dir
|
||||
logger.Info("SQLite is g2g! %s", color.Yellow(dir))
|
||||
logger.Infof("SQLite is g2g! %s", color.Yellow(dir))
|
||||
}
|
||||
|
||||
// SetSQLitePoolSize sets the pool size to match the runner pool size
|
||||
@ -45,12 +45,12 @@ func CleanupSQLite() {
|
||||
|
||||
for name, pool := range dbPools {
|
||||
if err := pool.Close(); err != nil {
|
||||
logger.Error("Failed to close database %s: %v", name, err)
|
||||
logger.Errorf("Failed to close database %s: %v", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
dbPools = make(map[string]*sqlitex.Pool)
|
||||
logger.Debug("SQLite connections closed")
|
||||
logger.Debugf("SQLite connections closed")
|
||||
}
|
||||
|
||||
// getPool returns a connection pool for the database
|
||||
@ -105,7 +105,7 @@ func getPool(dbName string) (*sqlitex.Pool, error) {
|
||||
}
|
||||
|
||||
dbPools[dbName] = pool
|
||||
logger.Debug("Created SQLite pool for %s (size: %d)", dbName, poolSize)
|
||||
logger.Debugf("Created SQLite pool for %s (size: %d)", dbName, poolSize)
|
||||
return pool, nil
|
||||
}
|
||||
|
||||
|
@ -6,8 +6,8 @@ import (
|
||||
fin "git.sharkk.net/Sharkk/Fin"
|
||||
)
|
||||
|
||||
// Config represents the current loaded configuration for the server
|
||||
type Config struct {
|
||||
// Server settings
|
||||
Server struct {
|
||||
Port int
|
||||
Debug bool
|
||||
@ -15,12 +15,10 @@ type Config struct {
|
||||
StaticPrefix string
|
||||
}
|
||||
|
||||
// Runner settings
|
||||
Runner struct {
|
||||
PoolSize int
|
||||
}
|
||||
|
||||
// Directory paths
|
||||
Dirs struct {
|
||||
Routes string
|
||||
Static string
|
||||
@ -30,7 +28,6 @@ type Config struct {
|
||||
Libs []string
|
||||
}
|
||||
|
||||
// Raw fin data struct for custom data
|
||||
data *fin.Data
|
||||
}
|
||||
|
||||
|
@ -8,345 +8,184 @@ import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"Moonshark/utils/color"
|
||||
)
|
||||
|
||||
// ANSI color codes
|
||||
// Log levels
|
||||
const (
|
||||
colorReset = "\033[0m"
|
||||
colorRed = "\033[31m"
|
||||
colorGreen = "\033[32m"
|
||||
colorYellow = "\033[33m"
|
||||
colorBlue = "\033[34m"
|
||||
colorPurple = "\033[35m"
|
||||
colorCyan = "\033[36m"
|
||||
colorGray = "\033[90m"
|
||||
LevelDebug = iota
|
||||
LevelInfo
|
||||
LevelWarn
|
||||
LevelError
|
||||
LevelFatal
|
||||
)
|
||||
|
||||
// Log types
|
||||
const (
|
||||
TypeNone = iota
|
||||
TypeDebug
|
||||
TypeInfo
|
||||
TypeWarning
|
||||
TypeError
|
||||
TypeServer
|
||||
TypeFatal
|
||||
)
|
||||
|
||||
// Type properties
|
||||
var typeProps = map[int]struct {
|
||||
// Level config
|
||||
var levels = map[int]struct {
|
||||
tag string
|
||||
color string
|
||||
color func(string) string
|
||||
}{
|
||||
TypeDebug: {"D", colorCyan},
|
||||
TypeInfo: {"I", colorBlue},
|
||||
TypeWarning: {"W", colorYellow},
|
||||
TypeError: {"E", colorRed},
|
||||
TypeServer: {"S", colorGreen},
|
||||
TypeFatal: {"F", colorPurple},
|
||||
LevelDebug: {"D", color.Cyan},
|
||||
LevelInfo: {"I", color.Blue},
|
||||
LevelWarn: {"W", color.Yellow},
|
||||
LevelError: {"E", color.Red},
|
||||
LevelFatal: {"F", color.Purple},
|
||||
}
|
||||
|
||||
const timeFormat = "15:04:05"
|
||||
|
||||
var (
|
||||
globalLogger *Logger
|
||||
globalLoggerOnce sync.Once
|
||||
global *Logger
|
||||
globalOnce sync.Once
|
||||
)
|
||||
|
||||
type Logger struct {
|
||||
writer io.Writer
|
||||
useColors bool
|
||||
timeFormat string
|
||||
showTimestamp bool
|
||||
mu sync.Mutex
|
||||
debugMode atomic.Bool
|
||||
out io.Writer
|
||||
enabled atomic.Bool
|
||||
timestamp atomic.Bool
|
||||
debug atomic.Bool
|
||||
colors atomic.Bool
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func GetLogger() *Logger {
|
||||
globalLoggerOnce.Do(func() {
|
||||
globalLogger = newLogger(true, true)
|
||||
func init() {
|
||||
globalOnce.Do(func() {
|
||||
global = &Logger{out: os.Stdout}
|
||||
global.enabled.Store(true)
|
||||
global.timestamp.Store(true)
|
||||
global.colors.Store(true)
|
||||
})
|
||||
return globalLogger
|
||||
}
|
||||
|
||||
func InitGlobalLogger(useColors bool, showTimestamp bool) {
|
||||
globalLogger = newLogger(useColors, showTimestamp)
|
||||
}
|
||||
|
||||
func newLogger(useColors bool, showTimestamp bool) *Logger {
|
||||
return &Logger{
|
||||
writer: os.Stdout,
|
||||
useColors: useColors,
|
||||
timeFormat: timeFormat,
|
||||
showTimestamp: showTimestamp,
|
||||
}
|
||||
}
|
||||
|
||||
func New(useColors bool, showTimestamp bool) *Logger {
|
||||
return newLogger(useColors, showTimestamp)
|
||||
}
|
||||
|
||||
func (l *Logger) SetOutput(w io.Writer) {
|
||||
l.mu.Lock()
|
||||
l.writer = w
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
func (l *Logger) TimeFormat() string {
|
||||
return l.timeFormat
|
||||
}
|
||||
|
||||
func (l *Logger) SetTimeFormat(format string) {
|
||||
l.mu.Lock()
|
||||
l.timeFormat = format
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
func (l *Logger) EnableTimestamp() {
|
||||
l.showTimestamp = true
|
||||
}
|
||||
|
||||
func (l *Logger) DisableTimestamp() {
|
||||
l.showTimestamp = false
|
||||
}
|
||||
|
||||
func (l *Logger) EnableColors() {
|
||||
l.useColors = true
|
||||
}
|
||||
|
||||
func (l *Logger) DisableColors() {
|
||||
l.useColors = false
|
||||
}
|
||||
|
||||
func (l *Logger) EnableDebug() {
|
||||
l.debugMode.Store(true)
|
||||
}
|
||||
|
||||
func (l *Logger) DisableDebug() {
|
||||
l.debugMode.Store(false)
|
||||
}
|
||||
|
||||
func (l *Logger) IsDebugEnabled() bool {
|
||||
return l.debugMode.Load()
|
||||
}
|
||||
|
||||
func (l *Logger) applyColor(text, color string) string {
|
||||
if l.useColors {
|
||||
return color + text + colorReset
|
||||
func applyColor(text string, colorFunc func(string) string) string {
|
||||
if global.colors.Load() {
|
||||
return colorFunc(text)
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
func stripAnsiColors(s string) string {
|
||||
result := ""
|
||||
inEscape := false
|
||||
|
||||
for _, c := range s {
|
||||
if inEscape {
|
||||
if c == 'm' {
|
||||
inEscape = false
|
||||
}
|
||||
continue
|
||||
}
|
||||
if c == '\033' {
|
||||
inEscape = true
|
||||
continue
|
||||
}
|
||||
result += string(c)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (l *Logger) writeMessage(logType int, message string, rawMode bool) {
|
||||
if rawMode {
|
||||
l.mu.Lock()
|
||||
_, _ = fmt.Fprint(l.writer, message+"\n")
|
||||
l.mu.Unlock()
|
||||
func write(level int, msg string) {
|
||||
if !global.enabled.Load() {
|
||||
return
|
||||
}
|
||||
|
||||
parts := []string{}
|
||||
|
||||
if l.showTimestamp {
|
||||
timestamp := time.Now().Format(l.timeFormat)
|
||||
if l.useColors {
|
||||
timestamp = l.applyColor(timestamp, colorGray)
|
||||
}
|
||||
parts = append(parts, timestamp)
|
||||
if level == LevelDebug && !global.debug.Load() {
|
||||
return
|
||||
}
|
||||
|
||||
if logType != TypeNone {
|
||||
props := typeProps[logType]
|
||||
tag := "[" + props.tag + "]"
|
||||
if l.useColors {
|
||||
tag = l.applyColor(tag, props.color)
|
||||
}
|
||||
var parts []string
|
||||
|
||||
if global.timestamp.Load() {
|
||||
ts := applyColor(time.Now().Format("3:04PM"), color.Gray)
|
||||
parts = append(parts, ts)
|
||||
}
|
||||
|
||||
if cfg, ok := levels[level]; ok {
|
||||
tag := applyColor("["+cfg.tag+"]", cfg.color)
|
||||
parts = append(parts, tag)
|
||||
}
|
||||
|
||||
parts = append(parts, message)
|
||||
logLine := strings.Join(parts, " ") + "\n"
|
||||
parts = append(parts, msg)
|
||||
line := strings.Join(parts, " ") + "\n"
|
||||
|
||||
l.mu.Lock()
|
||||
_, _ = fmt.Fprint(l.writer, logLine)
|
||||
if logType == TypeFatal {
|
||||
if f, ok := l.writer.(*os.File); ok {
|
||||
_ = f.Sync()
|
||||
global.mu.Lock()
|
||||
fmt.Fprint(global.out, line)
|
||||
if level == LevelFatal {
|
||||
if f, ok := global.out.(*os.File); ok {
|
||||
f.Sync()
|
||||
}
|
||||
}
|
||||
l.mu.Unlock()
|
||||
}
|
||||
global.mu.Unlock()
|
||||
|
||||
func (l *Logger) log(logType int, format string, args ...any) {
|
||||
// Only filter debug messages
|
||||
if logType == TypeDebug && !l.debugMode.Load() {
|
||||
return
|
||||
}
|
||||
|
||||
var message string
|
||||
if len(args) > 0 {
|
||||
message = fmt.Sprintf(format, args...)
|
||||
} else {
|
||||
message = format
|
||||
}
|
||||
|
||||
l.writeMessage(logType, message, false)
|
||||
|
||||
if logType == TypeFatal {
|
||||
if level == LevelFatal {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) LogRaw(format string, args ...any) {
|
||||
var message string
|
||||
func log(level int, format string, args ...any) {
|
||||
var msg string
|
||||
if len(args) > 0 {
|
||||
message = fmt.Sprintf(format, args...)
|
||||
msg = fmt.Sprintf(format, args...)
|
||||
} else {
|
||||
message = format
|
||||
msg = format
|
||||
}
|
||||
write(level, msg)
|
||||
}
|
||||
|
||||
func Debugf(format string, args ...any) { log(LevelDebug, format, args...) }
|
||||
func Infof(format string, args ...any) { log(LevelInfo, format, args...) }
|
||||
func Warnf(format string, args ...any) { log(LevelWarn, format, args...) }
|
||||
func Errorf(format string, args ...any) { log(LevelError, format, args...) }
|
||||
func Fatalf(format string, args ...any) { log(LevelFatal, format, args...) }
|
||||
|
||||
func Raw(format string, args ...any) {
|
||||
if !global.enabled.Load() {
|
||||
return
|
||||
}
|
||||
|
||||
if !l.useColors {
|
||||
message = stripAnsiColors(message)
|
||||
var msg string
|
||||
if len(args) > 0 {
|
||||
msg = fmt.Sprintf(format, args...)
|
||||
} else {
|
||||
msg = format
|
||||
}
|
||||
|
||||
l.writeMessage(TypeInfo, message, true)
|
||||
global.mu.Lock()
|
||||
fmt.Fprint(global.out, msg+"\n")
|
||||
global.mu.Unlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Debug(format string, args ...any) {
|
||||
l.log(TypeDebug, format, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Info(format string, args ...any) {
|
||||
l.log(TypeInfo, format, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Warning(format string, args ...any) {
|
||||
l.log(TypeWarning, format, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Error(format string, args ...any) {
|
||||
l.log(TypeError, format, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Fatal(format string, args ...any) {
|
||||
l.log(TypeFatal, format, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Server(format string, args ...any) {
|
||||
l.log(TypeServer, format, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) LogRequest(statusCode int, method, path string, duration time.Duration) {
|
||||
var statusColor string
|
||||
func Request(status int, method, path string, duration time.Duration) {
|
||||
if !global.enabled.Load() {
|
||||
return
|
||||
}
|
||||
|
||||
var statusColor func(string) string
|
||||
switch {
|
||||
case statusCode < 300:
|
||||
statusColor = colorGreen
|
||||
case statusCode < 400:
|
||||
statusColor = colorCyan
|
||||
case statusCode < 500:
|
||||
statusColor = colorYellow
|
||||
case status < 300:
|
||||
statusColor = color.Green
|
||||
case status < 400:
|
||||
statusColor = color.Cyan
|
||||
case status < 500:
|
||||
statusColor = color.Yellow
|
||||
default:
|
||||
statusColor = colorRed
|
||||
statusColor = color.Red
|
||||
}
|
||||
|
||||
var durationStr string
|
||||
micros := duration.Microseconds()
|
||||
if micros < 1000 {
|
||||
durationStr = fmt.Sprintf("%.0fµs", float64(micros))
|
||||
} else if micros < 1000000 {
|
||||
durationStr = fmt.Sprintf("%.1fms", float64(micros)/1000)
|
||||
} else {
|
||||
durationStr = fmt.Sprintf("%.2fs", duration.Seconds())
|
||||
var dur string
|
||||
us := duration.Microseconds()
|
||||
switch {
|
||||
case us < 1000:
|
||||
dur = fmt.Sprintf("%.0fµs", float64(us))
|
||||
case us < 1000000:
|
||||
dur = fmt.Sprintf("%.1fms", float64(us)/1000)
|
||||
default:
|
||||
dur = fmt.Sprintf("%.2fs", duration.Seconds())
|
||||
}
|
||||
|
||||
message := fmt.Sprintf("%s %s %s %s",
|
||||
l.applyColor("["+method+"]", colorGray),
|
||||
l.applyColor(fmt.Sprintf("%d", statusCode), statusColor),
|
||||
l.applyColor(path, colorGray),
|
||||
l.applyColor(durationStr, colorGray),
|
||||
msg := fmt.Sprintf("%s %s %s %s",
|
||||
applyColor("["+method+"]", color.Gray),
|
||||
applyColor(fmt.Sprintf("%d", status), statusColor),
|
||||
applyColor(path, color.Gray),
|
||||
applyColor(dur, color.Gray),
|
||||
)
|
||||
|
||||
l.writeMessage(TypeNone, message, false)
|
||||
}
|
||||
|
||||
// Global functions
|
||||
func Debug(format string, args ...any) {
|
||||
GetLogger().Debug(format, args...)
|
||||
}
|
||||
|
||||
func Info(format string, args ...any) {
|
||||
GetLogger().Info(format, args...)
|
||||
}
|
||||
|
||||
func Warning(format string, args ...any) {
|
||||
GetLogger().Warning(format, args...)
|
||||
}
|
||||
|
||||
func Error(format string, args ...any) {
|
||||
GetLogger().Error(format, args...)
|
||||
}
|
||||
|
||||
func Fatal(format string, args ...any) {
|
||||
GetLogger().Fatal(format, args...)
|
||||
}
|
||||
|
||||
func Server(format string, args ...any) {
|
||||
GetLogger().Server(format, args...)
|
||||
}
|
||||
|
||||
func LogRaw(format string, args ...any) {
|
||||
GetLogger().LogRaw(format, args...)
|
||||
global.mu.Lock()
|
||||
fmt.Fprint(global.out, msg+"\n")
|
||||
global.mu.Unlock()
|
||||
}
|
||||
|
||||
func SetOutput(w io.Writer) {
|
||||
GetLogger().SetOutput(w)
|
||||
global.mu.Lock()
|
||||
global.out = w
|
||||
global.mu.Unlock()
|
||||
}
|
||||
|
||||
func TimeFormat() string {
|
||||
return GetLogger().TimeFormat()
|
||||
}
|
||||
|
||||
func EnableDebug() {
|
||||
GetLogger().EnableDebug()
|
||||
}
|
||||
|
||||
func DisableDebug() {
|
||||
GetLogger().DisableDebug()
|
||||
}
|
||||
|
||||
func IsDebugEnabled() bool {
|
||||
return GetLogger().IsDebugEnabled()
|
||||
}
|
||||
|
||||
func EnableTimestamp() {
|
||||
GetLogger().EnableTimestamp()
|
||||
}
|
||||
|
||||
func DisableTimestamp() {
|
||||
GetLogger().DisableTimestamp()
|
||||
}
|
||||
|
||||
func LogRequest(statusCode int, method, path string, duration time.Duration) {
|
||||
GetLogger().LogRequest(statusCode, method, path, duration)
|
||||
}
|
||||
func Enable() { global.enabled.Store(true) }
|
||||
func Disable() { global.enabled.Store(false) }
|
||||
func IsEnabled() bool { return global.enabled.Load() }
|
||||
func EnableColors() { global.colors.Store(true) }
|
||||
func DisableColors() { global.colors.Store(false) }
|
||||
func ColorsEnabled() bool { return global.colors.Load() }
|
||||
func Timestamp(enabled bool) { global.timestamp.Store(enabled) }
|
||||
func Debug(enabled bool) { global.debug.Store(enabled) }
|
||||
func IsDebug() bool { return global.debug.Load() }
|
||||
|
@ -48,7 +48,7 @@ func WatchLuaRouter(router *router.LuaRouter, runner *runner.Runner, routesDir s
|
||||
return nil, fmt.Errorf("failed to watch directory: %w", err)
|
||||
}
|
||||
|
||||
logger.Info("Started watching Lua routes! %s", color.Yellow(routesDir))
|
||||
logger.Infof("Started watching Lua routes! %s", color.Yellow(routesDir))
|
||||
return watcher, nil
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@ func WatchLuaModules(luaRunner *runner.Runner, libDirs []string) ([]*DirectoryWa
|
||||
}
|
||||
|
||||
watchers = append(watchers, watcher)
|
||||
logger.Info("Started watching Lua modules! %s", color.Yellow(dir))
|
||||
logger.Infof("Started watching Lua modules! %s", color.Yellow(dir))
|
||||
}
|
||||
|
||||
return watchers, nil
|
||||
|
@ -198,12 +198,12 @@ func (w *DirectoryWatcher) notifyChange() {
|
||||
|
||||
// logDebug logs a debug message with the watcher's directory prefix
|
||||
func (w *DirectoryWatcher) logDebug(format string, args ...any) {
|
||||
logger.Debug("[Watcher] [%s] %s", w.dir, fmt.Sprintf(format, args...))
|
||||
logger.Debugf("[Watcher] [%s] %s", w.dir, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// logError logs an error message with the watcher's directory prefix
|
||||
func (w *DirectoryWatcher) logError(format string, args ...any) {
|
||||
logger.Error("[Watcher] [%s] %s", w.dir, fmt.Sprintf(format, args...))
|
||||
logger.Errorf("[Watcher] [%s] %s", w.dir, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// GetDir gets the DirectoryWatcher's current directory
|
||||
|
@ -86,7 +86,7 @@ func (m *WatcherManager) WatchDirectory(config DirectoryWatcherConfig) (*Directo
|
||||
}
|
||||
|
||||
m.watchers[config.Dir] = watcher
|
||||
logger.Debug("WatcherManager added watcher for %s", config.Dir)
|
||||
logger.Debugf("WatcherManager added watcher for %s", config.Dir)
|
||||
|
||||
return watcher, nil
|
||||
}
|
||||
@ -101,7 +101,7 @@ func (m *WatcherManager) UnwatchDirectory(dir string) error {
|
||||
}
|
||||
|
||||
delete(m.watchers, dir)
|
||||
logger.Debug("WatcherManager removed watcher for %s", dir)
|
||||
logger.Debugf("WatcherManager removed watcher for %s", dir)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -133,7 +133,7 @@ func (m *WatcherManager) checkAllDirectories() {
|
||||
for _, watcher := range watchers {
|
||||
if watcher.consecutiveErrors > 3 {
|
||||
if watcher.consecutiveErrors == 4 {
|
||||
logger.Error("Temporarily skipping directory %s due to errors: %v",
|
||||
logger.Errorf("Temporarily skipping directory %s due to errors: %v",
|
||||
watcher.dir, watcher.lastError)
|
||||
watcher.consecutiveErrors++
|
||||
}
|
||||
@ -142,7 +142,7 @@ func (m *WatcherManager) checkAllDirectories() {
|
||||
|
||||
changed, err := watcher.checkForChanges()
|
||||
if err != nil {
|
||||
logger.Error("Error checking directory %s: %v", watcher.dir, err)
|
||||
logger.Errorf("Error checking directory %s: %v", watcher.dir, err)
|
||||
continue
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user