fix env, add additional type support, use coroutines for endpoint exec

This commit is contained in:
Sky Johnson 2025-06-04 09:25:49 -05:00
parent ff01a1f0b1
commit 2c0067dfcf
3 changed files with 90 additions and 42 deletions

View File

@ -1,5 +1,4 @@
-- Environment variable module for Moonshark -- env.lua
-- 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
function env_get(key, default_value) function env_get(key, default_value)
@ -8,8 +7,8 @@ function env_get(key, default_value)
end end
-- Check context for environment variables -- Check context for environment variables
if __ctx and __ctx._env and __ctx._env[key] ~= nil then if __ctx and __ctx.env and __ctx.env[key] ~= nil then
return __ctx._env[key] return __ctx.env[key]
end end
return default_value return default_value
@ -23,8 +22,8 @@ function env_set(key, value)
-- Update context immediately for future reads -- Update context immediately for future reads
if __ctx then if __ctx then
__ctx._env = __ctx._env or {} __ctx.env = __ctx.env or {}
__ctx._env[key] = value __ctx.env[key] = value
end end
-- Persist to Go backend -- Persist to Go backend
@ -34,9 +33,9 @@ end
-- Get all environment variables as a table -- Get all environment variables as a table
function env_get_all() function env_get_all()
-- Return context table directly if available -- Return context table directly if available
if __ctx and __ctx._env then if __ctx and __ctx.env then
local copy = {} local copy = {}
for k, v in pairs(__ctx._env) do for k, v in pairs(__ctx.env) do
copy[k] = v copy[k] = v
end end
return copy return copy
@ -53,8 +52,8 @@ function env_exists(key)
end end
-- Check context first -- Check context first
if __ctx and __ctx._env then if __ctx and __ctx.env then
return __ctx._env[key] ~= nil return __ctx.env[key] ~= nil
end end
return false return false
@ -67,22 +66,22 @@ function env_set_many(vars)
end end
if __ctx then if __ctx then
__ctx._env = __ctx._env or {} __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" then
-- Update context -- Update context
if __ctx and __ctx._env then if __ctx and __ctx.env then
__ctx._env[key] = value __ctx.env[key] = value
end 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
end end
else else
error("env_set_many: all keys and values must be strings") error("env_set_many: all keys must be strings")
end end
end end

View File

@ -1,4 +1,4 @@
-- Simplified sandbox.lua - Global function approach -- sandbox.lua
function __execute(script_func, ctx, response) function __execute(script_func, ctx, response)
-- Store context and response globally for function access -- Store context and response globally for function access
@ -6,26 +6,33 @@ function __execute(script_func, ctx, response)
__response = response __response = response
_G.ctx = ctx _G.ctx = ctx
-- Execute script in global environment -- Create a coroutine for script execution to handle early exits
local ok, result = pcall(script_func) local co = coroutine.create(function()
return script_func()
end)
local ok, result = coroutine.resume(co)
-- Clean up -- Clean up
__ctx = nil __ctx = nil
__response = nil __response = nil
if not ok then if not ok then
-- Real error during script execution
error(result, 0)
end
-- Check if exit was requested
if result == "__EXIT__" then if result == "__EXIT__" then
return {nil, response} return {nil, response}
end end
error(result, 0)
end
return {result, response} return {result, response}
end end
-- Exit sentinel -- Exit sentinel using coroutine yield instead of error
function exit() function exit()
error("__EXIT__") coroutine.yield("__EXIT__")
end end
-- ====================================================================== -- ======================================================================
@ -55,7 +62,7 @@ function http_redirect(url, status)
__response.status = status or 302 __response.status = status or 302
__response.headers = __response.headers or {} __response.headers = __response.headers or {}
__response.headers["Location"] = url __response.headers["Location"] = url
error("__EXIT__") coroutine.yield("__EXIT__")
end end
-- ====================================================================== -- ======================================================================
@ -158,7 +165,7 @@ function csrf_validate()
local token = __ctx.session and __ctx.session.data and __ctx.session.data["_csrf_token"] local token = __ctx.session and __ctx.session.data and __ctx.session.data["_csrf_token"]
if not token then if not token then
__response.status = 403 __response.status = 403
error("__EXIT__") coroutine.yield("__EXIT__")
end end
local request_token = (__ctx._request_form and __ctx._request_form._csrf_token) or local request_token = (__ctx._request_form and __ctx._request_form._csrf_token) or
@ -166,7 +173,7 @@ function csrf_validate()
if not request_token or request_token ~= token then if not request_token or request_token ~= token then
__response.status = 403 __response.status = 403
error("__EXIT__") coroutine.yield("__EXIT__")
end end
return true return true
end end

View File

@ -6,6 +6,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
"strconv"
"strings" "strings"
"sync" "sync"
@ -65,6 +66,29 @@ func GetGlobalEnvManager() *EnvManager {
return globalEnvManager return globalEnvManager
} }
// parseValue attempts to parse a string value into the appropriate type
func parseValue(value string) any {
// Try boolean first
if value == "true" {
return true
}
if value == "false" {
return false
}
// Try number
if num, err := strconv.ParseFloat(value, 64); err == nil {
// Check if it's actually an integer
if num == float64(int64(num)) {
return int64(num)
}
return num
}
// Default to string
return value
}
// load reads the .env file and populates the vars map // load reads the .env file and populates the vars map
func (e *EnvManager) load() error { func (e *EnvManager) load() error {
file, err := os.Open(e.envPath) file, err := os.Open(e.envPath)
@ -110,7 +134,7 @@ func (e *EnvManager) load() error {
} }
} }
e.vars[key] = value e.vars[key] = parseValue(value)
} }
return scanner.Err() return scanner.Err()
@ -139,8 +163,7 @@ func (e *EnvManager) Save() error {
sort.Strings(keys) sort.Strings(keys)
// Write header comment // Write header comment
fmt.Fprintln(file, "# Environment variables for Moonshark") fmt.Fprintln(file, "# env variables - generated automatically - you can edit this file")
fmt.Fprintln(file, "# Generated automatically - you can edit this file")
fmt.Fprintln(file) fmt.Fprintln(file)
// Write each variable // Write each variable
@ -152,6 +175,12 @@ func (e *EnvManager) Save() error {
switch v := value.(type) { switch v := value.(type) {
case string: case string:
strValue = v strValue = v
case bool:
strValue = strconv.FormatBool(v)
case int64:
strValue = strconv.FormatInt(v, 10)
case float64:
strValue = strconv.FormatFloat(v, 'g', -1, 64)
case nil: case nil:
continue // Skip nil values continue // Skip nil values
default: default:
@ -255,11 +284,24 @@ func envSet(state *luajit.State) int {
return 1 return 1
} }
value, err := state.SafeToString(2) // Handle different value types from Lua
if err != nil { var value any
switch state.GetType(2) {
case luajit.TypeBoolean:
value = state.ToBoolean(2)
case luajit.TypeNumber:
value = state.ToNumber(2)
case luajit.TypeString:
value = state.ToString(2)
default:
// Try to convert to string as fallback
if str, err := state.SafeToString(2); err == nil {
value = str
} else {
state.PushBoolean(false) state.PushBoolean(false)
return 1 return 1
} }
}
globalEnvManager.Set(key, value) globalEnvManager.Set(key, value)
state.PushBoolean(true) state.PushBoolean(true)