Files
data-sync-service/nginx/nginx.conf
T
K-hermes 9a8914e90e debug: 保存请求体到文件并打印错误位置上下文
改进:
- 保存每次请求体到 /tmp/last_request_body.json
- 解析错误时提取错误位置(character N)
- 打印错误位置前后各 100 字符
- 返回错误位置给前端

方便调试 Unicode 转义问题。
2026-08-15 18:27:59 +08:00

427 lines
17 KiB
Nginx Configuration File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
worker_processes 1;
error_log stderr warn;
pid /tmp/nginx.pid;
events {
worker_connections 1024;
}
http {
include /usr/local/openresty/nginx/conf/mime.types;
default_type application/json;
access_log /dev/stdout;
sendfile on;
keepalive_timeout 65;
client_max_body_size 10M;
# Lua 共享字典用于缓存
lua_shared_dict data_cache 10m;
server {
listen 80;
server_name _;
# CORS 配置
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, X-Password' always;
# OPTIONS 请求
if ($request_method = 'OPTIONS') {
return 204;
}
# 健康检查
location = /health {
access_log off;
return 200 '{"status":"ok"}';
add_header Content-Type application/json;
}
# 登录页面
location = /login {
root /usr/local/openresty/nginx/html;
try_files /login.html =404;
}
# 登录API
location = /api/login {
content_by_lua_block {
local cjson = require "cjson"
ngx.req.read_body()
local body = ngx.req.get_body_data()
local ok, data = pcall(cjson.decode, body)
if ok and data.password == "admin123123123" then
ngx.header["Set-Cookie"] = "auth=verified; Path=/; Max-Age=604800"
ngx.status = 200
ngx.say(cjson.encode({success = true}))
else
ngx.status = 401
ngx.say(cjson.encode({error = "Invalid password"}))
end
}
}
# 静态文件(需要认证)
location / {
access_by_lua_block {
local cookie = ngx.var.http_cookie
if not cookie or not string.match(cookie, "auth=verified") then
return ngx.redirect("/login")
end
}
root /usr/local/openresty/nginx/html;
index index.html;
}
# POST /api/data - 上传/更新数据
location = /api/data {
content_by_lua_block {
local cjson = require "cjson"
local method = ngx.req.get_method()
-- GET 请求:列出所有 tag
if method == "GET" then
-- 读取索引文件
local index_file = io.open("/data/index.json", "r")
if not index_file then
ngx.status = 200
ngx.say(cjson.encode({tags = {}}))
return
end
local index_content = index_file:read("*all")
index_file:close()
local ok, index = pcall(cjson.decode, index_content)
if not ok then
ngx.status = 500
ngx.say(cjson.encode({error = "Invalid index file"}))
return
end
-- 计算状态
local now = os.time()
for tag, item in pairs(index.tags) do
if item.expires_at then
local expires_time = os.time({
year = tonumber(item.expires_at:sub(1, 4)),
month = tonumber(item.expires_at:sub(6, 7)),
day = tonumber(item.expires_at:sub(9, 10)),
hour = tonumber(item.expires_at:sub(12, 13)),
min = tonumber(item.expires_at:sub(15, 16)),
sec = tonumber(item.expires_at:sub(18, 19))
})
if expires_time < now then
item.status = "expired"
elseif (expires_time - now) < 172800 then -- 2 days
item.status = "expiring_soon"
else
item.status = "valid"
end
else
item.status = "no_expiry"
end
end
ngx.status = 200
ngx.say(cjson.encode(index))
return
end
-- POST 请求:上传数据
if method == "POST" then
-- 读取请求体
ngx.req.read_body()
local body = ngx.req.get_body_data()
-- 如果 body 为空,可能是因为请求体被写入了临时文件
if not body then
local body_file = ngx.req.get_body_file()
if body_file then
local file = io.open(body_file, "r")
if file then
body = file:read("*all")
file:close()
end
end
end
if not body then
ngx.status = 400
ngx.say(cjson.encode({error = "Empty body"}))
return
end
-- 记录请求体大小,方便调试
ngx.log(ngx.INFO, "Received body size: ", string.len(body))
-- 调试:保存请求体到文件以便分析
local debug_file = io.open("/tmp/last_request_body.json", "w")
if debug_file then
debug_file:write(body)
debug_file:close()
ngx.log(ngx.INFO, "Request body saved to /tmp/last_request_body.json")
end
-- 解析 JSON
local ok, data = pcall(cjson.decode, body)
if not ok then
-- 记录解析错误的详细信息
ngx.log(ngx.ERR, "JSON parse error: ", data)
ngx.log(ngx.ERR, "Body preview (first 500 chars): ", string.sub(body, 1, 500))
-- 保存出错的位置附近的内容
local error_pos = tonumber(string.match(tostring(data), "character (%d+)"))
if error_pos then
local start_pos = math.max(1, error_pos - 100)
local end_pos = math.min(string.len(body), error_pos + 100)
ngx.log(ngx.ERR, "Error context (chars ", start_pos, "-", end_pos, "): ", string.sub(body, start_pos, end_pos))
end
ngx.status = 400
ngx.say(cjson.encode({
error = "Invalid JSON",
details = tostring(data),
body_size = string.len(body),
body_preview = string.sub(body, 1, 200),
error_position = error_pos
}))
return
end
-- 验证密码
if data.password ~= "admin123123123" then
ngx.status = 401
ngx.say(cjson.encode({error = "Invalid password"}))
return
end
-- 验证必填字段
if not data.tag or not data.type or not data.data then
ngx.status = 400
ngx.say(cjson.encode({error = "Missing required fields: tag, type, data"}))
return
end
-- 生成文件名(tag 中的特殊字符替换为 _
local filename = data.tag:gsub("[^%w%-]", "_") .. ".json"
local filepath = "/data/" .. filename
-- 添加时间戳
local timestamp = os.date("%Y-%m-%d %H:%M:%S")
if not data.metadata then
data.metadata = {}
end
if not data.metadata.created_at then
data.metadata.created_at = timestamp
end
data.metadata.updated_at = timestamp
-- 计算过期时间
if data.metadata.expires_in then
local expires_at = os.time() + data.metadata.expires_in
data.metadata.expires_at = os.date("%Y-%m-%d %H:%M:%S", expires_at)
end
-- 移除密码字段
data.password = nil
-- 保存数据文件
local file = io.open(filepath, "w")
if not file then
ngx.status = 500
ngx.say(cjson.encode({error = "Failed to save file"}))
return
end
file:write(cjson.encode(data))
file:close()
-- 更新索引
local index_file = io.open("/data/index.json", "r")
local index = {tags = {}}
if index_file then
local index_content = index_file:read("*all")
index_file:close()
local ok, parsed = pcall(cjson.decode, index_content)
if ok then
index = parsed
end
end
-- 更新或添加 tagtags是对象,直接赋值)
if not index.tags then
index.tags = {}
end
index.tags[data.tag] = {
tag = data.tag,
type = data.type,
file = filename,
created_at = index.tags[data.tag] and index.tags[data.tag].created_at or data.metadata.created_at,
updated_at = timestamp,
expires_at = data.metadata.expires_at
}
-- 保存索引
index_file = io.open("/data/index.json", "w")
if index_file then
index_file:write(cjson.encode(index))
index_file:close()
end
ngx.status = 200
ngx.say(cjson.encode({
success = true,
message = "Data saved",
tag = data.tag,
timestamp = timestamp
}))
return
end
-- 其他方法不支持
ngx.status = 405
ngx.say(cjson.encode({error = "Method not allowed"}))
}
}
# GET /api/data/copy/:tag - 一键复制格式
location ~ ^/api/data/copy/([^/]+)$ {
content_by_lua_block {
local cjson = require "cjson"
local tag = ngx.var[1]
-- 生成文件名
local filename = tag:gsub("[^%w%-]", "_") .. ".json"
local filepath = "/data/" .. filename
-- 读取数据
local file = io.open(filepath, "r")
if not file then
ngx.status = 404
ngx.say(cjson.encode({error = "Tag not found"}))
return
end
local content = file:read("*all")
file:close()
local ok, data = pcall(cjson.decode, content)
if not ok then
ngx.status = 500
ngx.say(cjson.encode({error = "Invalid data file"}))
return
end
-- 生成复制文本
local copy_config = {
serverUrl = "http://47.122.126.244:5001",
password = "admin123123123",
tag = tag,
type = data.type
}
local copy_text = cjson.encode(copy_config)
ngx.status = 200
ngx.say(cjson.encode({
copy_text = copy_text,
formatted = "```json\\n" .. copy_text .. "\\n```"
}))
}
}
# GET /api/data/:tag - 获取数据
# DELETE /api/data/:tag - 删除数据
location ~ ^/api/data/([^/]+)$ {
content_by_lua_block {
local cjson = require "cjson"
local tag = ngx.var[1]
local method = ngx.req.get_method()
-- 生成文件名
local filename = tag:gsub("[^%w%-]", "_") .. ".json"
local filepath = "/data/" .. filename
-- DELETE 请求
if method == "DELETE" then
-- 验证密码
local password = ngx.var.http_x_password
if password ~= "admin123123123" then
ngx.status = 401
ngx.say(cjson.encode({error = "Invalid password"}))
return
end
-- 删除文件
os.remove(filepath)
-- 更新索引
local index_file = io.open("/data/index.json", "r")
if index_file then
local index_content = index_file:read("*all")
index_file:close()
local ok, index = pcall(cjson.decode, index_content)
if ok then
-- tags 是对象,直接删除key
if index.tags and index.tags[tag] then
index.tags[tag] = nil
end
index_file = io.open("/data/index.json", "w")
if index_file then
index_file:write(cjson.encode(index))
index_file:close()
end
end
end
ngx.status = 200
ngx.say(cjson.encode({
success = true,
message = "Data deleted",
tag = tag
}))
return
end
-- GET 请求
if method == "GET" then
-- 验证密码
local password = ngx.var.http_x_password
if password ~= "admin123123123" then
ngx.status = 401
ngx.say(cjson.encode({error = "Invalid password"}))
return
end
-- 读取数据
local file = io.open(filepath, "r")
if not file then
ngx.status = 404
ngx.say(cjson.encode({error = "Tag not found"}))
return
end
local content = file:read("*all")
file:close()
ngx.status = 200
ngx.say(content)
return
end
-- 其他方法不支持
ngx.status = 405
ngx.say(cjson.encode({error = "Method not allowed"}))
}
}
}
}