errorpages
This commit is contained in:
parent
474ed89dda
commit
be57476a8e
135
core/runner/http.go
Normal file
135
core/runner/http.go
Normal 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
227
core/utils/errorpages.go
Normal 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user