add script running, move all to main
This commit is contained in:
parent
4fbdf995ef
commit
fbd0753d1d
@ -1,262 +0,0 @@
|
|||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"Moonshark/core/http"
|
|
||||||
"Moonshark/core/routers"
|
|
||||||
"Moonshark/core/runner"
|
|
||||||
"Moonshark/core/sessions"
|
|
||||||
"Moonshark/core/utils/config"
|
|
||||||
"Moonshark/core/utils/logger"
|
|
||||||
"Moonshark/core/watchers"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Moonshark represents the server and all its dependencies
|
|
||||||
type Moonshark struct {
|
|
||||||
Config *config.Config
|
|
||||||
LuaRouter *routers.LuaRouter
|
|
||||||
StaticRouter *routers.StaticRouter
|
|
||||||
LuaRunner *runner.Runner
|
|
||||||
HTTPServer *http.Server
|
|
||||||
|
|
||||||
// Clean-up functions for watchers
|
|
||||||
cleanupFuncs []func() error
|
|
||||||
}
|
|
||||||
|
|
||||||
// dirExists checks if a directory exists without creating it
|
|
||||||
func dirExists(path string) bool {
|
|
||||||
info, err := os.Stat(path)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return info.IsDir()
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMoonshark creates a new Moonshark server instance
|
|
||||||
func NewMoonshark(configPath string) (*Moonshark, error) {
|
|
||||||
server := &Moonshark{}
|
|
||||||
|
|
||||||
if err := server.loadConfig(configPath); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
server.setupLogging()
|
|
||||||
|
|
||||||
if err := server.initRouters(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := server.initRunner(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := server.setupWatchers(); err != nil {
|
|
||||||
logger.Warning("Error setting up watchers: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
server.HTTPServer = http.New(
|
|
||||||
server.LuaRouter,
|
|
||||||
server.StaticRouter,
|
|
||||||
server.LuaRunner,
|
|
||||||
server.Config.Server.HTTPLogging,
|
|
||||||
server.Config.Server.Debug,
|
|
||||||
server.Config.Dirs.Override,
|
|
||||||
server.Config,
|
|
||||||
)
|
|
||||||
|
|
||||||
return server, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// loadConfig loads the configuration file
|
|
||||||
func (s *Moonshark) loadConfig(configPath string) error {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
s.Config, err = config.Load(configPath)
|
|
||||||
if err != nil {
|
|
||||||
logger.Warning("Wipeout! Couldn't load config file: %v", err)
|
|
||||||
logger.Warning("Rolling with the default setup")
|
|
||||||
s.Config = config.New()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// setupLogging configures the logger based on config settings
|
|
||||||
func (s *Moonshark) setupLogging() {
|
|
||||||
switch s.Config.Server.LogLevel {
|
|
||||||
case "warn":
|
|
||||||
logger.SetLevel(logger.LevelWarning)
|
|
||||||
case "error":
|
|
||||||
logger.SetLevel(logger.LevelError)
|
|
||||||
case "server":
|
|
||||||
logger.SetLevel(logger.LevelServer)
|
|
||||||
case "fatal":
|
|
||||||
logger.SetLevel(logger.LevelFatal)
|
|
||||||
default:
|
|
||||||
logger.SetLevel(logger.LevelInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.Config.Server.Debug {
|
|
||||||
logger.EnableDebug() // Force debug logs regardless of level
|
|
||||||
logger.Debug("Debug mode is ready to party, bro")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// initRouters initializes the Lua and static routers
|
|
||||||
func (s *Moonshark) initRouters() error {
|
|
||||||
if !dirExists(s.Config.Dirs.Routes) {
|
|
||||||
logger.Fatal("Routes directory doesn't exist: %s", s.Config.Dirs.Routes)
|
|
||||||
return fmt.Errorf("routes directory doesn't exist: %s", s.Config.Dirs.Routes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize Lua router for dynamic routes
|
|
||||||
var err error
|
|
||||||
s.LuaRouter, err = routers.NewLuaRouter(s.Config.Dirs.Routes)
|
|
||||||
if err != nil {
|
|
||||||
// Check if this is a compilation warning or a more serious error
|
|
||||||
if errors.Is(err, routers.ErrRoutesCompilationErrors) {
|
|
||||||
// Some routes failed to compile, but router is still usable
|
|
||||||
logger.Warning("Some Lua routes failed to compile. Check logs for details.")
|
|
||||||
|
|
||||||
// Log details about each failed route
|
|
||||||
if failedRoutes := s.LuaRouter.ReportFailedRoutes(); len(failedRoutes) > 0 {
|
|
||||||
for _, re := range failedRoutes {
|
|
||||||
logger.Error("Route %s %s failed to compile: %v", re.Method, re.Path, re.Err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// More serious error that prevents router initialization
|
|
||||||
return fmt.Errorf("failed to initialize Lua router: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.Info("Lua router is stoked and riding routes from %s", s.Config.Dirs.Routes)
|
|
||||||
|
|
||||||
if dirExists(s.Config.Dirs.Static) {
|
|
||||||
s.StaticRouter, err = routers.NewStaticRouter(s.Config.Dirs.Static)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to initialize static router: %v", err)
|
|
||||||
}
|
|
||||||
logger.Info("Static router catching waves with files from %s", s.Config.Dirs.Static)
|
|
||||||
s.StaticRouter.EnableDebugLog()
|
|
||||||
} else {
|
|
||||||
logger.Warning("Static directory doesn't exist: %s - static file serving disabled", s.Config.Dirs.Static)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// initRunner initializes the Lua runner
|
|
||||||
func (s *Moonshark) initRunner() error {
|
|
||||||
if !dirExists(s.Config.Dirs.Override) {
|
|
||||||
logger.Warning("Override directory doesn't exist: %s", s.Config.Dirs.Override)
|
|
||||||
s.Config.Dirs.Override = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, dir := range s.Config.Dirs.Libs {
|
|
||||||
if !dirExists(dir) {
|
|
||||||
logger.Warning("Lib directory doesn't exist: %s", dir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionManager := sessions.GlobalSessionManager
|
|
||||||
sessionManager.SetCookieOptions(
|
|
||||||
"MoonsharkSID", // name
|
|
||||||
"/", // path
|
|
||||||
"", // domain
|
|
||||||
false, // secure
|
|
||||||
true, // httpOnly
|
|
||||||
86400, // maxAge (1 day)
|
|
||||||
)
|
|
||||||
|
|
||||||
runnerOpts := []runner.RunnerOption{
|
|
||||||
runner.WithPoolSize(s.Config.Runner.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("failed to initialize Lua runner: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Server("Lua runner waxing up with a pool size of %d", s.Config.Runner.PoolSize)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// setupWatchers initializes and starts all configured file watchers
|
|
||||||
func (s *Moonshark) setupWatchers() error {
|
|
||||||
manager := watchers.GetWatcherManager()
|
|
||||||
|
|
||||||
// Set up watcher for Lua routes
|
|
||||||
routeWatcher, err := watchers.WatchLuaRouter(s.LuaRouter, s.LuaRunner, s.Config.Dirs.Routes)
|
|
||||||
if err != nil {
|
|
||||||
logger.Warning("Failed to watch routes directory: %v", err)
|
|
||||||
} else {
|
|
||||||
routesDir := routeWatcher.GetDir()
|
|
||||||
s.cleanupFuncs = append(s.cleanupFuncs, func() error {
|
|
||||||
return manager.UnwatchDirectory(routesDir)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up watchers for Lua modules libraries
|
|
||||||
moduleWatchers, err := watchers.WatchLuaModules(s.LuaRunner, s.Config.Dirs.Libs)
|
|
||||||
if err != nil {
|
|
||||||
logger.Warning("Failed to watch Lua module directories: %v", err)
|
|
||||||
} else {
|
|
||||||
for _, watcher := range moduleWatchers {
|
|
||||||
dirPath := watcher.GetDir()
|
|
||||||
s.cleanupFuncs = append(s.cleanupFuncs, func() error {
|
|
||||||
return manager.UnwatchDirectory(dirPath)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
logger.Info("File watchers active for %d Lua module directories", len(moduleWatchers))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start starts the HTTP server
|
|
||||||
func (s *Moonshark) Start() error {
|
|
||||||
logger.Server("Surf's up on port %d!", 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 the server
|
|
||||||
func (s *Moonshark) Shutdown() error {
|
|
||||||
logger.Server("Shutting down server...")
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := s.HTTPServer.Shutdown(ctx); err != nil {
|
|
||||||
logger.Error("Server shutdown error: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, cleanup := range s.cleanupFuncs {
|
|
||||||
if err := cleanup(); err != nil {
|
|
||||||
logger.Warning("Cleanup error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s.LuaRunner.Close()
|
|
||||||
|
|
||||||
logger.Server("Server stopped")
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -4,6 +4,8 @@ import (
|
|||||||
"Moonshark/core/utils/logger"
|
"Moonshark/core/utils/logger"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
@ -412,3 +414,84 @@ func (r *Runner) GetActiveStateCount() int {
|
|||||||
|
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RunScriptFile loads, compiles and executes a Lua script file
|
||||||
|
func (r *Runner) RunScriptFile(filePath string) (*Response, error) {
|
||||||
|
if !r.isRunning.Load() {
|
||||||
|
return nil, ErrRunnerClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||||
|
return nil, fmt.Errorf("script file not found: %s", filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err := os.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
absPath, err := filepath.Abs(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get absolute path: %w", err)
|
||||||
|
}
|
||||||
|
scriptDir := filepath.Dir(absPath)
|
||||||
|
|
||||||
|
r.mu.Lock()
|
||||||
|
prevScriptDir := r.scriptDir
|
||||||
|
r.scriptDir = scriptDir
|
||||||
|
r.moduleLoader.SetScriptDir(scriptDir)
|
||||||
|
r.mu.Unlock()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
r.mu.Lock()
|
||||||
|
r.scriptDir = prevScriptDir
|
||||||
|
r.moduleLoader.SetScriptDir(prevScriptDir)
|
||||||
|
r.mu.Unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
|
var stateIndex int
|
||||||
|
select {
|
||||||
|
case stateIndex = <-r.statePool:
|
||||||
|
// Got a state
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
return nil, ErrTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
state := r.states[stateIndex]
|
||||||
|
if state == nil {
|
||||||
|
r.statePool <- stateIndex
|
||||||
|
return nil, ErrStateNotReady
|
||||||
|
}
|
||||||
|
|
||||||
|
state.inUse = true
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
state.inUse = false
|
||||||
|
if r.isRunning.Load() {
|
||||||
|
select {
|
||||||
|
case r.statePool <- stateIndex:
|
||||||
|
// State returned to pool
|
||||||
|
default:
|
||||||
|
// Pool is full or closed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
bytecode, err := state.L.CompileBytecode(string(content), filepath.Base(absPath))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("compilation error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := NewContext()
|
||||||
|
defer ctx.Release()
|
||||||
|
|
||||||
|
ctx.Set("_script_path", absPath)
|
||||||
|
ctx.Set("_script_dir", scriptDir)
|
||||||
|
|
||||||
|
response, err := state.sandbox.Execute(state.L, bytecode, ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("execution error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
@ -12,6 +12,8 @@ import (
|
|||||||
|
|
||||||
"Moonshark/core/utils/logger"
|
"Moonshark/core/utils/logger"
|
||||||
|
|
||||||
|
"maps"
|
||||||
|
|
||||||
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -256,7 +258,7 @@ func luaSQLQuery(state *luajit.State) int {
|
|||||||
row := make(map[string]any)
|
row := make(map[string]any)
|
||||||
columnCount := stmt.ColumnCount()
|
columnCount := stmt.ColumnCount()
|
||||||
|
|
||||||
for i := 0; i < columnCount; i++ {
|
for i := range columnCount {
|
||||||
columnName := stmt.ColumnName(i)
|
columnName := stmt.ColumnName(i)
|
||||||
columnType := stmt.ColumnType(i)
|
columnType := stmt.ColumnType(i)
|
||||||
|
|
||||||
@ -279,9 +281,7 @@ func luaSQLQuery(state *luajit.State) int {
|
|||||||
|
|
||||||
// Add row copy to results
|
// Add row copy to results
|
||||||
rowCopy := make(map[string]any, len(row))
|
rowCopy := make(map[string]any, len(row))
|
||||||
for k, v := range row {
|
maps.Copy(rowCopy, row)
|
||||||
rowCopy[k] = v
|
|
||||||
}
|
|
||||||
rows = append(rows, rowCopy)
|
rows = append(rows, rowCopy)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
351
main.go
351
main.go
@ -1,41 +1,372 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
"Moonshark/core"
|
"Moonshark/core/http"
|
||||||
|
"Moonshark/core/routers"
|
||||||
|
"Moonshark/core/runner"
|
||||||
|
"Moonshark/core/sessions"
|
||||||
|
"Moonshark/core/utils/config"
|
||||||
"Moonshark/core/utils/logger"
|
"Moonshark/core/utils/logger"
|
||||||
|
"Moonshark/core/watchers"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Moonshark represents the server and all its dependencies
|
||||||
|
type Moonshark struct {
|
||||||
|
Config *config.Config
|
||||||
|
LuaRouter *routers.LuaRouter
|
||||||
|
StaticRouter *routers.StaticRouter
|
||||||
|
LuaRunner *runner.Runner
|
||||||
|
HTTPServer *http.Server
|
||||||
|
cleanupFuncs []func() error
|
||||||
|
scriptMode bool
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
configPath := flag.String("config", "config.lua", "Path to configuration file")
|
configPath := flag.String("config", "config.lua", "Path to configuration file")
|
||||||
|
scriptPath := flag.String("script", "", "Path to Lua script to execute once")
|
||||||
|
debugFlag := flag.Bool("debug", false, "Enable debug mode")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
logger.InitGlobalLogger(logger.LevelDebug, true, false)
|
// Initialize logger with basic settings first
|
||||||
logger.Server("Moonshark is starting to prowl 🦈")
|
logger.InitGlobalLogger(logger.LevelInfo, true, false)
|
||||||
|
|
||||||
server, err := core.NewMoonshark(*configPath)
|
scriptMode := *scriptPath != ""
|
||||||
if err != nil {
|
var moonshark *Moonshark
|
||||||
logger.Fatal("Failed to initialize server: %v", err)
|
var err error
|
||||||
|
|
||||||
|
if scriptMode {
|
||||||
|
logger.Server("Moonshark script execution mode 🦈")
|
||||||
|
moonshark, err = initScriptMode(*configPath, *debugFlag)
|
||||||
|
} else {
|
||||||
|
logger.Server("Moonshark server mode 🦈")
|
||||||
|
moonshark, err = initServerMode(*configPath, *debugFlag)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := server.Start(); err != nil {
|
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)
|
logger.Fatal("Failed to start server: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait for shutdown signal
|
||||||
stop := make(chan os.Signal, 1)
|
stop := make(chan os.Signal, 1)
|
||||||
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
|
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
|
||||||
<-stop
|
<-stop
|
||||||
|
|
||||||
fmt.Print("\n")
|
fmt.Print("\n")
|
||||||
logger.Server("Shutdown signal received")
|
logger.Server("Shutdown signal received")
|
||||||
|
}
|
||||||
|
|
||||||
if err := server.Shutdown(); err != nil {
|
// initScriptMode initializes minimal components needed for script execution
|
||||||
logger.Error("Error during shutdown: %v", err)
|
func initScriptMode(configPath string, debug bool) (*Moonshark, error) {
|
||||||
os.Exit(1)
|
moonshark := &Moonshark{scriptMode: true}
|
||||||
|
|
||||||
|
// Load config (minimal validation for script mode)
|
||||||
|
cfg, err := config.Load(configPath)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warning("Config load failed: %v, using defaults", err)
|
||||||
|
cfg = config.New()
|
||||||
|
}
|
||||||
|
moonshark.Config = cfg
|
||||||
|
|
||||||
|
// Setup logging
|
||||||
|
setupLogging(cfg, debug)
|
||||||
|
|
||||||
|
// 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),
|
||||||
|
}
|
||||||
|
|
||||||
|
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(configPath string, debug bool) (*Moonshark, error) {
|
||||||
|
moonshark := &Moonshark{scriptMode: false}
|
||||||
|
|
||||||
|
cfg, err := config.Load(configPath)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warning("Config load failed: %v, using defaults", err)
|
||||||
|
cfg = config.New()
|
||||||
|
}
|
||||||
|
moonshark.Config = cfg
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
cfg.Server.Debug = true
|
||||||
|
}
|
||||||
|
|
||||||
|
setupLogging(cfg, debug)
|
||||||
|
|
||||||
|
if err := initRouters(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)
|
||||||
|
}
|
||||||
|
|
||||||
|
moonshark.HTTPServer = http.New(
|
||||||
|
moonshark.LuaRouter,
|
||||||
|
moonshark.StaticRouter,
|
||||||
|
moonshark.LuaRunner,
|
||||||
|
cfg.Server.HTTPLogging,
|
||||||
|
cfg.Server.Debug,
|
||||||
|
cfg.Dirs.Override,
|
||||||
|
cfg,
|
||||||
|
)
|
||||||
|
|
||||||
|
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.Server("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.Server("Script result: %v", resp.Body)
|
||||||
|
} else {
|
||||||
|
logger.Server("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.Server("Surf's up on port %d!", 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.Server("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()
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Server("Shutdown complete")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupLogging(cfg *config.Config, debug bool) {
|
||||||
|
switch cfg.Server.LogLevel {
|
||||||
|
case "warn":
|
||||||
|
logger.SetLevel(logger.LevelWarning)
|
||||||
|
case "error":
|
||||||
|
logger.SetLevel(logger.LevelError)
|
||||||
|
case "server":
|
||||||
|
logger.SetLevel(logger.LevelServer)
|
||||||
|
case "fatal":
|
||||||
|
logger.SetLevel(logger.LevelFatal)
|
||||||
|
default:
|
||||||
|
logger.SetLevel(logger.LevelInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug || cfg.Server.Debug {
|
||||||
|
logger.EnableDebug()
|
||||||
|
logger.Debug("Debug logging enabled")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dirExists(path string) bool {
|
||||||
|
info, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return info.IsDir()
|
||||||
|
}
|
||||||
|
|
||||||
|
func initRouters(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 = routers.NewLuaRouter(s.Config.Dirs.Routes)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, routers.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("Lua router initialized: %s", s.Config.Dirs.Routes)
|
||||||
|
|
||||||
|
if dirExists(s.Config.Dirs.Static) {
|
||||||
|
s.StaticRouter, err = routers.NewStaticRouter(s.Config.Dirs.Static)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("static router init failed: %v", err)
|
||||||
|
}
|
||||||
|
logger.Info("Static router initialized: %s", s.Config.Dirs.Static)
|
||||||
|
} else {
|
||||||
|
logger.Warning("Static directory not found: %s", s.Config.Dirs.Static)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func initRunner(s *Moonshark) error {
|
||||||
|
if !dirExists(s.Config.Dirs.Override) {
|
||||||
|
logger.Warning("Override directory not found: %s", 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", dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Server("Lua runner initialized with pool size: %d", 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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
logger.Info("Watching %d module directories", len(moduleWatchers))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user