errorpages

This commit is contained in:
Sky Johnson 2025-03-25 12:13:40 -05:00
parent 474ed89dda
commit be57476a8e
2 changed files with 362 additions and 0 deletions

135
core/runner/http.go Normal file
View File

@ -0,0 +1,135 @@
package runner
import (
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
)
// HTTPResponse represents an HTTP response from Lua
type HTTPResponse struct {
Status int `json:"status"`
Headers map[string]string `json:"headers"`
Body any `json:"body"`
}
// NewHTTPResponse creates a default HTTP response
func NewHTTPResponse() *HTTPResponse {
return &HTTPResponse{
Status: 200,
Headers: make(map[string]string),
}
}
// LuaHTTPModule is the pure Lua implementation of the HTTP module
const LuaHTTPModule = `
-- Table to store response data
__http_responses = {}
-- HTTP module implementation
local http = {
-- Set HTTP status code
set_status = function(code)
if type(code) ~= "number" then
error("http.set_status: status code must be a number", 2)
end
local resp = __http_responses[1] or {}
resp.status = code
__http_responses[1] = resp
end,
-- Set HTTP header
set_header = function(name, value)
if type(name) ~= "string" or type(value) ~= "string" then
error("http.set_header: name and value must be strings", 2)
end
local resp = __http_responses[1] or {}
resp.headers = resp.headers or {}
resp.headers[name] = value
__http_responses[1] = resp
end
}
-- Set content type; set_header helper
http.set_content_type = function(content_type)
http.set_header("Content-Type", content_type)
end
-- Install HTTP module
_G.http = http
-- Override sandbox executor to clear HTTP responses
local old_execute_sandbox = __execute_sandbox
__execute_sandbox = function(bytecode, ctx)
-- Clear previous response for this thread
__http_responses[1] = nil
-- Execute the original function
local result = old_execute_sandbox(bytecode, ctx)
-- Return the result unchanged
return result
end
-- Make sure the HTTP module is accessible in sandbox
if __env_system and __env_system.base_env then
__env_system.base_env.http = http
end
`
// HTTPModuleInitFunc returns an initializer function for the HTTP module
func HTTPModuleInitFunc() StateInitFunc {
return func(state *luajit.State) error {
// Initialize pure Lua HTTP module
return state.DoString(LuaHTTPModule)
}
}
// GetHTTPResponse extracts the HTTP response from Lua state
func GetHTTPResponse(state *luajit.State) (*HTTPResponse, bool) {
response := NewHTTPResponse()
// Get response table
state.GetGlobal("__http_responses")
if state.IsNil(-1) {
state.Pop(1)
return response, false
}
// Check for response at thread index
state.PushNumber(1)
state.GetTable(-2)
if state.IsNil(-1) {
state.Pop(2)
return response, false
}
// Get status
state.GetField(-1, "status")
if state.IsNumber(-1) {
response.Status = int(state.ToNumber(-1))
}
state.Pop(1)
// Get headers
state.GetField(-1, "headers")
if state.IsTable(-1) {
// Iterate through headers table
state.PushNil() // Start iteration
for state.Next(-2) {
// Stack has key at -2 and value at -1
if state.IsString(-2) && state.IsString(-1) {
key := state.ToString(-2)
value := state.ToString(-1)
response.Headers[key] = value
}
state.Pop(1) // Pop value, leave key for next iteration
}
}
state.Pop(1)
// Clean up
state.Pop(2) // Pop response table and __http_responses
return response, true
}

227
core/utils/errorpages.go Normal file
View File

@ -0,0 +1,227 @@
package utils
import (
"math/rand"
"os"
"path/filepath"
)
// ErrorPageConfig holds configuration for generating error pages
type ErrorPageConfig struct {
OverrideDir string // Directory where override templates are stored
DebugMode bool // Whether to show debug information
}
// ErrorType represents HTTP error types
type ErrorType int
const (
ErrorTypeNotFound ErrorType = 404
ErrorTypeMethodNotAllowed ErrorType = 405
ErrorTypeInternalError ErrorType = 500
)
// ErrorPage generates an HTML error page based on the error type
// It first checks for an override file, and if not found, generates a default page
func ErrorPage(config ErrorPageConfig, errorType ErrorType, url string, errMsg string) string {
// Check for override file
if config.OverrideDir != "" {
var filename string
switch errorType {
case ErrorTypeNotFound:
filename = "404.html"
case ErrorTypeMethodNotAllowed:
filename = "405.html"
case ErrorTypeInternalError:
filename = "500.html"
}
if filename != "" {
overridePath := filepath.Join(config.OverrideDir, filename)
if content, err := os.ReadFile(overridePath); err == nil {
return string(content)
}
}
}
// No override found, generate default page
switch errorType {
case ErrorTypeNotFound:
return generateNotFoundHTML(url)
case ErrorTypeMethodNotAllowed:
return generateMethodNotAllowedHTML(url)
case ErrorTypeInternalError:
return generateInternalErrorHTML(config.DebugMode, url, errMsg)
default:
// Fallback to internal error
return generateInternalErrorHTML(config.DebugMode, url, errMsg)
}
}
// NotFoundPage generates a 404 Not Found error page
func NotFoundPage(config ErrorPageConfig, url string) string {
return ErrorPage(config, ErrorTypeNotFound, url, "")
}
// MethodNotAllowedPage generates a 405 Method Not Allowed error page
func MethodNotAllowedPage(config ErrorPageConfig, url string) string {
return ErrorPage(config, ErrorTypeMethodNotAllowed, url, "")
}
// InternalErrorPage generates a 500 Internal Server Error page
func InternalErrorPage(config ErrorPageConfig, url string, errMsg string) string {
return ErrorPage(config, ErrorTypeInternalError, url, errMsg)
}
// generateInternalErrorHTML creates a 500 Internal Server Error page
func generateInternalErrorHTML(debugMode bool, url string, errMsg string) string {
errorMessages := []string{
"Oops! Something went wrong",
"Oh no! The server choked",
"Well, this is embarrassing...",
"Houston, we have a problem",
"Gremlins in the system",
"The server is taking a coffee break",
"Moonshark encountered a lunar eclipse",
"Our code monkeys are working on it",
"The server is feeling under the weather",
"500 Brain Not Found",
}
randomMessage := errorMessages[rand.Intn(len(errorMessages))]
codeContent := url
if errMsg != "" {
codeContent = url + " → " + errMsg
}
return generateErrorHTML("500", randomMessage, "Internal Server Error", debugMode, codeContent)
}
// generateNotFoundHTML creates a 404 Not Found error page
func generateNotFoundHTML(url string) string {
errorMessages := []string{
"Nothing to see here",
"This page is on vacation",
"The page is missing in action",
"This page has left the building",
"This page is in another castle",
"Sorry, we can't find that",
"The page you're looking for doesn't exist",
"Lost in space",
"That's a 404",
"Page not found",
}
randomMessage := errorMessages[rand.Intn(len(errorMessages))]
return generateErrorHTML("404", randomMessage, "Page Not Found", false, url)
}
// generateMethodNotAllowedHTML creates a 405 Method Not Allowed error page
func generateMethodNotAllowedHTML(url string) string {
errorMessages := []string{
"That's not how this works",
"Method not allowed",
"Wrong way!",
"This method is not supported",
"You can't do that here",
"Sorry, wrong door",
"That method won't work here",
"Try a different approach",
"Access denied for this method",
"Method mismatch",
}
randomMessage := errorMessages[rand.Intn(len(errorMessages))]
return generateErrorHTML("405", randomMessage, "Method Not Allowed", false, url)
}
// generateErrorHTML creates the common HTML structure for error pages
func generateErrorHTML(errorCode, mainMessage, subMessage string, showDebugInfo bool, codeContent string) string {
errorHTML := `<!doctype html>
<html>
<head>
<title>` + errorCode + `</title>
<style>
:root {
--bg-color: #2d2e2d;
--bg-gradient: linear-gradient(to bottom, #2d2e2d 0%, #000 100%);
--text-color: white;
--code-bg: rgba(255, 255, 255, 0.1);
}
@media (prefers-color-scheme: light) {
:root {
--bg-color: #f5f5f5;
--bg-gradient: linear-gradient(to bottom, #f5f5f5 0%, #ddd 100%);
--text-color: #333;
--code-bg: rgba(0, 0, 0, 0.1);
}
}
body {
font-family: sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
margin: 0;
background-color: var(--bg-color);
color: var(--text-color);
background: var(--bg-gradient);
}
h1 {
font-size: 4rem;
margin: 0;
padding: 0;
}
p {
font-size: 1.5rem;
margin: 0.5rem 0;
padding: 0;
}
.sub-message {
font-size: 1.2rem;
margin-bottom: 1rem;
opacity: 0.8;
}
code {
display: inline-block;
font-size: 1rem;
font-family: monospace;
background-color: var(--code-bg);
padding: 0.25em 0.5em;
border-radius: 0.25em;
margin-top: 1rem;
max-width: 90vw;
overflow-wrap: break-word;
word-break: break-all;
}
</style>
</head>
<body>
<div>
<h1>` + errorCode + `</h1>
<p>` + mainMessage + `</p>
<div class="sub-message">` + subMessage + `</div>`
if codeContent != "" {
errorHTML += `
<code>` + codeContent + `</code>`
}
// Add a note for debug mode
if showDebugInfo {
errorHTML += `
<p style="font-size: 0.9rem; margin-top: 1rem;">
An error occurred while processing your request.<br>
Please check the server logs for details.
</p>`
}
errorHTML += `
</div>
</body>
</html>`
return errorHTML
}