Files
ai-kefu-admin/deploy/openresty-all-kefu.conf.template
T
浅念 7808f8a299 docs: 手册审查修正(前缀同键空间表述/WS验证预期)
- .env PUSH_EXTERNAL_KEY_PREFIX 注释纠错: 两租户前缀与各自老项目完全
  相同(同键空间)——这正是切换/回滚不产生重复 UniService 线索的依据;
  原稿写成'与老项目隔离'方向反了。两租户之间互异即可, 勿改。
- WS 验证预期收敛为 HTTP/1.1 101(curl 已强制 --http1.1, 不可能是 HTTP/2),
  模板与手册 §3.5 同步。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-14 09:52:23 +08:00

154 lines
8.9 KiB
Plaintext
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.
# =============================================================================
# all-kefu 生产反代模板(openresty / nginx 通用,完整可抄)
# =============================================================================
# 拓扑(spec §9):所有域名(租户域名 × N + 管理后台)反代到同一个容器
# chat.agent.hnxinyouhe.com (租户 auto) ─┐
# chat2.agent.hnxinyouhe.com (租户 jiancai) ─┼→ 127.0.0.1:8018 → all-kefu 容器
# <admin 域名>(形态 A,部署前由需求方确定)─┘
#
# 两种用法:
# · 独立 openresty/nginx:整份可抄——upstream + server 块齐备,按需删掉
# 不用的块(admin 只用形态 A 或 B 其一)。
# · 1Panel:server 块由面板生成,**只抄 location 片段**到
# /opt/1panel/www/sites/<域名>/proxy/*.conf——文末「1Panel 适配注记」
# 有逐条对照与现成片段。
#
# 血泪纪律(每条都出过线上事故,改配置前重读一遍):
# 1. Host 头必须透传(proxy_set_header Host $host)——本项目按 Host 头
# 解析租户,漏了这一行全站 404(不是报错,是静默 404)。
# 2. WS 升级头(Upgrade/Connection)全站只能出现一套——1Panel 面板生成
# 的反代若已带一套,再抄一份会重复定义,uvicorn 严格解析直接拒绝
# 握手(HTTP 正常、WS 426)。
# 3. client_max_body_size 至少 5m——头像上传上限 2MB,nginx 默认 1m
# 会直接 413(应用层校验根本收不到请求)。
# =============================================================================
# ---- upstream(独立部署时用;1Panel 场景见文末注记,直接写 IP:端口) --------
upstream all_kefu {
server 127.0.0.1:8018; # 单容器单 upstream;换端口只改这里
}
# ---- 租户域名 server 块(每个租户域名复制一份,改 server_name 即可) --------
# 含:全站反代 + WS 单套升级头 + /api/public(widget 头像/品牌,放行即全站
# 反代的一部分,单独点名是防"按路径收紧"时漏掉它——漏配的症状是 widget
# 头像 404)。
server {
listen 80; # 1Panel/证书层负责 443 与 HTTPS;独立部署自行补 listen 443 ssl
server_name chat2.agent.hnxinyouhe.com; # ← 每个租户域名一份:chat.(auto)/ chat2.(jiancai)
# WebSocket(/ws/chat):升级头单套 + 长读超时(长连接心跳间隔内不断开)
location /ws/ {
proxy_pass http://all_kefu;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host; # 纪律 1:WS 也按 Host 解析租户
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 引擎 IP/区域解析取 XFF 第一段
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
# 其余全部(/, /static, /api/chat*, /api/public, /health …)——
# /static/widget.js 的 no-cache 由应用层中间件下发(响应头已带
# Cache-Control: no-cache),反代无需另加,也别加会覆盖它的 expires。
location / {
proxy_pass http://all_kefu;
proxy_http_version 1.1;
proxy_set_header Host $host; # 纪律 1
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
client_max_body_size 5m; # 纪律 3(头像上传)
}
# ---- admin 形态 B(同域名路径分流;不用独立管理域名时启用) --------------
# 应用层对 /admin 与 /api/platform/ 前缀豁免租户 Host 解析,挂在租户
# 域名下照常工作(与租户聊天面共存,同源零 CORS)。启用 = 取消注释;
# nginx 最长前缀匹配,这两块优先于上面的 location /。
# 已知边界:形态 B 下后台"品牌-头像预览"走本域 /api/public/avatar,
# 解析到的是**本域名对应的租户**——切换到其他租户的配置页时头像预览
# 不准(已知项,CLAUDE.md Phase 4 清理单 #3/#4)。
#
# location /admin {
# proxy_pass http://all_kefu;
# proxy_http_version 1.1;
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto $scheme;
# proxy_read_timeout 300s;
# client_max_body_size 5m; # 头像上传走 /api/platform/.../brand/avatar
# }
# location /api/platform/ {
# proxy_pass http://all_kefu;
# proxy_http_version 1.1;
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto $scheme;
# proxy_read_timeout 300s;
# client_max_body_size 5m;
# }
}
# ---- admin 形态 A:独立管理域名 server 块(与形态 B 二选一) ------------------
# 整域反代到同一容器:/admin(SPA 静态,含 assets)与 /api/platform(后台
# API,认证由路由依赖守护)同一 upstream。全站反代即可,无需按路径拆分。
# 入口是 https://<admin 域名>/admin/——管理域根路径 / 走租户 Host 解析,
# 404 属预期(要友好可在本块加 location = / { return 302 /admin/; })。
# 已知边界:独立管理域名下 /api/public/*(按租户 Host 解析)必 404——后台
# "品牌-头像预览"在该形态加载不出(预览以本地简化预览为准),已知项
# (CLAUDE.md Phase 4 清理单 #3),非配置错误,勿按 404 排障反代。
server {
listen 80;
server_name admin.example.com; # ← 占位:管理域名部署前由需求方确定
location / {
proxy_pass http://all_kefu;
proxy_http_version 1.1;
proxy_set_header Host $host; # 形态 A 的 Host 是管理域(未知租户)——
# /admin 与 /api/platform 豁免租户解析,照常工作
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
client_max_body_size 5m; # 头像上传
}
}
# =============================================================================
# 1Panel 适配注记(jc 项目两次线上事故的源头,逐条核对)
# =============================================================================
# 1. server 块由面板建站生成,不要手写整个 server——只把上面的 location
# 片段放进站点目录:
# /opt/1panel/www/sites/<域名>/proxy/root.conf ← location /(全站反代)
# /opt/1panel/www/sites/<域名>/proxy/ws.conf ← location /ws/(升级头单套)
# /opt/1panel/www/sites/<域名>/proxy/admin.conf ← 形态 B 的两块(启用时)
# 片段内 proxy_pass 直接写 http://127.0.0.1:8018——upstream 块只能声明在
# http 上下文,server 级 include 文件里放不进去(面板世界没有"全局
# upstream 文件"的挂载点,直写地址即单一事实源,换端口全文替换)。
#
# 2. 主 conf 的 include 行检查(1Panel 坑:面板建站默认可能不带):
# /opt/1panel/www/conf.d/<域名>.conf
# 里必须有(没有就手工补,改前备份同目录 *.conf.bak.<日期>):
# include /www/sites/<域名>/proxy/*.conf;
# 注意路径前缀是 /www/sites/...(1Panel 容器内视角),不是 /opt/1panel/...。
# 面板配了反代却不生效,九成是这行缺失。
#
# 3. 1Panel 面板生成的反代若自带 Upgrade/Connection 头,删掉面板那套再放
# ws.conf(升级头只能一套,见顶部纪律 2)。
#
# 4. 改完必验再重载(先 -t 再 reload;reload 平滑,不影响其他站点):
# docker exec $(docker ps --filter name=openresty -q | head -1) nginx -t
# docker exec $(docker ps --filter name=openresty -q | head -1) nginx -s reload
# 验证(逐域名):
# curl -s https://<租户域名>/health | head -c 200 # {"status":"ok",...}
# curl -sI https://<租户域名>/static/widget.js | grep -i cache # Cache-Control: no-cache
# curl -s -i --http1.1 -H "Connection: Upgrade" -H "Upgrade: websocket" \
# -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" -H "Sec-WebSocket-Version: 13" \
# https://<租户域名>/ws/chat | head -1 # 期待 HTTP/1.1 101
# =============================================================================