massive optimizations and fixes
This commit is contained in:
parent
98be4aef25
commit
22c340648b
2
go.mod
2
go.mod
@ -5,7 +5,7 @@ go 1.24.1
|
|||||||
require (
|
require (
|
||||||
git.sharkk.net/Go/LRU v1.0.0
|
git.sharkk.net/Go/LRU v1.0.0
|
||||||
git.sharkk.net/Sharkk/Fin v1.2.0
|
git.sharkk.net/Sharkk/Fin v1.2.0
|
||||||
git.sharkk.net/Sky/LuaJIT-to-Go v0.5.1
|
git.sharkk.net/Sky/LuaJIT-to-Go v0.5.2
|
||||||
github.com/VictoriaMetrics/fastcache v1.12.4
|
github.com/VictoriaMetrics/fastcache v1.12.4
|
||||||
github.com/alexedwards/argon2id v1.0.0
|
github.com/alexedwards/argon2id v1.0.0
|
||||||
github.com/deneonet/benc v1.1.8
|
github.com/deneonet/benc v1.1.8
|
||||||
|
4
go.sum
4
go.sum
@ -2,8 +2,8 @@ git.sharkk.net/Go/LRU v1.0.0 h1:/KqdRVhHldi23aVfQZ4ss6vhCWZqA3vFiQyf1MJPpQc=
|
|||||||
git.sharkk.net/Go/LRU v1.0.0/go.mod h1:8tdTyl85mss9a+KKwo+Wj9gKHOizhfLfpJhz1ltYz50=
|
git.sharkk.net/Go/LRU v1.0.0/go.mod h1:8tdTyl85mss9a+KKwo+Wj9gKHOizhfLfpJhz1ltYz50=
|
||||||
git.sharkk.net/Sharkk/Fin v1.2.0 h1:axhme8vHRYoaB3us7PNfXzXxKOxhpS5BMuNpN8ESe6U=
|
git.sharkk.net/Sharkk/Fin v1.2.0 h1:axhme8vHRYoaB3us7PNfXzXxKOxhpS5BMuNpN8ESe6U=
|
||||||
git.sharkk.net/Sharkk/Fin v1.2.0/go.mod h1:ca0Ej9yCM/vHh1o3YMvBZspme3EtbwoEL2UXN5UPXMo=
|
git.sharkk.net/Sharkk/Fin v1.2.0/go.mod h1:ca0Ej9yCM/vHh1o3YMvBZspme3EtbwoEL2UXN5UPXMo=
|
||||||
git.sharkk.net/Sky/LuaJIT-to-Go v0.5.1 h1:e9rby0xJs8m2SAPv0di/LplDok88UyjcNjKu8S4d1BY=
|
git.sharkk.net/Sky/LuaJIT-to-Go v0.5.2 h1:BgsZPkoqJjQ7Rb+bSs7QQ24+wwLzyc2AALbnpB/n9Kw=
|
||||||
git.sharkk.net/Sky/LuaJIT-to-Go v0.5.1/go.mod h1:HQz+D7AFxOfNbTIogjxP+shEBtz1KKrLlLucU+w07c8=
|
git.sharkk.net/Sky/LuaJIT-to-Go v0.5.2/go.mod h1:HQz+D7AFxOfNbTIogjxP+shEBtz1KKrLlLucU+w07c8=
|
||||||
github.com/VictoriaMetrics/fastcache v1.12.4 h1:2xvmwZBW+9QtHsXggfzAZRs1FZWCsBs8QDg22bMidf0=
|
github.com/VictoriaMetrics/fastcache v1.12.4 h1:2xvmwZBW+9QtHsXggfzAZRs1FZWCsBs8QDg22bMidf0=
|
||||||
github.com/VictoriaMetrics/fastcache v1.12.4/go.mod h1:K+JGPBn0sueFlLjZ8rcVM0cKkWKNElKyQXmw57QOoYI=
|
github.com/VictoriaMetrics/fastcache v1.12.4/go.mod h1:K+JGPBn0sueFlLjZ8rcVM0cKkWKNElKyQXmw57QOoYI=
|
||||||
github.com/alexedwards/argon2id v1.0.0 h1:wJzDx66hqWX7siL/SRUmgz3F8YMrd/nfX/xHHcQQP0w=
|
github.com/alexedwards/argon2id v1.0.0 h1:wJzDx66hqWX7siL/SRUmgz3F8YMrd/nfX/xHHcQQP0w=
|
||||||
|
@ -257,7 +257,7 @@ func (s *Server) handleLuaRoute(ctx *fasthttp.RequestCtx, bytecode []byte, scrip
|
|||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range response.SessionData {
|
for k, v := range response.SessionData {
|
||||||
if v == "__SESSION_DELETE_MARKER__" {
|
if v == "__DELETE__" {
|
||||||
session.Delete(k)
|
session.Delete(k)
|
||||||
} else {
|
} else {
|
||||||
session.Set(k, v)
|
session.Set(k, v)
|
||||||
|
@ -2,44 +2,41 @@
|
|||||||
-- Provides access to persistent environment variables stored in .env file
|
-- Provides access to persistent environment variables stored in .env file
|
||||||
|
|
||||||
-- Get an environment variable with a default value
|
-- Get an environment variable with a default value
|
||||||
-- Returns the value if it exists, default_value otherwise
|
|
||||||
function env_get(key, default_value)
|
function env_get(key, default_value)
|
||||||
if type(key) ~= "string" then
|
if type(key) ~= "string" then
|
||||||
error("env_get: key must be a string")
|
error("env_get: key must be a string")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- First check context for environment variables (no Go call needed)
|
-- Check context for environment variables
|
||||||
if _env and _env[key] ~= nil then
|
if __ctx and __ctx._env and __ctx._env[key] ~= nil then
|
||||||
return _env[key]
|
return __ctx._env[key]
|
||||||
end
|
end
|
||||||
|
|
||||||
return default_value
|
return default_value
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set an environment variable
|
-- Set an environment variable
|
||||||
-- Returns true on success, false on failure
|
|
||||||
function env_set(key, value)
|
function env_set(key, value)
|
||||||
if type(key) ~= "string" then
|
if type(key) ~= "string" then
|
||||||
error("env_set: key must be a string")
|
error("env_set: key must be a string")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Update context immediately for future reads
|
-- Update context immediately for future reads
|
||||||
if not _env then
|
if __ctx then
|
||||||
_env = {}
|
__ctx._env = __ctx._env or {}
|
||||||
|
__ctx._env[key] = value
|
||||||
end
|
end
|
||||||
_env[key] = value
|
|
||||||
|
|
||||||
-- Persist to Go backend
|
-- Persist to Go backend
|
||||||
return __env_set(key, value)
|
return __env_set(key, value)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get all environment variables as a table
|
-- Get all environment variables as a table
|
||||||
-- Returns a table with all key-value pairs
|
|
||||||
function env_get_all()
|
function env_get_all()
|
||||||
-- Return context table directly if available
|
-- Return context table directly if available
|
||||||
if _env then
|
if __ctx and __ctx._env then
|
||||||
local copy = {}
|
local copy = {}
|
||||||
for k, v in pairs(_env) do
|
for k, v in pairs(__ctx._env) do
|
||||||
copy[k] = v
|
copy[k] = v
|
||||||
end
|
end
|
||||||
return copy
|
return copy
|
||||||
@ -50,36 +47,36 @@ function env_get_all()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Check if an environment variable exists
|
-- Check if an environment variable exists
|
||||||
-- Returns true if the variable exists, false otherwise
|
|
||||||
function env_exists(key)
|
function env_exists(key)
|
||||||
if type(key) ~= "string" then
|
if type(key) ~= "string" then
|
||||||
error("env_exists: key must be a string")
|
error("env_exists: key must be a string")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check context first
|
-- Check context first
|
||||||
if _env then
|
if __ctx and __ctx._env then
|
||||||
return _env[key] ~= nil
|
return __ctx._env[key] ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set multiple environment variables from a table
|
-- Set multiple environment variables from a table
|
||||||
-- Returns true on success, false if any setting failed
|
|
||||||
function env_set_many(vars)
|
function env_set_many(vars)
|
||||||
if type(vars) ~= "table" then
|
if type(vars) ~= "table" then
|
||||||
error("env_set_many: vars must be a table")
|
error("env_set_many: vars must be a table")
|
||||||
end
|
end
|
||||||
|
|
||||||
if not _env then
|
if __ctx then
|
||||||
_env = {}
|
__ctx._env = __ctx._env or {}
|
||||||
end
|
end
|
||||||
|
|
||||||
local success = true
|
local success = true
|
||||||
for key, value in pairs(vars) do
|
for key, value in pairs(vars) do
|
||||||
if type(key) == "string" and type(value) == "string" then
|
if type(key) == "string" and type(value) == "string" then
|
||||||
-- Update context
|
-- Update context
|
||||||
_env[key] = value
|
if __ctx and __ctx._env then
|
||||||
|
__ctx._env[key] = value
|
||||||
|
end
|
||||||
-- Persist to Go
|
-- Persist to Go
|
||||||
if not __env_set(key, value) then
|
if not __env_set(key, value) then
|
||||||
success = false
|
success = false
|
||||||
@ -90,4 +87,4 @@ function env_set_many(vars)
|
|||||||
end
|
end
|
||||||
|
|
||||||
return success
|
return success
|
||||||
end
|
end
|
@ -1,244 +1,390 @@
|
|||||||
-- Simplified sandbox.lua - Direct execution with explicit state passing
|
-- Simplified sandbox.lua - Global function approach
|
||||||
|
|
||||||
-- Main execution wrapper - receives script, context, and response object
|
|
||||||
function __execute(script_func, ctx, response)
|
function __execute(script_func, ctx, response)
|
||||||
-- Create clean environment
|
-- Store context and response globally for function access
|
||||||
local env = setmetatable({}, {__index = _G})
|
__ctx = ctx
|
||||||
|
__response = response
|
||||||
-- Direct context and response access
|
|
||||||
env.ctx = ctx
|
|
||||||
env.response = response
|
|
||||||
|
|
||||||
-- Exit sentinel
|
|
||||||
env.exit = function() error("__EXIT__") end
|
|
||||||
|
|
||||||
-- ======================================================================
|
|
||||||
-- HTTP FUNCTIONS - Modify response directly
|
|
||||||
-- ======================================================================
|
|
||||||
|
|
||||||
env.http_set_status = function(code)
|
|
||||||
if type(code) ~= "number" then
|
|
||||||
error("http_set_status: status code must be a number", 2)
|
|
||||||
end
|
|
||||||
response.status = code
|
|
||||||
end
|
|
||||||
|
|
||||||
env.http_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
|
|
||||||
response.headers = response.headers or {}
|
|
||||||
response.headers[name] = value
|
|
||||||
end
|
|
||||||
|
|
||||||
env.http_set_content_type = function(ct)
|
|
||||||
env.http_set_header("Content-Type", ct)
|
|
||||||
end
|
|
||||||
|
|
||||||
env.http_set_metadata = function(key, value)
|
|
||||||
if type(key) ~= "string" then
|
|
||||||
error("http_set_metadata: key must be a string", 2)
|
|
||||||
end
|
|
||||||
response.metadata = response.metadata or {}
|
|
||||||
response.metadata[key] = value
|
|
||||||
end
|
|
||||||
|
|
||||||
env.http_redirect = function(url, status)
|
|
||||||
if type(url) ~= "string" then
|
|
||||||
error("http_redirect: url must be a string", 2)
|
|
||||||
end
|
|
||||||
response.status = status or 302
|
|
||||||
response.headers = response.headers or {}
|
|
||||||
response.headers["Location"] = url
|
|
||||||
env.exit()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- HTTP request functions (use native implementation)
|
|
||||||
env.http_request = __http_request
|
|
||||||
|
|
||||||
local function make_method(method, needs_body)
|
|
||||||
return function(url, body_or_options, options)
|
|
||||||
if needs_body then
|
|
||||||
options = options or {}
|
|
||||||
return env.http_request(method, url, body_or_options, options)
|
|
||||||
else
|
|
||||||
body_or_options = body_or_options or {}
|
|
||||||
return env.http_request(method, url, nil, body_or_options)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
env.http_get = make_method("GET", false)
|
|
||||||
env.http_post = make_method("POST", true)
|
|
||||||
env.http_put = make_method("PUT", true)
|
|
||||||
env.http_patch = make_method("PATCH", true)
|
|
||||||
env.http_delete = make_method("DELETE", false)
|
|
||||||
env.http_head = make_method("HEAD", false)
|
|
||||||
env.http_options = make_method("OPTIONS", false)
|
|
||||||
|
|
||||||
-- ======================================================================
|
|
||||||
-- COOKIE FUNCTIONS - Direct access to ctx and response
|
|
||||||
-- ======================================================================
|
|
||||||
|
|
||||||
env.cookie_set = function(name, value, options)
|
|
||||||
if type(name) ~= "string" then
|
|
||||||
error("cookie_set: name must be a string", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
response.cookies = response.cookies or {}
|
|
||||||
local opts = options or {}
|
|
||||||
|
|
||||||
local cookie = {
|
|
||||||
name = name,
|
|
||||||
value = value or "",
|
|
||||||
path = opts.path or "/",
|
|
||||||
domain = opts.domain,
|
|
||||||
secure = opts.secure ~= false,
|
|
||||||
http_only = opts.http_only ~= false,
|
|
||||||
same_site = opts.same_site or "Lax"
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.expires then
|
|
||||||
if type(opts.expires) == "number" and opts.expires > 0 then
|
|
||||||
cookie.max_age = opts.expires
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(response.cookies, cookie)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
env.cookie_get = function(name)
|
|
||||||
if type(name) ~= "string" then
|
|
||||||
error("cookie_get: name must be a string", 2)
|
|
||||||
end
|
|
||||||
return ctx.cookies and ctx.cookies[name]
|
|
||||||
end
|
|
||||||
|
|
||||||
env.cookie_remove = function(name, path, domain)
|
|
||||||
return env.cookie_set(name, "", {expires = -1, path = path or "/", domain = domain})
|
|
||||||
end
|
|
||||||
|
|
||||||
-- ======================================================================
|
|
||||||
-- SESSION FUNCTIONS - Direct access to ctx and response
|
|
||||||
-- ======================================================================
|
|
||||||
|
|
||||||
env.session_get = function(key)
|
|
||||||
if type(key) ~= "string" then
|
|
||||||
error("session_get: key must be a string", 2)
|
|
||||||
end
|
|
||||||
return ctx.session and ctx.session.data and ctx.session.data[key]
|
|
||||||
end
|
|
||||||
|
|
||||||
env.session_set = function(key, value)
|
|
||||||
if type(key) ~= "string" then
|
|
||||||
error("session_set: key must be a string", 2)
|
|
||||||
end
|
|
||||||
response.session = response.session or {}
|
|
||||||
response.session[key] = value
|
|
||||||
-- Update context if available
|
|
||||||
if ctx.session and ctx.session.data then
|
|
||||||
ctx.session.data[key] = value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
env.session_id = function()
|
|
||||||
return ctx.session and ctx.session.id
|
|
||||||
end
|
|
||||||
|
|
||||||
env.session_get_all = function()
|
|
||||||
if ctx.session and ctx.session.data then
|
|
||||||
local copy = {}
|
|
||||||
for k, v in pairs(ctx.session.data) do
|
|
||||||
copy[k] = v
|
|
||||||
end
|
|
||||||
return copy
|
|
||||||
end
|
|
||||||
return {}
|
|
||||||
end
|
|
||||||
|
|
||||||
env.session_delete = function(key)
|
|
||||||
if type(key) ~= "string" then
|
|
||||||
error("session_delete: key must be a string", 2)
|
|
||||||
end
|
|
||||||
response.session = response.session or {}
|
|
||||||
response.session[key] = "__DELETE__"
|
|
||||||
if ctx.session and ctx.session.data then
|
|
||||||
ctx.session.data[key] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
env.session_clear = function()
|
|
||||||
response.session = {__clear_all = true}
|
|
||||||
if ctx.session and ctx.session.data then
|
|
||||||
for k in pairs(ctx.session.data) do
|
|
||||||
ctx.session.data[k] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- ======================================================================
|
|
||||||
-- CONTENT TYPE HELPERS
|
|
||||||
-- ======================================================================
|
|
||||||
|
|
||||||
env.send_html = function(content)
|
|
||||||
env.http_set_content_type("text/html")
|
|
||||||
return content
|
|
||||||
end
|
|
||||||
|
|
||||||
env.send_json = function(content)
|
|
||||||
env.http_set_content_type("application/json")
|
|
||||||
return content
|
|
||||||
end
|
|
||||||
|
|
||||||
env.send_text = function(content)
|
|
||||||
env.http_set_content_type("text/plain")
|
|
||||||
return content
|
|
||||||
end
|
|
||||||
|
|
||||||
-- ======================================================================
|
|
||||||
-- NATIVE FUNCTIONS (injected by Go)
|
|
||||||
-- ======================================================================
|
|
||||||
|
|
||||||
-- Copy over native functions
|
|
||||||
local natives = {
|
|
||||||
"__password_hash", "__password_verify",
|
|
||||||
"__sqlite_open", "__sqlite_exec", "__sqlite_query", "__sqlite_close",
|
|
||||||
"__fs_read", "__fs_write", "__fs_exists", "__fs_delete",
|
|
||||||
"generate_token", "url_encode", "url_decode",
|
|
||||||
"html_special_chars", "html_entities",
|
|
||||||
"base64_encode", "base64_decode",
|
|
||||||
"json_encode", "json_decode",
|
|
||||||
"sha256", "md5", "hmac",
|
|
||||||
"env_get", "env_set"
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, name in ipairs(natives) do
|
|
||||||
if _G[name] then
|
|
||||||
env[name:gsub("^__", "")] = _G[name]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Template functions
|
|
||||||
env.render = _G.render
|
|
||||||
env.parse = _G.parse
|
|
||||||
env.iparse = _G.iparse
|
|
||||||
|
|
||||||
-- Module support
|
|
||||||
if __setup_require then
|
|
||||||
__setup_require(env)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Set function environment and execute
|
|
||||||
setfenv(script_func, env)
|
|
||||||
|
|
||||||
|
-- Execute script in global environment
|
||||||
local ok, result = pcall(script_func)
|
local ok, result = pcall(script_func)
|
||||||
|
|
||||||
|
-- Clean up
|
||||||
|
__ctx = nil
|
||||||
|
__response = nil
|
||||||
|
|
||||||
if not ok then
|
if not ok then
|
||||||
if result == "__EXIT__" then
|
if result == "__EXIT__" then
|
||||||
return nil
|
return {nil, response}
|
||||||
end
|
end
|
||||||
error(result, 0)
|
error(result, 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
return result
|
return {result, response}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Exit sentinel
|
||||||
|
function exit()
|
||||||
|
error("__EXIT__")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ======================================================================
|
||||||
|
-- HTTP RESPONSE FUNCTIONS
|
||||||
|
-- ======================================================================
|
||||||
|
|
||||||
|
function http_set_status(code)
|
||||||
|
__response.status = code
|
||||||
|
end
|
||||||
|
|
||||||
|
function http_set_header(name, value)
|
||||||
|
__response.headers = __response.headers or {}
|
||||||
|
__response.headers[name] = value
|
||||||
|
end
|
||||||
|
|
||||||
|
function http_set_content_type(ct)
|
||||||
|
__response.headers = __response.headers or {}
|
||||||
|
__response.headers["Content-Type"] = ct
|
||||||
|
end
|
||||||
|
|
||||||
|
function http_set_metadata(key, value)
|
||||||
|
__response.metadata = __response.metadata or {}
|
||||||
|
__response.metadata[key] = value
|
||||||
|
end
|
||||||
|
|
||||||
|
function http_redirect(url, status)
|
||||||
|
__response.status = status or 302
|
||||||
|
__response.headers = __response.headers or {}
|
||||||
|
__response.headers["Location"] = url
|
||||||
|
error("__EXIT__")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ======================================================================
|
||||||
|
-- COOKIE FUNCTIONS
|
||||||
|
-- ======================================================================
|
||||||
|
|
||||||
|
function cookie_set(name, value, options)
|
||||||
|
__response.cookies = __response.cookies or {}
|
||||||
|
local opts = options or {}
|
||||||
|
local cookie = {
|
||||||
|
name = name,
|
||||||
|
value = value or "",
|
||||||
|
path = opts.path or "/",
|
||||||
|
domain = opts.domain,
|
||||||
|
secure = opts.secure ~= false,
|
||||||
|
http_only = opts.http_only ~= false
|
||||||
|
}
|
||||||
|
if opts.expires and opts.expires > 0 then
|
||||||
|
cookie.max_age = opts.expires
|
||||||
|
end
|
||||||
|
table.insert(__response.cookies, cookie)
|
||||||
|
end
|
||||||
|
|
||||||
|
function cookie_get(name)
|
||||||
|
return __ctx._request_cookies and __ctx._request_cookies[name]
|
||||||
|
end
|
||||||
|
|
||||||
|
function cookie_remove(name, path, domain)
|
||||||
|
return cookie_set(name, "", {expires = -1, path = path or "/", domain = domain})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ======================================================================
|
||||||
|
-- SESSION FUNCTIONS
|
||||||
|
-- ======================================================================
|
||||||
|
|
||||||
|
function session_set(key, value)
|
||||||
|
__response.session = __response.session or {}
|
||||||
|
__response.session[key] = value
|
||||||
|
if __ctx.session and __ctx.session.data then
|
||||||
|
__ctx.session.data[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function session_get(key)
|
||||||
|
return __ctx.session and __ctx.session.data and __ctx.session.data[key]
|
||||||
|
end
|
||||||
|
|
||||||
|
function session_id()
|
||||||
|
return __ctx.session and __ctx.session.id
|
||||||
|
end
|
||||||
|
|
||||||
|
function session_get_all()
|
||||||
|
if __ctx.session and __ctx.session.data then
|
||||||
|
local copy = {}
|
||||||
|
for k, v in pairs(__ctx.session.data) do
|
||||||
|
copy[k] = v
|
||||||
|
end
|
||||||
|
return copy
|
||||||
|
end
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function session_delete(key)
|
||||||
|
__response.session = __response.session or {}
|
||||||
|
__response.session[key] = "__DELETE__"
|
||||||
|
if __ctx.session and __ctx.session.data then
|
||||||
|
__ctx.session.data[key] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function session_clear()
|
||||||
|
__response.session = {__clear_all = true}
|
||||||
|
if __ctx.session and __ctx.session.data then
|
||||||
|
for k in pairs(__ctx.session.data) do
|
||||||
|
__ctx.session.data[k] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ======================================================================
|
||||||
|
-- CSRF FUNCTIONS
|
||||||
|
-- ======================================================================
|
||||||
|
|
||||||
|
function csrf_generate()
|
||||||
|
local token = generate_token(32)
|
||||||
|
session_set("_csrf_token", token)
|
||||||
|
return token
|
||||||
|
end
|
||||||
|
|
||||||
|
function csrf_field()
|
||||||
|
local token = session_get("_csrf_token")
|
||||||
|
if not token then
|
||||||
|
token = csrf_generate()
|
||||||
|
end
|
||||||
|
return string.format('<input type="hidden" name="_csrf_token" value="%s" />',
|
||||||
|
html_special_chars(token))
|
||||||
|
end
|
||||||
|
|
||||||
|
function csrf_validate()
|
||||||
|
local token = __ctx.session and __ctx.session.data and __ctx.session.data["_csrf_token"]
|
||||||
|
if not token then
|
||||||
|
__response.status = 403
|
||||||
|
error("__EXIT__")
|
||||||
|
end
|
||||||
|
|
||||||
|
local request_token = (__ctx._request_form and __ctx._request_form._csrf_token) or
|
||||||
|
(__ctx._request_headers and (__ctx._request_headers["x-csrf-token"] or __ctx._request_headers["csrf-token"]))
|
||||||
|
|
||||||
|
if not request_token or request_token ~= token then
|
||||||
|
__response.status = 403
|
||||||
|
error("__EXIT__")
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ======================================================================
|
||||||
|
-- CONTENT TYPE HELPERS
|
||||||
|
-- ======================================================================
|
||||||
|
|
||||||
|
function send_html(content)
|
||||||
|
http_set_content_type("text/html")
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
function send_json(content)
|
||||||
|
http_set_content_type("application/json")
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
function send_text(content)
|
||||||
|
http_set_content_type("text/plain")
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
function send_xml(content)
|
||||||
|
http_set_content_type("application/xml")
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
function send_javascript(content)
|
||||||
|
http_set_content_type("application/javascript")
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
function send_css(content)
|
||||||
|
http_set_content_type("text/css")
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
function send_svg(content)
|
||||||
|
http_set_content_type("image/svg+xml")
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
function send_csv(content)
|
||||||
|
http_set_content_type("text/csv")
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
function send_binary(content, mime_type)
|
||||||
|
http_set_content_type(mime_type or "application/octet-stream")
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ======================================================================
|
||||||
|
-- TEMPLATE RENDER FUNCTIONS
|
||||||
|
-- ======================================================================
|
||||||
|
|
||||||
|
-- Template processing with code execution
|
||||||
|
function render(template_str, env)
|
||||||
|
local function get_line(s, ln)
|
||||||
|
for line in s:gmatch("([^\n]*)\n?") do
|
||||||
|
if ln == 1 then return line end
|
||||||
|
ln = ln - 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function pos_to_line(s, pos)
|
||||||
|
local line = 1
|
||||||
|
for _ in s:sub(1, pos):gmatch("\n") do line = line + 1 end
|
||||||
|
return line
|
||||||
|
end
|
||||||
|
|
||||||
|
local pos, chunks = 1, {}
|
||||||
|
while pos <= #template_str do
|
||||||
|
local unescaped_start = template_str:find("{{{", pos, true)
|
||||||
|
local escaped_start = template_str:find("{{", pos, true)
|
||||||
|
|
||||||
|
local start, tag_type, open_len
|
||||||
|
if unescaped_start and (not escaped_start or unescaped_start <= escaped_start) then
|
||||||
|
start, tag_type, open_len = unescaped_start, "-", 3
|
||||||
|
elseif escaped_start then
|
||||||
|
start, tag_type, open_len = escaped_start, "=", 2
|
||||||
|
else
|
||||||
|
table.insert(chunks, template_str:sub(pos))
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
if start > pos then
|
||||||
|
table.insert(chunks, template_str:sub(pos, start-1))
|
||||||
|
end
|
||||||
|
|
||||||
|
pos = start + open_len
|
||||||
|
local close_tag = tag_type == "-" and "}}}" or "}}"
|
||||||
|
local close_start, close_stop = template_str:find(close_tag, pos, true)
|
||||||
|
if not close_start then
|
||||||
|
error("Failed to find closing tag at position " .. pos)
|
||||||
|
end
|
||||||
|
|
||||||
|
local code = template_str:sub(pos, close_start-1):match("^%s*(.-)%s*$")
|
||||||
|
|
||||||
|
-- Check if it's a simple variable name for escaped output
|
||||||
|
local is_simple_var = tag_type == "=" and code:match("^[%w_]+$")
|
||||||
|
|
||||||
|
table.insert(chunks, {tag_type, code, pos, is_simple_var})
|
||||||
|
pos = close_stop + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local buffer = {"local _tostring, _escape, _b, _b_i = ...\n"}
|
||||||
|
for _, chunk in ipairs(chunks) do
|
||||||
|
local t = type(chunk)
|
||||||
|
if t == "string" then
|
||||||
|
table.insert(buffer, "_b_i = _b_i + 1\n")
|
||||||
|
table.insert(buffer, "_b[_b_i] = " .. string.format("%q", chunk) .. "\n")
|
||||||
|
else
|
||||||
|
t = chunk[1]
|
||||||
|
if t == "=" then
|
||||||
|
if chunk[4] then -- is_simple_var
|
||||||
|
table.insert(buffer, "_b_i = _b_i + 1\n")
|
||||||
|
table.insert(buffer, "--[[" .. chunk[3] .. "]] _b[_b_i] = _escape(_tostring(" .. chunk[2] .. "))\n")
|
||||||
|
else
|
||||||
|
table.insert(buffer, "--[[" .. chunk[3] .. "]] " .. chunk[2] .. "\n")
|
||||||
|
end
|
||||||
|
elseif t == "-" then
|
||||||
|
table.insert(buffer, "_b_i = _b_i + 1\n")
|
||||||
|
table.insert(buffer, "--[[" .. chunk[3] .. "]] _b[_b_i] = _tostring(" .. chunk[2] .. ")\n")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(buffer, "return _b")
|
||||||
|
|
||||||
|
local fn, err = loadstring(table.concat(buffer))
|
||||||
|
if not fn then error(err) end
|
||||||
|
|
||||||
|
env = env or {}
|
||||||
|
local runtime_env = setmetatable({}, {__index = function(_, k) return env[k] or _G[k] end})
|
||||||
|
setfenv(fn, runtime_env)
|
||||||
|
|
||||||
|
local output_buffer = {}
|
||||||
|
fn(tostring, html_special_chars, output_buffer, 0)
|
||||||
|
return table.concat(output_buffer)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Named placeholder processing
|
||||||
|
function parse(template_str, env)
|
||||||
|
local pos, output = 1, {}
|
||||||
|
env = env or {}
|
||||||
|
|
||||||
|
while pos <= #template_str do
|
||||||
|
local unescaped_start, unescaped_end, unescaped_name = template_str:find("{{{%s*([%w_]+)%s*}}}", pos)
|
||||||
|
local escaped_start, escaped_end, escaped_name = template_str:find("{{%s*([%w_]+)%s*}}", pos)
|
||||||
|
|
||||||
|
local next_pos, placeholder_end, name, escaped
|
||||||
|
if unescaped_start and (not escaped_start or unescaped_start <= escaped_start) then
|
||||||
|
next_pos, placeholder_end, name, escaped = unescaped_start, unescaped_end, unescaped_name, false
|
||||||
|
elseif escaped_start then
|
||||||
|
next_pos, placeholder_end, name, escaped = escaped_start, escaped_end, escaped_name, true
|
||||||
|
else
|
||||||
|
local text = template_str:sub(pos)
|
||||||
|
if text and #text > 0 then
|
||||||
|
table.insert(output, text)
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
local text = template_str:sub(pos, next_pos - 1)
|
||||||
|
if text and #text > 0 then
|
||||||
|
table.insert(output, text)
|
||||||
|
end
|
||||||
|
|
||||||
|
local value = env[name]
|
||||||
|
local str = tostring(value or "")
|
||||||
|
if escaped then
|
||||||
|
str = html_special_chars(str)
|
||||||
|
end
|
||||||
|
table.insert(output, str)
|
||||||
|
|
||||||
|
pos = placeholder_end + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
return table.concat(output)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Indexed placeholder processing
|
||||||
|
function iparse(template_str, values)
|
||||||
|
local pos, output, value_index = 1, {}, 1
|
||||||
|
values = values or {}
|
||||||
|
|
||||||
|
while pos <= #template_str do
|
||||||
|
local unescaped_start, unescaped_end = template_str:find("{{{}}}", pos, true)
|
||||||
|
local escaped_start, escaped_end = template_str:find("{{}}", pos, true)
|
||||||
|
|
||||||
|
local next_pos, placeholder_end, escaped
|
||||||
|
if unescaped_start and (not escaped_start or unescaped_start <= escaped_start) then
|
||||||
|
next_pos, placeholder_end, escaped = unescaped_start, unescaped_end, false
|
||||||
|
elseif escaped_start then
|
||||||
|
next_pos, placeholder_end, escaped = escaped_start, escaped_end, true
|
||||||
|
else
|
||||||
|
local text = template_str:sub(pos)
|
||||||
|
if text and #text > 0 then
|
||||||
|
table.insert(output, text)
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
local text = template_str:sub(pos, next_pos - 1)
|
||||||
|
if text and #text > 0 then
|
||||||
|
table.insert(output, text)
|
||||||
|
end
|
||||||
|
|
||||||
|
local value = values[value_index]
|
||||||
|
local str = tostring(value or "")
|
||||||
|
if escaped then
|
||||||
|
str = html_special_chars(str)
|
||||||
|
end
|
||||||
|
table.insert(output, str)
|
||||||
|
|
||||||
|
pos = placeholder_end + 1
|
||||||
|
value_index = value_index + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
return table.concat(output)
|
||||||
end
|
end
|
@ -100,7 +100,7 @@ func httpRequest(state *luajit.State) int {
|
|||||||
req.SetBodyString(bodyStr)
|
req.SetBodyString(bodyStr)
|
||||||
} else if state.IsTable(3) {
|
} else if state.IsTable(3) {
|
||||||
// Table body - convert to JSON
|
// Table body - convert to JSON
|
||||||
luaTable, err := state.SafeToTable(3)
|
luaTable, err := state.ToTable(3)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return state.PushError("Failed to parse body table: %v", err)
|
return state.PushError("Failed to parse body table: %v", err)
|
||||||
}
|
}
|
||||||
@ -123,14 +123,9 @@ func httpRequest(state *luajit.State) int {
|
|||||||
// Process options (headers, timeout, etc.)
|
// Process options (headers, timeout, etc.)
|
||||||
timeout := config.DefaultTimeout
|
timeout := config.DefaultTimeout
|
||||||
if state.GetTop() >= 4 && !state.IsNil(4) && state.IsTable(4) {
|
if state.GetTop() >= 4 && !state.IsNil(4) && state.IsTable(4) {
|
||||||
// Process headers using ForEachTableKV
|
|
||||||
if headers, ok := state.GetFieldTable(4, "headers"); ok {
|
if headers, ok := state.GetFieldTable(4, "headers"); ok {
|
||||||
if headerMap, ok := headers.(map[string]string); ok {
|
if headerMap, ok := headers.(map[string]any); ok {
|
||||||
for name, value := range headerMap {
|
for name, value := range headerMap {
|
||||||
req.Header.Set(name, value)
|
|
||||||
}
|
|
||||||
} else if headerMapAny, ok := headers.(map[string]any); ok {
|
|
||||||
for name, value := range headerMapAny {
|
|
||||||
if valueStr, ok := value.(string); ok {
|
if valueStr, ok := value.(string); ok {
|
||||||
req.Header.Set(name, valueStr)
|
req.Header.Set(name, valueStr)
|
||||||
}
|
}
|
||||||
@ -154,12 +149,8 @@ func httpRequest(state *luajit.State) int {
|
|||||||
if query, ok := state.GetFieldTable(4, "query"); ok {
|
if query, ok := state.GetFieldTable(4, "query"); ok {
|
||||||
args := req.URI().QueryArgs()
|
args := req.URI().QueryArgs()
|
||||||
|
|
||||||
if queryMap, ok := query.(map[string]string); ok {
|
if queryMap, ok := query.(map[string]any); ok {
|
||||||
for name, value := range queryMap {
|
for name, value := range queryMap {
|
||||||
args.Add(name, value)
|
|
||||||
}
|
|
||||||
} else if queryMapAny, ok := query.(map[string]any); ok {
|
|
||||||
for name, value := range queryMapAny {
|
|
||||||
switch v := value.(type) {
|
switch v := value.(type) {
|
||||||
case string:
|
case string:
|
||||||
args.Add(name, v)
|
args.Add(name, v)
|
||||||
|
@ -239,36 +239,29 @@ func sqlExec(state *luajit.State) int {
|
|||||||
|
|
||||||
func setupParams(state *luajit.State, paramIndex int, execOpts *sqlitex.ExecOptions) error {
|
func setupParams(state *luajit.State, paramIndex int, execOpts *sqlitex.ExecOptions) error {
|
||||||
if state.IsTable(paramIndex) {
|
if state.IsTable(paramIndex) {
|
||||||
paramsAny, err := state.SafeToTable(paramIndex)
|
paramsAny, err := state.ToTable(paramIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid parameters: %w", err)
|
return fmt.Errorf("invalid parameters: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch params := paramsAny.(type) {
|
params, ok := paramsAny.(map[string]any)
|
||||||
case map[string]any:
|
if !ok {
|
||||||
if arr, ok := params[""]; ok {
|
return fmt.Errorf("unsupported parameter type: %T", paramsAny)
|
||||||
if arrParams, ok := arr.([]any); ok {
|
}
|
||||||
execOpts.Args = arrParams
|
|
||||||
} else if floatArr, ok := arr.([]float64); ok {
|
|
||||||
args := make([]any, len(floatArr))
|
|
||||||
for i, v := range floatArr {
|
|
||||||
args[i] = v
|
|
||||||
}
|
|
||||||
execOpts.Args = args
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
named := make(map[string]any, len(params))
|
|
||||||
for k, v := range params {
|
|
||||||
if len(k) > 0 && k[0] != ':' {
|
|
||||||
named[":"+k] = v
|
|
||||||
} else {
|
|
||||||
named[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
execOpts.Named = named
|
|
||||||
}
|
|
||||||
|
|
||||||
case map[string]string:
|
// Check for array-style parameters (empty string key indicates array)
|
||||||
|
if arr, ok := params[""]; ok {
|
||||||
|
if arrParams, ok := arr.([]any); ok {
|
||||||
|
execOpts.Args = arrParams
|
||||||
|
} else if floatArr, ok := arr.([]float64); ok {
|
||||||
|
args := make([]any, len(floatArr))
|
||||||
|
for i, v := range floatArr {
|
||||||
|
args[i] = v
|
||||||
|
}
|
||||||
|
execOpts.Args = args
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Named parameters
|
||||||
named := make(map[string]any, len(params))
|
named := make(map[string]any, len(params))
|
||||||
for k, v := range params {
|
for k, v := range params {
|
||||||
if len(k) > 0 && k[0] != ':' {
|
if len(k) > 0 && k[0] != ':' {
|
||||||
@ -278,53 +271,9 @@ func setupParams(state *luajit.State, paramIndex int, execOpts *sqlitex.ExecOpti
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
execOpts.Named = named
|
execOpts.Named = named
|
||||||
|
|
||||||
case map[string]int:
|
|
||||||
named := make(map[string]any, len(params))
|
|
||||||
for k, v := range params {
|
|
||||||
if len(k) > 0 && k[0] != ':' {
|
|
||||||
named[":"+k] = v
|
|
||||||
} else {
|
|
||||||
named[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
execOpts.Named = named
|
|
||||||
|
|
||||||
case map[int]any:
|
|
||||||
named := make(map[string]any, len(params))
|
|
||||||
for k, v := range params {
|
|
||||||
named[fmt.Sprintf(":%d", k)] = v
|
|
||||||
}
|
|
||||||
execOpts.Named = named
|
|
||||||
|
|
||||||
case []any:
|
|
||||||
execOpts.Args = params
|
|
||||||
|
|
||||||
case []string:
|
|
||||||
args := make([]any, len(params))
|
|
||||||
for i, v := range params {
|
|
||||||
args[i] = v
|
|
||||||
}
|
|
||||||
execOpts.Args = args
|
|
||||||
|
|
||||||
case []int:
|
|
||||||
args := make([]any, len(params))
|
|
||||||
for i, v := range params {
|
|
||||||
args[i] = v
|
|
||||||
}
|
|
||||||
execOpts.Args = args
|
|
||||||
|
|
||||||
case []float64:
|
|
||||||
args := make([]any, len(params))
|
|
||||||
for i, v := range params {
|
|
||||||
args[i] = v
|
|
||||||
}
|
|
||||||
execOpts.Args = args
|
|
||||||
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unsupported parameter type: %T", params)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Multiple individual parameters
|
||||||
count := state.GetTop() - 2
|
count := state.GetTop() - 2
|
||||||
args := make([]any, count)
|
args := make([]any, count)
|
||||||
for i := range count {
|
for i := range count {
|
||||||
|
@ -150,7 +150,7 @@ func jsonMarshal(state *luajit.State) int {
|
|||||||
return state.PushError("json marshal: %v", err)
|
return state.PushError("json marshal: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
value, err := state.SafeToTable(1)
|
value, err := state.ToTable(1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Try as generic value if not a table
|
// Try as generic value if not a table
|
||||||
value, err = state.ToValue(1)
|
value, err = state.ToValue(1)
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
package runner
|
package runner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"Moonshark/runner/lualibs"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"maps"
|
||||||
|
|
||||||
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
||||||
"github.com/valyala/fasthttp"
|
"github.com/valyala/fasthttp"
|
||||||
)
|
)
|
||||||
@ -42,7 +45,23 @@ func (s *Sandbox) Setup(state *luajit.State, verbose bool) error {
|
|||||||
|
|
||||||
// Execute runs a Lua script in the sandbox with the given context
|
// Execute runs a Lua script in the sandbox with the given context
|
||||||
func (s *Sandbox) Execute(state *luajit.State, bytecode []byte, ctx *Context) (*Response, error) {
|
func (s *Sandbox) Execute(state *luajit.State, bytecode []byte, ctx *Context) (*Response, error) {
|
||||||
// Create response object in Lua
|
// Load script and executor
|
||||||
|
if err := state.LoadBytecode(bytecode, "script"); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load bytecode: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := state.LoadBytecode(s.executorBytecode, "executor"); err != nil {
|
||||||
|
state.Pop(1)
|
||||||
|
return nil, fmt.Errorf("failed to load executor: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get __execute function
|
||||||
|
if err := state.Call(0, 1); err != nil {
|
||||||
|
state.Pop(1)
|
||||||
|
return nil, fmt.Errorf("failed to get executor: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare response object
|
||||||
response := map[string]any{
|
response := map[string]any{
|
||||||
"status": 200,
|
"status": 200,
|
||||||
"headers": make(map[string]string),
|
"headers": make(map[string]string),
|
||||||
@ -51,52 +70,36 @@ func (s *Sandbox) Execute(state *luajit.State, bytecode []byte, ctx *Context) (*
|
|||||||
"session": make(map[string]any),
|
"session": make(map[string]any),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load script bytecode (pushes function)
|
|
||||||
if err := state.LoadBytecode(bytecode, "script"); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to load bytecode: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load executor (pushes __execute function)
|
|
||||||
if err := state.LoadBytecode(s.executorBytecode, "executor"); err != nil {
|
|
||||||
state.Pop(1) // Remove script function
|
|
||||||
return nil, fmt.Errorf("failed to load executor: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call the loaded executor to get __execute
|
|
||||||
if err := state.Call(0, 1); err != nil {
|
|
||||||
state.Pop(1) // Remove script function
|
|
||||||
return nil, fmt.Errorf("failed to get executor: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stack: [script_func, __execute]
|
|
||||||
state.PushCopy(-2) // Copy script function
|
|
||||||
// Stack: [script_func, __execute, script_func]
|
|
||||||
|
|
||||||
// Push context
|
|
||||||
if err := state.PushValue(ctx.Values); err != nil {
|
|
||||||
state.Pop(3)
|
|
||||||
return nil, fmt.Errorf("failed to push context: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push response object
|
|
||||||
if err := state.PushValue(response); err != nil {
|
|
||||||
state.Pop(4)
|
|
||||||
return nil, fmt.Errorf("failed to push response: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stack: [script_func, __execute, script_func, ctx, response]
|
|
||||||
// Call __execute(script_func, ctx, response)
|
// Call __execute(script_func, ctx, response)
|
||||||
|
state.PushCopy(-2) // script function
|
||||||
|
state.PushValue(ctx.Values)
|
||||||
|
state.PushValue(response)
|
||||||
|
|
||||||
if err := state.Call(3, 1); err != nil {
|
if err := state.Call(3, 1); err != nil {
|
||||||
state.Pop(1) // Clean up
|
state.Pop(1)
|
||||||
return nil, fmt.Errorf("script execution failed: %w", err)
|
return nil, fmt.Errorf("script execution failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the result
|
// Extract result
|
||||||
result, _ := state.ToValue(-1)
|
result, _ := state.ToValue(-1)
|
||||||
state.Pop(2) // Remove result and original script function
|
state.Pop(2) // Clean up
|
||||||
|
|
||||||
// Extract response data directly from the response object we passed
|
var modifiedResponse map[string]any
|
||||||
return s.buildResponse(response, result), nil
|
var scriptResult any
|
||||||
|
|
||||||
|
if arr, ok := result.([]any); ok && len(arr) >= 2 {
|
||||||
|
scriptResult = arr[0]
|
||||||
|
if resp, ok := arr[1].(map[string]any); ok {
|
||||||
|
modifiedResponse = resp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if modifiedResponse == nil {
|
||||||
|
scriptResult = result
|
||||||
|
modifiedResponse = response
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.buildResponse(modifiedResponse, scriptResult), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildResponse converts the Lua response object to a Go Response
|
// buildResponse converts the Lua response object to a Go Response
|
||||||
@ -118,10 +121,6 @@ func (s *Sandbox) buildResponse(luaResp map[string]any, body any) *Response {
|
|||||||
resp.Headers[k] = str
|
resp.Headers[k] = str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if headers, ok := luaResp["headers"].(map[string]string); ok {
|
|
||||||
for k, v := range headers {
|
|
||||||
resp.Headers[k] = v
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract cookies
|
// Extract cookies
|
||||||
@ -161,18 +160,14 @@ func (s *Sandbox) buildResponse(luaResp map[string]any, body any) *Response {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract metadata
|
// Extract metadata - simplified
|
||||||
if metadata, ok := luaResp["metadata"].(map[string]any); ok {
|
if metadata, ok := luaResp["metadata"].(map[string]any); ok {
|
||||||
for k, v := range metadata {
|
maps.Copy(resp.Metadata, metadata)
|
||||||
resp.Metadata[k] = v
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract session data
|
// Extract session data - simplified
|
||||||
if session, ok := luaResp["session"].(map[string]any); ok {
|
if session, ok := luaResp["session"].(map[string]any); ok {
|
||||||
for k, v := range session {
|
maps.Copy(resp.SessionData, session)
|
||||||
resp.SessionData[k] = v
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp
|
return resp
|
||||||
@ -180,7 +175,33 @@ func (s *Sandbox) buildResponse(luaResp map[string]any, body any) *Response {
|
|||||||
|
|
||||||
// registerCoreFunctions registers all built-in functions in the Lua state
|
// registerCoreFunctions registers all built-in functions in the Lua state
|
||||||
func (s *Sandbox) registerCoreFunctions(state *luajit.State) error {
|
func (s *Sandbox) registerCoreFunctions(state *luajit.State) error {
|
||||||
// Register your native functions here
|
if err := lualibs.RegisterCryptoFunctions(state); err != nil {
|
||||||
// This stays the same as your current implementation
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := lualibs.RegisterEnvFunctions(state); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := lualibs.RegisterFSFunctions(state); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := lualibs.RegisterHttpFunctions(state); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := lualibs.RegisterPasswordFunctions(state); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := lualibs.RegisterSQLiteFunctions(state); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := lualibs.RegisterUtilFunctions(state); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user