require watcher
This commit is contained in:
parent
3e26f348b4
commit
87aadc8574
|
@ -3,6 +3,9 @@ package runner
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
|
@ -139,9 +142,9 @@ func setupRequireFunction(state *luajit.State) error {
|
||||||
function __setup_secure_require(env)
|
function __setup_secure_require(env)
|
||||||
-- Replace env.require with our secure version
|
-- Replace env.require with our secure version
|
||||||
env.require = function(modname)
|
env.require = function(modname)
|
||||||
-- Check if already loaded in package.loaded
|
-- Check if already loaded in this environment's package.loaded
|
||||||
if package.loaded[modname] then
|
if env.package.loaded[modname] then
|
||||||
return package.loaded[modname]
|
return env.package.loaded[modname]
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Try to load the module using our Go loader
|
-- Try to load the module using our Go loader
|
||||||
|
@ -164,8 +167,8 @@ func setupRequireFunction(state *luajit.State) error {
|
||||||
result = true
|
result = true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Cache the result
|
-- Cache the result in this environment only
|
||||||
package.loaded[modname] = result
|
env.package.loaded[modname] = result
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
@ -325,12 +328,87 @@ func (r *LuaRunner) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RequireCache returns the require cache for external access
|
||||||
|
func (r *LuaRunner) RequireCache() *RequireCache {
|
||||||
|
return r.requireCache
|
||||||
|
}
|
||||||
|
|
||||||
// ClearRequireCache clears the cache of loaded modules
|
// ClearRequireCache clears the cache of loaded modules
|
||||||
func (r *LuaRunner) ClearRequireCache() {
|
func (r *LuaRunner) ClearRequireCache() {
|
||||||
r.requireCache = NewRequireCache()
|
r.requireCache.Clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddModule adds a module to the sandbox environment
|
// AddModule adds a module to the sandbox environment
|
||||||
func (r *LuaRunner) AddModule(name string, module any) {
|
func (r *LuaRunner) AddModule(name string, module any) {
|
||||||
r.sandbox.AddModule(name, module)
|
r.sandbox.AddModule(name, module)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RefreshRequireCache refreshes the module cache if needed
|
||||||
|
func (r *LuaRunner) RefreshRequireCache() int {
|
||||||
|
count := r.requireCache.RefreshAll()
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetPackageLoaded resets the Lua package.loaded table
|
||||||
|
func (r *LuaRunner) ResetPackageLoaded() error {
|
||||||
|
return r.state.DoString(`
|
||||||
|
-- Create list of modules to unload (excluding core modules)
|
||||||
|
local to_unload = {}
|
||||||
|
for name, _ in pairs(package.loaded) do
|
||||||
|
-- Skip core modules
|
||||||
|
if name ~= "string" and
|
||||||
|
name ~= "table" and
|
||||||
|
name ~= "math" and
|
||||||
|
name ~= "os" and
|
||||||
|
name ~= "package" and
|
||||||
|
name ~= "io" and
|
||||||
|
name ~= "coroutine" and
|
||||||
|
name ~= "debug" and
|
||||||
|
name ~= "_G" then
|
||||||
|
table.insert(to_unload, name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Unload each module
|
||||||
|
for _, name in ipairs(to_unload) do
|
||||||
|
package.loaded[name] = nil
|
||||||
|
end
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RefreshModuleByName clears a specific module from Lua's package.loaded table
|
||||||
|
func (r *LuaRunner) RefreshModuleByName(modName string) error {
|
||||||
|
if r.state == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.state.DoString(fmt.Sprintf(`
|
||||||
|
package.loaded["%s"] = nil
|
||||||
|
`, modName))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessModuleChange handles a file change notification from a watcher
|
||||||
|
func (r *LuaRunner) ProcessModuleChange(filePath string) {
|
||||||
|
// Mark cache as needing refresh
|
||||||
|
r.requireCache.MarkNeedsRefresh()
|
||||||
|
|
||||||
|
// Extract module name from file path for package.loaded clearing
|
||||||
|
ext := filepath.Ext(filePath)
|
||||||
|
if ext == ".lua" {
|
||||||
|
// Get relative path from lib directories
|
||||||
|
var modName string
|
||||||
|
for _, libDir := range r.requireCfg.LibDirs {
|
||||||
|
if rel, err := filepath.Rel(libDir, filePath); err == nil && !strings.HasPrefix(rel, "..") {
|
||||||
|
// Convert path to module name format
|
||||||
|
modName = strings.TrimSuffix(rel, ext)
|
||||||
|
modName = strings.ReplaceAll(modName, string(filepath.Separator), ".")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if modName != "" {
|
||||||
|
// Clear from Lua's package.loaded (non-blocking)
|
||||||
|
go r.RefreshModuleByName(modName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
||||||
|
@ -31,17 +32,21 @@ type RequireConfig struct {
|
||||||
|
|
||||||
// RequireCache is a thread-safe cache for loaded Lua modules
|
// RequireCache is a thread-safe cache for loaded Lua modules
|
||||||
type RequireCache struct {
|
type RequireCache struct {
|
||||||
modules sync.Map // Maps full file paths to ModuleEntry
|
modules sync.Map // Maps full file paths to ModuleEntry
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
maxItems int // Maximum number of modules to cache
|
maxItems int // Maximum number of modules to cache
|
||||||
|
lastRefresh time.Time // When we last did a full refresh check
|
||||||
|
needsRefresh atomic.Bool // Flag for watchers to signal refresh needed
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRequireCache creates a new, empty require cache
|
// NewRequireCache creates a new, empty require cache
|
||||||
func NewRequireCache() *RequireCache {
|
func NewRequireCache() *RequireCache {
|
||||||
return &RequireCache{
|
cache := &RequireCache{
|
||||||
modules: sync.Map{},
|
modules: sync.Map{},
|
||||||
maxItems: 100, // Default cache size - can be adjusted based on expected module load
|
maxItems: 100, // Default cache size
|
||||||
|
lastRefresh: time.Now(),
|
||||||
}
|
}
|
||||||
|
return cache
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCacheSize adjusts the maximum cache size
|
// SetCacheSize adjusts the maximum cache size
|
||||||
|
@ -56,13 +61,18 @@ func (c *RequireCache) SetCacheSize(size int) {
|
||||||
// Size returns the approximate number of items in the cache
|
// Size returns the approximate number of items in the cache
|
||||||
func (c *RequireCache) Size() int {
|
func (c *RequireCache) Size() int {
|
||||||
size := 0
|
size := 0
|
||||||
c.modules.Range(func(_, _ interface{}) bool {
|
c.modules.Range(func(_, _ any) bool {
|
||||||
size++
|
size++
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarkNeedsRefresh signals that modules have changed and need refresh
|
||||||
|
func (c *RequireCache) MarkNeedsRefresh() {
|
||||||
|
c.needsRefresh.Store(true)
|
||||||
|
}
|
||||||
|
|
||||||
// Get retrieves a module from the cache, updating its last used time
|
// Get retrieves a module from the cache, updating its last used time
|
||||||
func (c *RequireCache) Get(path string) ([]byte, bool) {
|
func (c *RequireCache) Get(path string) ([]byte, bool) {
|
||||||
value, ok := c.modules.Load(path)
|
value, ok := c.modules.Load(path)
|
||||||
|
@ -118,7 +128,7 @@ func (c *RequireCache) evictOldest() {
|
||||||
first := true
|
first := true
|
||||||
|
|
||||||
// Find oldest entry
|
// Find oldest entry
|
||||||
c.modules.Range(func(key, value interface{}) bool {
|
c.modules.Range(func(key, value any) bool {
|
||||||
// Handle different value types
|
// Handle different value types
|
||||||
var lastUsed time.Time
|
var lastUsed time.Time
|
||||||
|
|
||||||
|
@ -149,6 +159,82 @@ func (c *RequireCache) evictOldest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear empties the entire cache
|
||||||
|
func (c *RequireCache) Clear() {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
// Create a new sync.Map to replace the existing one
|
||||||
|
c.modules = sync.Map{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RefreshModule checks if a specific module needs to be refreshed
|
||||||
|
func (c *RequireCache) RefreshModule(path string) bool {
|
||||||
|
// Get the cached module
|
||||||
|
val, ok := c.modules.Load(path)
|
||||||
|
if !ok {
|
||||||
|
// Not in cache, nothing to refresh
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get file info
|
||||||
|
fileInfo, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
// File no longer exists or can't be accessed, remove from cache
|
||||||
|
c.modules.Delete(path)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the cached module is up-to-date
|
||||||
|
entry, ok := val.(ModuleEntry)
|
||||||
|
if !ok {
|
||||||
|
// Invalid entry, remove it
|
||||||
|
c.modules.Delete(path)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the file has been modified since it was cached
|
||||||
|
if fileInfo.ModTime().After(entry.LastUsed) {
|
||||||
|
// File is newer than the cached version, remove from cache
|
||||||
|
c.modules.Delete(path)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// RefreshAll checks all cached modules and refreshes those that have changed
|
||||||
|
func (c *RequireCache) RefreshAll() int {
|
||||||
|
refreshed := 0
|
||||||
|
|
||||||
|
// No need to refresh if flag isn't set
|
||||||
|
if !c.needsRefresh.Load() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect paths to check
|
||||||
|
var paths []string
|
||||||
|
c.modules.Range(func(key, _ any) bool {
|
||||||
|
if path, ok := key.(string); ok {
|
||||||
|
paths = append(paths, path)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
// Check each path
|
||||||
|
for _, path := range paths {
|
||||||
|
if c.RefreshModule(path) {
|
||||||
|
refreshed++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the needsRefresh flag
|
||||||
|
c.needsRefresh.Store(false)
|
||||||
|
c.lastRefresh = time.Now()
|
||||||
|
|
||||||
|
return refreshed
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateRequirePaths updates the require paths in the config without further allocations or re-registering the loader.
|
// UpdateRequirePaths updates the require paths in the config without further allocations or re-registering the loader.
|
||||||
func UpdateRequirePaths(config *RequireConfig, scriptPath string) {
|
func UpdateRequirePaths(config *RequireConfig, scriptPath string) {
|
||||||
if scriptPath != "" {
|
if scriptPath != "" {
|
||||||
|
@ -201,9 +287,28 @@ func findAndCompileModule(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if already in cache - using our Get method to update LRU info
|
// Check if already in cache
|
||||||
if bytecode, ok := cache.Get(cleanPath); ok {
|
if value, ok := cache.modules.Load(cleanPath); ok {
|
||||||
return bytecode, nil
|
entry, ok := value.(ModuleEntry)
|
||||||
|
if !ok {
|
||||||
|
// Legacy format, use it anyway
|
||||||
|
return value.([]byte), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only do refresh check if marked as needed (by watcher)
|
||||||
|
if cache.needsRefresh.Load() {
|
||||||
|
fileInfo, err := os.Stat(cleanPath)
|
||||||
|
// Remove from cache if file changed or doesn't exist
|
||||||
|
if err != nil || (entry.LastUsed.Before(fileInfo.ModTime())) {
|
||||||
|
cache.modules.Delete(cleanPath)
|
||||||
|
// Continue to recompile
|
||||||
|
} else {
|
||||||
|
return entry.Bytecode, nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No refresh needed, use cached bytecode
|
||||||
|
return entry.Bytecode, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if file exists
|
// Check if file exists
|
||||||
|
@ -224,8 +329,11 @@ func findAndCompileModule(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store in cache - using our Store method with LRU eviction
|
// Store in cache with current time
|
||||||
cache.Store(cleanPath, bytecode)
|
cache.modules.Store(cleanPath, ModuleEntry{
|
||||||
|
Bytecode: bytecode,
|
||||||
|
LastUsed: time.Now(),
|
||||||
|
})
|
||||||
|
|
||||||
return bytecode, nil
|
return bytecode, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,7 +137,7 @@ func (s *Sandbox) Setup(state *luajit.State) error {
|
||||||
|
|
||||||
-- Function to run code in sandbox
|
-- Function to run code in sandbox
|
||||||
function __run_sandboxed(bytecode, ctx)
|
function __run_sandboxed(bytecode, ctx)
|
||||||
-- Create environment for this request
|
-- Create fresh environment for this request
|
||||||
local env = __create_sandbox_env(ctx)
|
local env = __create_sandbox_env(ctx)
|
||||||
|
|
||||||
-- Set environment and execute
|
-- Set environment and execute
|
||||||
|
@ -176,7 +176,7 @@ func (s *Sandbox) registerModules(state *luajit.State) error {
|
||||||
|
|
||||||
// Execute runs bytecode in the sandbox
|
// Execute runs bytecode in the sandbox
|
||||||
func (s *Sandbox) Execute(state *luajit.State, bytecode []byte, ctx map[string]any) (any, error) {
|
func (s *Sandbox) Execute(state *luajit.State, bytecode []byte, ctx map[string]any) (any, error) {
|
||||||
// Update modules if needed
|
// Update custom modules if needed
|
||||||
if !s.initialized {
|
if !s.initialized {
|
||||||
if err := s.registerModules(state); err != nil {
|
if err := s.registerModules(state); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
48
core/watchers/modulewatcher.go
Normal file
48
core/watchers/modulewatcher.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package watchers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.sharkk.net/Sky/Moonshark/core/logger"
|
||||||
|
"git.sharkk.net/Sky/Moonshark/core/runner"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WatchLuaModules sets up an optimized watcher for Lua module directories
|
||||||
|
func WatchLuaModules(luaRunner *runner.LuaRunner, libDirs []string, log *logger.Logger) ([]*Watcher, error) {
|
||||||
|
watchers := make([]*Watcher, 0, len(libDirs))
|
||||||
|
|
||||||
|
for _, dir := range libDirs {
|
||||||
|
// Create a directory-specific callback that only signals changes
|
||||||
|
// without doing heavy processing in the callback itself
|
||||||
|
dirCopy := dir // Capture for closure
|
||||||
|
|
||||||
|
callback := func() error {
|
||||||
|
log.Debug("Detected changes in Lua module directory: %s", dirCopy)
|
||||||
|
// Only mark that refresh is needed instead of doing refresh now
|
||||||
|
luaRunner.RequireCache().MarkNeedsRefresh()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
config := WatcherConfig{
|
||||||
|
Dir: dir,
|
||||||
|
Callback: callback,
|
||||||
|
Log: log,
|
||||||
|
Recursive: true,
|
||||||
|
Adaptive: true,
|
||||||
|
// Use a longer debounce time to avoid too frequent updates
|
||||||
|
DebounceTime: defaultDebounceTime * 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
watcher, err := WatchDirectory(config)
|
||||||
|
if err != nil {
|
||||||
|
// Close any watchers we've already created
|
||||||
|
for _, w := range watchers {
|
||||||
|
w.Close()
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
watchers = append(watchers, watcher)
|
||||||
|
log.Info("Started watching Lua modules directory: %s", dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
return watchers, nil
|
||||||
|
}
|
|
@ -267,6 +267,7 @@ func (w *Watcher) logWarning(format string, args ...any) {
|
||||||
func (w *Watcher) logError(format string, args ...any) {
|
func (w *Watcher) logError(format string, args ...any) {
|
||||||
w.log.Error("[Watcher] [%s] %s", w.dir, fmt.Sprintf(format, args...))
|
w.log.Error("[Watcher] [%s] %s", w.dir, fmt.Sprintf(format, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Watcher) checkForChanges() (bool, error) {
|
func (w *Watcher) checkForChanges() (bool, error) {
|
||||||
// Get current state
|
// Get current state
|
||||||
currentFiles := make(map[string]FileInfo)
|
currentFiles := make(map[string]FileInfo)
|
||||||
|
@ -307,36 +308,43 @@ func (w *Watcher) checkForChanges() (bool, error) {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for modified, added, or removed files
|
// Check for modified files
|
||||||
|
var changed bool
|
||||||
for path, currentInfo := range currentFiles {
|
for path, currentInfo := range currentFiles {
|
||||||
prevInfo, exists := previousFiles[path]
|
prevInfo, exists := previousFiles[path]
|
||||||
if !exists {
|
if !exists {
|
||||||
// New file
|
// New file
|
||||||
w.logDebug("New file detected: %s", path)
|
w.logDebug("New file detected: %s", path)
|
||||||
w.updateFiles(currentFiles)
|
changed = true
|
||||||
return true, nil
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if currentInfo.ModTime != prevInfo.ModTime || currentInfo.Size != prevInfo.Size {
|
if currentInfo.ModTime != prevInfo.ModTime || currentInfo.Size != prevInfo.Size {
|
||||||
// File modified
|
// File modified
|
||||||
w.logDebug("File modified: %s", path)
|
w.logDebug("File modified: %s", path)
|
||||||
w.updateFiles(currentFiles)
|
changed = true
|
||||||
return true, nil
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for deleted files
|
// Check for deleted files
|
||||||
for path := range previousFiles {
|
if !changed {
|
||||||
if _, exists := currentFiles[path]; !exists {
|
for path := range previousFiles {
|
||||||
// File deleted
|
if _, exists := currentFiles[path]; !exists {
|
||||||
w.logDebug("File deleted: %s", path)
|
// File deleted
|
||||||
w.updateFiles(currentFiles)
|
w.logDebug("File deleted: %s", path)
|
||||||
return true, nil
|
changed = true
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// No changes detected
|
// Update internal files state if there were changes
|
||||||
return false, nil
|
if changed {
|
||||||
|
w.updateFiles(currentFiles)
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateFiles updates the internal file list
|
// updateFiles updates the internal file list
|
||||||
|
|
115
moonshark.go
115
moonshark.go
|
@ -45,6 +45,48 @@ func initRouters(routesDir, staticDir string, log *logger.Logger) (*routers.LuaR
|
||||||
return luaRouter, staticRouter, nil
|
return luaRouter, staticRouter, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setupWatchers initializes and starts all file watchers
|
||||||
|
func setupWatchers(luaRouter *routers.LuaRouter, staticRouter *routers.StaticRouter,
|
||||||
|
luaRunner *runner.LuaRunner, routesDir string, staticDir string,
|
||||||
|
libDirs []string, log *logger.Logger) ([]func() error, error) {
|
||||||
|
|
||||||
|
var cleanupFuncs []func() error
|
||||||
|
|
||||||
|
// Set up watcher for Lua routes
|
||||||
|
luaRouterWatcher, err := watchers.WatchLuaRouter(luaRouter, routesDir, log)
|
||||||
|
if err != nil {
|
||||||
|
log.Warning("Failed to watch routes directory: %v", err)
|
||||||
|
} else {
|
||||||
|
cleanupFuncs = append(cleanupFuncs, luaRouterWatcher.Close)
|
||||||
|
log.Info("File watcher active for Lua routes")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up watcher for static files
|
||||||
|
staticWatcher, err := watchers.WatchStaticRouter(staticRouter, staticDir, log)
|
||||||
|
if err != nil {
|
||||||
|
log.Warning("Failed to watch static directory: %v", err)
|
||||||
|
} else {
|
||||||
|
cleanupFuncs = append(cleanupFuncs, staticWatcher.Close)
|
||||||
|
log.Info("File watcher active for static files")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up watchers for Lua modules libraries
|
||||||
|
if len(libDirs) > 0 {
|
||||||
|
moduleWatchers, err := watchers.WatchLuaModules(luaRunner, libDirs, log)
|
||||||
|
if err != nil {
|
||||||
|
log.Warning("Failed to watch Lua module directories: %v", err)
|
||||||
|
} else {
|
||||||
|
for _, watcher := range moduleWatchers {
|
||||||
|
w := watcher // Capture variable for closure
|
||||||
|
cleanupFuncs = append(cleanupFuncs, w.Close)
|
||||||
|
}
|
||||||
|
log.Info("File watchers active for %d Lua module directories", len(moduleWatchers))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleanupFuncs, nil
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Initialize logger
|
// Initialize logger
|
||||||
log := logger.New(logger.LevelDebug, true)
|
log := logger.New(logger.LevelDebug, true)
|
||||||
|
@ -59,6 +101,7 @@ func main() {
|
||||||
cfg = config.New()
|
cfg = config.New()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set log level from config
|
||||||
switch cfg.GetString("log_level", "info") {
|
switch cfg.GetString("log_level", "info") {
|
||||||
case "debug":
|
case "debug":
|
||||||
log.SetLevel(logger.LevelDebug)
|
log.SetLevel(logger.LevelDebug)
|
||||||
|
@ -74,52 +117,66 @@ func main() {
|
||||||
log.SetLevel(logger.LevelInfo)
|
log.SetLevel(logger.LevelInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get port from config or use default
|
// Get configuration values
|
||||||
port := cfg.GetInt("port", 3117)
|
port := cfg.GetInt("port", 3117)
|
||||||
|
|
||||||
// Initialize routers
|
|
||||||
routesDir := cfg.GetString("routes_dir", "./routes")
|
routesDir := cfg.GetString("routes_dir", "./routes")
|
||||||
staticDir := cfg.GetString("static_dir", "./static")
|
staticDir := cfg.GetString("static_dir", "./static")
|
||||||
|
bufferSize := cfg.GetInt("buffer_size", 20)
|
||||||
|
|
||||||
|
// Get library directories
|
||||||
|
var libDirs []string
|
||||||
|
libDirsConfig := cfg.GetStringArray("lib_dirs")
|
||||||
|
if libDirsConfig != nil && len(libDirsConfig) > 0 {
|
||||||
|
libDirs = libDirsConfig
|
||||||
|
} else {
|
||||||
|
// Default lib directory
|
||||||
|
libDirs = []string{"./libs"}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure lib directories exist
|
||||||
|
for _, dir := range libDirs {
|
||||||
|
if err := utils.EnsureDir(dir); err != nil {
|
||||||
|
log.Warning("Lib directory doesn't exist, and could not create it: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize routers
|
||||||
luaRouter, staticRouter, err := initRouters(routesDir, staticDir, log)
|
luaRouter, staticRouter, err := initRouters(routesDir, staticDir, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Router initialization failed: %v", err)
|
log.Fatal("Router initialization failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.GetBool("watchers", true) {
|
|
||||||
// Set up file watchers for automatic reloading
|
|
||||||
luaWatcher, err := watchers.WatchLuaRouter(luaRouter, routesDir, log)
|
|
||||||
if err != nil {
|
|
||||||
log.Warning("Failed to watch routes directory: %v", err)
|
|
||||||
} else {
|
|
||||||
defer luaWatcher.Close()
|
|
||||||
log.Info("File watcher active for Lua routes")
|
|
||||||
}
|
|
||||||
|
|
||||||
staticWatcher, err := watchers.WatchStaticRouter(staticRouter, staticDir, log)
|
|
||||||
if err != nil {
|
|
||||||
log.Warning("Failed to watch static directory: %v", err)
|
|
||||||
} else {
|
|
||||||
defer staticWatcher.Close()
|
|
||||||
log.Info("File watcher active for static files")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get buffer size from config or use default
|
|
||||||
bufferSize := cfg.GetInt("buffer_size", 20)
|
|
||||||
|
|
||||||
// Initialize Lua runner
|
// Initialize Lua runner
|
||||||
runner, err := runner.NewRunner(
|
luaRunner, err := runner.NewRunner(
|
||||||
runner.WithBufferSize(bufferSize),
|
runner.WithBufferSize(bufferSize),
|
||||||
runner.WithLibDirs("./libs"),
|
runner.WithLibDirs(libDirs...),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to initialize Lua runner: %v", err)
|
log.Fatal("Failed to initialize Lua runner: %v", err)
|
||||||
}
|
}
|
||||||
log.Server("Lua runner initialized with buffer size %d", bufferSize)
|
log.Server("Lua runner initialized with buffer size %d", bufferSize)
|
||||||
defer runner.Close()
|
defer luaRunner.Close()
|
||||||
|
|
||||||
|
// Set up file watchers if enabled
|
||||||
|
var cleanupFuncs []func() error
|
||||||
|
if cfg.GetBool("watchers", true) {
|
||||||
|
cleanupFuncs, err = setupWatchers(luaRouter, staticRouter, luaRunner, routesDir, staticDir, libDirs, log)
|
||||||
|
if err != nil {
|
||||||
|
log.Warning("Error setting up watchers: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register cleanup functions
|
||||||
|
defer func() {
|
||||||
|
for _, cleanup := range cleanupFuncs {
|
||||||
|
if err := cleanup(); err != nil {
|
||||||
|
log.Warning("Cleanup error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// Create HTTP server
|
// Create HTTP server
|
||||||
server := http.New(luaRouter, staticRouter, runner, log)
|
server := http.New(luaRouter, staticRouter, luaRunner, log)
|
||||||
|
|
||||||
// Handle graceful shutdown
|
// Handle graceful shutdown
|
||||||
stop := make(chan os.Signal, 1)
|
stop := make(chan os.Signal, 1)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user