2
This commit is contained in:
@@ -1,6 +1,4 @@
|
||||
server {
|
||||
# 文件名前缀是 000-,会比 ai.sggai.site.conf 更早加载。
|
||||
# 这样可以在不修改原配置文件的情况下,让这里的 mock 逻辑优先生效。
|
||||
listen 80;
|
||||
listen 443 ssl;
|
||||
server_name ai.sggai.site;
|
||||
@@ -19,7 +17,6 @@ server {
|
||||
gzip off;
|
||||
gunzip off;
|
||||
|
||||
# 公共反代配置放在 server 级别,主后端 proxy_pass location 会继承这些配置。
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header Authorization $http_authorization;
|
||||
@@ -57,59 +54,45 @@ server {
|
||||
proxy_read_timeout 3600s;
|
||||
proxy_send_timeout 3600s;
|
||||
|
||||
# 只拦截很小的非流式 Chat Completions 请求。
|
||||
# 不满足条件的请求会内部跳转到正常后端,不直接返回 mock。
|
||||
location = /v1/chat/completions {
|
||||
client_body_buffer_size 16k;
|
||||
|
||||
content_by_lua_block {
|
||||
-- 不满足 mock 条件时,统一内部跳转到这个 named location。
|
||||
local backend = "@ai_sggai_site_backend"
|
||||
|
||||
-- 请求体总大小上限:只处理非常小的探测类请求。
|
||||
local max_body_bytes = 1024
|
||||
|
||||
-- 只处理 POST;其它方法按普通请求转发到真实后端。
|
||||
if ngx.req.get_method() ~= "POST" then
|
||||
return ngx.exec(backend)
|
||||
end
|
||||
|
||||
-- 先看 Content-Length,超过阈值就不读取 body,直接放行。
|
||||
local content_length = tonumber(ngx.var.http_content_length)
|
||||
if not content_length then
|
||||
-- 没有 Content-Length 的请求可能是 chunked,不为了判断 mock 去读取未知大小的 body。
|
||||
return ngx.exec(backend)
|
||||
end
|
||||
|
||||
-- 请求体太大,说明不是目标小请求,直接走真实后端。
|
||||
if content_length > max_body_bytes then
|
||||
return ngx.exec(backend)
|
||||
end
|
||||
|
||||
-- 到这里说明 body 足够小,可以安全读取并解析 JSON。
|
||||
ngx.req.read_body()
|
||||
|
||||
local body = ngx.req.get_body_data()
|
||||
if not body or #body > max_body_bytes then
|
||||
-- body 不在内存里或实际大小仍然超限时,不 mock,转发到真实后端。
|
||||
return ngx.exec(backend)
|
||||
end
|
||||
|
||||
-- JSON 解析失败,或 stream 不是 false,都不 mock。
|
||||
local cjson = require "cjson.safe"
|
||||
local payload = cjson.decode(body)
|
||||
if type(payload) ~= "table" or payload.stream ~= false then
|
||||
return ngx.exec(backend)
|
||||
end
|
||||
|
||||
-- 必须有 messages 数组;没有就不 mock。
|
||||
if type(payload.messages) ~= "table" then
|
||||
return ngx.exec(backend)
|
||||
end
|
||||
|
||||
local has_hi_content = false
|
||||
|
||||
-- 遍历所有 message,只匹配精确的 "content": "hi"。
|
||||
for _, message in ipairs(payload.messages) do
|
||||
if type(message) == "table" and message.content == "hi" then
|
||||
has_hi_content = true
|
||||
@@ -117,12 +100,10 @@ server {
|
||||
end
|
||||
end
|
||||
|
||||
-- 没有精确命中 "content": "hi",交给真实后端处理。
|
||||
if not has_hi_content then
|
||||
return ngx.exec(backend)
|
||||
end
|
||||
|
||||
-- 命中小请求 + stream=false + "content": "hi" 条件,直接返回 mock 响应。
|
||||
ngx.status = ngx.HTTP_OK
|
||||
ngx.header["Content-Type"] = "application/json; charset=utf-8"
|
||||
ngx.say([[{
|
||||
@@ -143,12 +124,10 @@ server {
|
||||
}
|
||||
}
|
||||
|
||||
# 仅供上面的 Lua 逻辑通过 ngx.exec 内部跳转使用,外部 URL 不能直接访问这个 location。
|
||||
location @ai_sggai_site_backend {
|
||||
proxy_pass http://10.1.0.1:3001;
|
||||
}
|
||||
|
||||
# 普通流量保持原来的上游转发行为。
|
||||
location / {
|
||||
proxy_pass http://10.1.0.1:3001;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user