From a761611ad0895eac2ea537b6f90a0ac4d7ac3d3c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 06:17:14 +0000 Subject: [PATCH 1/2] Initial plan From 4c36af011424999add1718696e309970e89991f1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 06:22:14 +0000 Subject: [PATCH 2/2] Add debug/administration website with Lua terminal and log viewer - web/nginx.conf: OpenResty config with API endpoints for Lua code execution, log retrieval, log clearing, and server info - web/index.html: Single-page debug console UI with Lua terminal (code editor + output) and server log viewer with auto-refresh Co-authored-by: paintdream <7030141+paintdream@users.noreply.github.com> Agent-Logs-Url: https://github.com/paintdream/ngx_lua_cpp/sessions/f44b2ed1-ba42-480f-9e4a-7035bf3c50ab --- web/index.html | 674 +++++++++++++++++++++++++++++++++++++++++++++++++ web/nginx.conf | 249 ++++++++++++++++++ 2 files changed, 923 insertions(+) create mode 100644 web/index.html create mode 100644 web/nginx.conf diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..b01aecb --- /dev/null +++ b/web/index.html @@ -0,0 +1,674 @@ + + + + + +ngx_lua_cpp Debug Console + + + + + +
+
+ + ngx_lua_cpp Debug Console +
+
+ Connecting... + +
+
+ + +
+ +
+
+

▶ Lua Terminal

+
+ + +
+
+
+ +
+
+ Ctrl+Enter to run · Ctrl+L to clear output · Tab to indent +
+
+
+

Output

+ +
+
+ Output will appear here... +
+
+
+ + +
+
+

📜 Server Logs

+
+ + +
+
+
+ + + + +
+ + +
+
+
+
Loading logs...
+
+
+
+ + + + + diff --git a/web/nginx.conf b/web/nginx.conf new file mode 100644 index 0000000..f21a54d --- /dev/null +++ b/web/nginx.conf @@ -0,0 +1,249 @@ +# ngx_lua_cpp Debug/Administration Server +# OpenResty nginx configuration +# +# Usage: +# 1. Adjust the paths below to match your environment: +# - Replace [[ngx_lua_cpp source directory]] with the path to this repository +# - Replace [[ngx_lua_cpp binary directory]] with the path to the built library +# 2. Start OpenResty: +# openresty -p `pwd` -c nginx.conf +# 3. Open http://localhost:8080 in your browser + +worker_processes 1; +error_log logs/error.log info; +pid logs/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include mime.types; + default_type application/octet-stream; + + access_log logs/access.log; + + sendfile on; + keepalive_timeout 65; + + # Shared dictionary for storing execution history + lua_shared_dict exec_history 1m; + + init_worker_by_lua_block { + -- Adjust these paths to your environment + -- package.path = package.path .. ";[[ngx_lua_cpp source directory]]/demo/?.lua" + -- package.cpath = package.cpath .. ";[[ngx_lua_cpp binary directory]]/?.dll;[[ngx_lua_cpp binary directory]]/lib?.so;" + -- require("init_ngx_lua_cpp") + } + + server { + listen 8080; + server_name localhost; + + # Serve static files (index.html, etc.) + location / { + root html; + index index.html; + } + + # API: Execute Lua code + location /api/exec { + default_type application/json; + + content_by_lua_block { + ngx.req.read_body() + local body = ngx.req.get_body_data() + if not body then + ngx.status = 400 + ngx.say('{"ok":false,"error":"Empty request body"}') + return + end + + local cjson = require("cjson.safe") + local data = cjson.decode(body) + if not data or not data.code then + ngx.status = 400 + ngx.say('{"ok":false,"error":"Invalid JSON or missing \\"code\\" field"}') + return + end + + local code = data.code + + -- Capture output by overriding ngx.say/ngx.print within the chunk + local output_parts = {} + local env = setmetatable({ + print = function(...) + local args = {...} + local parts = {} + for i = 1, select("#", ...) do + parts[#parts + 1] = tostring(args[i]) + end + output_parts[#output_parts + 1] = table.concat(parts, "\t") + end, + ngx = ngx, + require = require, + tostring = tostring, + tonumber = tonumber, + type = type, + pairs = pairs, + ipairs = ipairs, + pcall = pcall, + xpcall = xpcall, + error = error, + assert = assert, + select = select, + unpack = unpack, + table = table, + string = string, + math = math, + os = os, + io = io, + coroutine = coroutine, + collectgarbage = collectgarbage, + rawget = rawget, + rawset = rawset, + setmetatable = setmetatable, + getmetatable = getmetatable, + }, {__index = _G}) + + local fn, err = loadstring(code) + if not fn then + ngx.say(cjson.encode({ok = false, error = "Compile error: " .. err})) + return + end + + setfenv(fn, env) + + local ok, result = pcall(fn) + local output = table.concat(output_parts, "\n") + + if ok then + local res = {ok = true, output = output} + if result ~= nil then + res.result = tostring(result) + end + ngx.say(cjson.encode(res)) + else + ngx.say(cjson.encode({ok = false, error = tostring(result), output = output})) + end + } + } + + # API: Retrieve server logs + location /api/logs { + default_type application/json; + + content_by_lua_block { + local cjson = require("cjson.safe") + local args = ngx.req.get_uri_args() + local log_type = args.type or "error" + local lines = tonumber(args.lines) or 100 + + local log_file + if log_type == "access" then + log_file = ngx.config.prefix() .. "logs/access.log" + else + log_file = ngx.config.prefix() .. "logs/error.log" + end + + -- Read log file directly (no shell commands) + local f = io.open(log_file, "r") + if not f then + ngx.say(cjson.encode({ok = false, error = "Failed to open log file"})) + return + end + + -- Read all lines and keep only the last N + local all_lines = {} + for line in f:lines() do + all_lines[#all_lines + 1] = line + end + f:close() + + local start = math.max(1, #all_lines - math.min(lines, 10000) + 1) + local tail_lines = {} + for i = start, #all_lines do + tail_lines[#tail_lines + 1] = all_lines[i] + end + local content = table.concat(tail_lines, "\n") + + ngx.say(cjson.encode({ + ok = true, + log_type = log_type, + file = log_file, + content = content + })) + } + } + + # API: Clear logs + location /api/logs/clear { + default_type application/json; + + content_by_lua_block { + local cjson = require("cjson.safe") + local args = ngx.req.get_uri_args() + local log_type = args.type or "error" + + local log_file + if log_type == "access" then + log_file = ngx.config.prefix() .. "logs/access.log" + else + log_file = ngx.config.prefix() .. "logs/error.log" + end + + local ok, err = pcall(function() + local f = io.open(log_file, "w") + if f then + f:close() + end + -- Signal nginx to reopen log files + local pid_file = ngx.config.prefix() .. "logs/nginx.pid" + local pf = io.open(pid_file, "r") + if pf then + local pid = pf:read("*l") + pf:close() + if pid then + os.execute("kill -USR1 " .. tonumber(pid)) + end + end + end) + + if ok then + ngx.say(cjson.encode({ok = true, message = log_type .. " log cleared"})) + else + ngx.say(cjson.encode({ok = false, error = tostring(err)})) + end + } + } + + # API: Server info + location /api/info { + default_type application/json; + + content_by_lua_block { + local cjson = require("cjson.safe") + + local info = { + ok = true, + ngx_lua_version = ngx.config.ngx_lua_version, + nginx_version = ngx.config.nginx_version, + worker_pid = ngx.worker.pid(), + worker_count = ngx.worker.count(), + prefix = ngx.config.prefix(), + } + + -- Try to load ngx_lua_cpp info + local inst_ok, inst = pcall(require, "init_ngx_lua_cpp") + if inst_ok and inst then + info.ngx_lua_cpp = true + info.ngx_lua_cpp_running = inst:is_running() + else + info.ngx_lua_cpp = false + end + + ngx.say(cjson.encode(info)) + } + } + } +}