error page upgrades
This commit is contained in:
parent
be57476a8e
commit
5ab4fd7456
|
@ -98,7 +98,28 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// Try Lua routes first
|
// Try Lua routes first
|
||||||
params := &routers.Params{}
|
params := &routers.Params{}
|
||||||
if bytecode, scriptPath, found := s.luaRouter.GetBytecode(r.Method, r.URL.Path, params); found {
|
bytecode, scriptPath, found := s.luaRouter.GetBytecode(r.Method, r.URL.Path, params)
|
||||||
|
|
||||||
|
// Check if we found a route but it has no valid bytecode (compile error)
|
||||||
|
if found && (bytecode == nil || len(bytecode) == 0) {
|
||||||
|
// Get the actual error from the router - this requires exposing the actual error
|
||||||
|
// from the node in the GetBytecode method
|
||||||
|
errorMsg := "Route exists but failed to compile. Check server logs for details."
|
||||||
|
|
||||||
|
// Get the actual node to access its error
|
||||||
|
if node, _ := s.luaRouter.GetNodeWithError(r.Method, r.URL.Path, params); node != nil && node.Error != nil {
|
||||||
|
errorMsg = node.Error.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logger.Error("%s %s - %s", r.Method, r.URL.Path, errorMsg)
|
||||||
|
|
||||||
|
// Show error page with the actual error message
|
||||||
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
errorHTML := utils.InternalErrorPage(s.errorConfig, r.URL.Path, errorMsg)
|
||||||
|
w.Write([]byte(errorHTML))
|
||||||
|
return
|
||||||
|
} else if found {
|
||||||
s.logger.Debug("Found Lua route match for %s %s with %d params", r.Method, r.URL.Path, params.Count)
|
s.logger.Debug("Found Lua route match for %s %s with %d params", r.Method, r.URL.Path, params.Count)
|
||||||
s.handleLuaRoute(w, r, bytecode, scriptPath, params)
|
s.handleLuaRoute(w, r, bytecode, scriptPath, params)
|
||||||
return
|
return
|
||||||
|
|
26
core/routers/errors.go
Normal file
26
core/routers/errors.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package routers
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
// Common errors
|
||||||
|
var (
|
||||||
|
// ErrRoutesCompilationErrors indicates that some routes failed to compile
|
||||||
|
// but the router is still operational
|
||||||
|
ErrRoutesCompilationErrors = errors.New("some routes failed to compile")
|
||||||
|
)
|
||||||
|
|
||||||
|
// RouteError represents an error with a specific route
|
||||||
|
type RouteError struct {
|
||||||
|
Path string // The URL path
|
||||||
|
Method string // HTTP method
|
||||||
|
ScriptPath string // Path to the Lua script
|
||||||
|
Err error // The actual error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns the error message
|
||||||
|
func (re *RouteError) Error() string {
|
||||||
|
if re.Err == nil {
|
||||||
|
return "unknown route error"
|
||||||
|
}
|
||||||
|
return re.Err.Error()
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ const maxParams = 20
|
||||||
type LuaRouter struct {
|
type LuaRouter struct {
|
||||||
routesDir string // Root directory containing route files
|
routesDir string // Root directory containing route files
|
||||||
routes map[string]*node // Method -> route tree
|
routes map[string]*node // Method -> route tree
|
||||||
|
failedRoutes map[string]*RouteError // Track failed routes
|
||||||
mu sync.RWMutex // Lock for concurrent access to routes
|
mu sync.RWMutex // Lock for concurrent access to routes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +28,7 @@ type node struct {
|
||||||
paramName string // Parameter name (if this is a parameter node)
|
paramName string // Parameter name (if this is a parameter node)
|
||||||
staticChild map[string]*node // Static children by segment name
|
staticChild map[string]*node // Static children by segment name
|
||||||
paramChild *node // Parameter/wildcard child
|
paramChild *node // Parameter/wildcard child
|
||||||
|
err error // Compilation error if any
|
||||||
}
|
}
|
||||||
|
|
||||||
// Params holds URL parameters with fixed-size arrays to avoid allocations
|
// Params holds URL parameters with fixed-size arrays to avoid allocations
|
||||||
|
@ -60,6 +62,7 @@ func NewLuaRouter(routesDir string) (*LuaRouter, error) {
|
||||||
r := &LuaRouter{
|
r := &LuaRouter{
|
||||||
routesDir: routesDir,
|
routesDir: routesDir,
|
||||||
routes: make(map[string]*node),
|
routes: make(map[string]*node),
|
||||||
|
failedRoutes: make(map[string]*RouteError),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize method trees
|
// Initialize method trees
|
||||||
|
@ -71,15 +74,22 @@ func NewLuaRouter(routesDir string) (*LuaRouter, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build routes
|
// Build routes
|
||||||
if err := r.buildRoutes(); err != nil {
|
err = r.buildRoutes()
|
||||||
return nil, err
|
|
||||||
|
// If some routes failed to compile, return the router with a warning error
|
||||||
|
// This allows the server to continue running with the routes that did compile
|
||||||
|
if len(r.failedRoutes) > 0 {
|
||||||
|
return r, ErrRoutesCompilationErrors
|
||||||
}
|
}
|
||||||
|
|
||||||
return r, nil
|
return r, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildRoutes scans the routes directory and builds the routing tree
|
// buildRoutes scans the routes directory and builds the routing tree
|
||||||
func (r *LuaRouter) buildRoutes() error {
|
func (r *LuaRouter) buildRoutes() error {
|
||||||
|
// Clear failed routes map
|
||||||
|
r.failedRoutes = make(map[string]*RouteError)
|
||||||
|
|
||||||
return filepath.Walk(r.routesDir, func(path string, info os.FileInfo, err error) error {
|
return filepath.Walk(r.routesDir, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -116,8 +126,9 @@ func (r *LuaRouter) buildRoutes() error {
|
||||||
urlPath = "/" + strings.ReplaceAll(relDir, "\\", "/")
|
urlPath = "/" + strings.ReplaceAll(relDir, "\\", "/")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add route to tree
|
// Add route to tree - continue even if there are errors
|
||||||
return r.addRoute(root, urlPath, path)
|
r.addRoute(root, urlPath, path)
|
||||||
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,13 +163,24 @@ func (r *LuaRouter) addRoute(root *node, urlPath, handlerPath string) error {
|
||||||
current.handler = handlerPath
|
current.handler = handlerPath
|
||||||
|
|
||||||
// Compile Lua file to bytecode
|
// Compile Lua file to bytecode
|
||||||
if err := r.compileHandler(current); err != nil {
|
if err := r.compileHandler(current, urlPath); err != nil {
|
||||||
return err
|
// Track the failure but don't fail the entire process
|
||||||
|
routeKey := getRouteKey(urlPath, handlerPath)
|
||||||
|
r.failedRoutes[routeKey] = &RouteError{
|
||||||
|
Path: urlPath,
|
||||||
|
ScriptPath: handlerPath,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getRouteKey generates a unique key for a route
|
||||||
|
func getRouteKey(path, handler string) string {
|
||||||
|
return path + ":" + handler
|
||||||
|
}
|
||||||
|
|
||||||
// Match finds a handler for the given method and path
|
// Match finds a handler for the given method and path
|
||||||
// Uses the pre-allocated params struct to avoid allocations
|
// Uses the pre-allocated params struct to avoid allocations
|
||||||
func (r *LuaRouter) Match(method, path string, params *Params) (*node, bool) {
|
func (r *LuaRouter) Match(method, path string, params *Params) (*node, bool) {
|
||||||
|
@ -222,7 +244,7 @@ func (r *LuaRouter) matchPath(current *node, segments []string, params *Params,
|
||||||
}
|
}
|
||||||
|
|
||||||
// compileHandler compiles a Lua file to bytecode
|
// compileHandler compiles a Lua file to bytecode
|
||||||
func (r *LuaRouter) compileHandler(n *node) error {
|
func (r *LuaRouter) compileHandler(n *node, urlPath string) error {
|
||||||
if n.handler == "" {
|
if n.handler == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -230,32 +252,44 @@ func (r *LuaRouter) compileHandler(n *node) error {
|
||||||
// Read the Lua file
|
// Read the Lua file
|
||||||
content, err := os.ReadFile(n.handler)
|
content, err := os.ReadFile(n.handler)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
n.err = err // Store the error in the node
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile to bytecode
|
// Compile to bytecode
|
||||||
state := luajit.New()
|
state := luajit.New()
|
||||||
if state == nil {
|
if state == nil {
|
||||||
return errors.New("failed to create Lua state")
|
compileErr := errors.New("failed to create Lua state")
|
||||||
|
n.err = compileErr // Store the error in the node
|
||||||
|
return compileErr
|
||||||
}
|
}
|
||||||
defer state.Close()
|
defer state.Close()
|
||||||
|
|
||||||
bytecode, err := state.CompileBytecode(string(content), n.handler)
|
bytecode, err := state.CompileBytecode(string(content), n.handler)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
n.err = err // Store the error in the node
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store bytecode in the node
|
// Store bytecode in the node
|
||||||
n.bytecode = bytecode
|
n.bytecode = bytecode
|
||||||
|
n.err = nil // Clear any previous error
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBytecode returns the compiled bytecode for a matched route
|
// GetBytecode returns the compiled bytecode for a matched route
|
||||||
|
// If a route exists but failed to compile, returns nil bytecode with found=true
|
||||||
func (r *LuaRouter) GetBytecode(method, path string, params *Params) ([]byte, string, bool) {
|
func (r *LuaRouter) GetBytecode(method, path string, params *Params) ([]byte, string, bool) {
|
||||||
node, found := r.Match(method, path, params)
|
node, found := r.Match(method, path, params)
|
||||||
if !found {
|
if !found {
|
||||||
return nil, "", false
|
return nil, "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the route exists but has a compilation error
|
||||||
|
if node.err != nil {
|
||||||
|
return nil, node.handler, true
|
||||||
|
}
|
||||||
|
|
||||||
return node.bytecode, node.handler, true
|
return node.bytecode, node.handler, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,6 +305,47 @@ func (r *LuaRouter) Refresh() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear failed routes
|
||||||
|
r.failedRoutes = make(map[string]*RouteError)
|
||||||
|
|
||||||
// Rebuild routes
|
// Rebuild routes
|
||||||
return r.buildRoutes()
|
err := r.buildRoutes()
|
||||||
|
|
||||||
|
// If some routes failed to compile, return a warning error
|
||||||
|
if len(r.failedRoutes) > 0 {
|
||||||
|
return ErrRoutesCompilationErrors
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReportFailedRoutes returns a list of routes that failed to compile
|
||||||
|
func (r *LuaRouter) ReportFailedRoutes() []*RouteError {
|
||||||
|
r.mu.RLock()
|
||||||
|
defer r.mu.RUnlock()
|
||||||
|
|
||||||
|
result := make([]*RouteError, 0, len(r.failedRoutes))
|
||||||
|
for _, re := range r.failedRoutes {
|
||||||
|
result = append(result, re)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
type NodeWithError struct {
|
||||||
|
ScriptPath string
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNodeWithError returns the node with its error for a given path
|
||||||
|
func (r *LuaRouter) GetNodeWithError(method, path string, params *Params) (*NodeWithError, bool) {
|
||||||
|
node, found := r.Match(method, path, params)
|
||||||
|
if !found {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return &NodeWithError{
|
||||||
|
ScriptPath: node.handler,
|
||||||
|
Error: node.err,
|
||||||
|
}, true
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,11 +89,7 @@ func generateInternalErrorHTML(debugMode bool, url string, errMsg string) string
|
||||||
}
|
}
|
||||||
|
|
||||||
randomMessage := errorMessages[rand.Intn(len(errorMessages))]
|
randomMessage := errorMessages[rand.Intn(len(errorMessages))]
|
||||||
codeContent := url
|
return generateErrorHTML("500", randomMessage, "Internal Server Error", debugMode, errMsg)
|
||||||
if errMsg != "" {
|
|
||||||
codeContent = url + " → " + errMsg
|
|
||||||
}
|
|
||||||
return generateErrorHTML("500", randomMessage, "Internal Server Error", debugMode, codeContent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateNotFoundHTML creates a 404 Not Found error page
|
// generateNotFoundHTML creates a 404 Not Found error page
|
||||||
|
|
15
moonshark.go
15
moonshark.go
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
@ -37,8 +38,22 @@ func initRouters(routesDir, staticDir string, log *logger.Logger) (*routers.LuaR
|
||||||
// Initialize Lua router for dynamic routes
|
// Initialize Lua router for dynamic routes
|
||||||
luaRouter, err := routers.NewLuaRouter(routesDir)
|
luaRouter, err := routers.NewLuaRouter(routesDir)
|
||||||
if err != nil {
|
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
|
||||||
|
log.Warning("Some Lua routes failed to compile. Check logs for details.")
|
||||||
|
|
||||||
|
// Log details about each failed route
|
||||||
|
if failedRoutes := luaRouter.ReportFailedRoutes(); len(failedRoutes) > 0 {
|
||||||
|
for _, re := range failedRoutes {
|
||||||
|
log.Error("Route %s %s failed to compile: %v", re.Method, re.Path, re.Err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// More serious error that prevents router initialization
|
||||||
return nil, nil, fmt.Errorf("failed to initialize Lua router: %v", err)
|
return nil, nil, fmt.Errorf("failed to initialize Lua router: %v", err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
log.Info("Lua router initialized with routes from %s", routesDir)
|
log.Info("Lua router initialized with routes from %s", routesDir)
|
||||||
|
|
||||||
// Initialize static file router
|
// Initialize static file router
|
||||||
|
|
Loading…
Reference in New Issue
Block a user