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 @@
+
+
+
+
+
+
+
+
+
+
+
+ Ctrl+Enter to run · Ctrl+L to clear output · Tab to indent
+
+
+
+
+ Output will appear here...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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))
+ }
+ }
+ }
+}