-- Simplified sandbox.lua - Direct execution with explicit state passing -- Main execution wrapper - receives script, context, and response object function __execute(script_func, ctx, response) -- Create clean environment local env = setmetatable({}, {__index = _G}) -- 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) local ok, result = pcall(script_func) if not ok then if result == "__EXIT__" then return nil end error(result, 0) end return result end