1. 架构与背景

在部署基于 NTQQ 核心的机器人协议端 NapCat (NapCat.Onebot) 与第三方 Web 客户端 Stapxs QQ Lite (napcat-plugin-ssqq) 时,典型网络拓扑如下:

+--------------------------+
|  Stapxs QQ Lite (Web UI) |
+-------------+------------+
              |
              | WSS (https://napcat.example.com/ws)
              v
+--------------------------+
|      Nginx Gateway       |
+-------------+------------+
              |
              | WS (127.0.0.1:3001)
              v
+--------------------------+          +--------------------------+
| NapCat OneBot 11 (Core)  | <------> |  AstrBot / Other Client  |
+--------------------------+          +--------------------------+
              |
              | Native NTQQ API
              v
+--------------------------+
|        Linux QQ          |
+--------------------------+
  • NapCat 协议端:同时维护针对机器人的反向 WebSocket 客户端(如连接 AstrBot 127.0.0.1:6199)与针对人类管理/客户端的正向 WebSocket 服务端(监听 127.0.0.1:3001)。
  • Stapxs QQ Lite 插件:以 NapCat 插件扩展页面形式注入,其前端静态资源由 NapCat Web 服务(端口 6099)托管在 /plugin/napcat-plugin-ssqq/files/static/。
  • Nginx 网关:负责处理外部 SSL 证书终端,并将 /ws 前缀路由反向代理至正向 WebSocket 服务端口(3001)。

2. 常见故障与深度根因分析

故障 1:点击“去连接”无任何弹窗且页面卡死/空白

现象

在设置界面看到“还没有连接到 OneBot 耶”,点击右侧的“去连接”按钮后,没有任何配置弹窗弹出,页面停留在空白主页,用户无法配置连接地址。

深度根因

  1. 按钮实现逻辑:
    在 ViewOptAccount 组件中,“去连接”按钮的代码为:

    function c() {
      var f;
      (f = document.getElementById("bar-home")) == null || f.click();
    }

    该按钮本身并非弹窗触发器,其本意仅是触发侧边栏“主页”(Home)图标的点击事件。

  2. 构建时条件隐藏:
    在 napcat-plugin-ssqq 专用构建分支中,作者设置了环境变量 VITE_NAPCAT=1,导致主页的连接表单被硬编码条件隐藏:

    // setup 中 l="1"
    s(v).page == "Home" ? t("div", t5, [
      s(l) ? H("", !0) : p("div", n5, [ /* 连接到 OneBot 表单 */ ])
    ])

    因为 s(l) 恒为真,主页上的表单直接被渲染为注释节点 <!-- -->。

  3. 内置快速连接失效闭环:
    该插件原本设计为读取父级 WebUI 的 localStorage.getItem("token") 并调用 /api/Debug/create 创建临时调试适配器。然而 NapCat WebUI 的 Session 凭据默认仅 1 小时有效(WithinOneHour),一旦过期或未命中,快速连接抛出 401 失败。而主页表单又被 s(l) 隐藏,导致用户完全被锁在无界面的死循环中。

故障 2:外部赞助商接口异常导致 Vue 渲染崩溃

现象

浏览器控制台报出:

TypeError: Cannot read properties of undefined (reading 'length')
    at Proxy.<anonymous> (main-xxx.js)

整个 Web 客户端界面白屏卡死,所有交互无响应。

深度根因

客户端启动时会请求远程接口 https://api.stapxs.cn/ssqq/sponsor。当远程服务端由于缺少参数返回 {"error":"params incomplete"} 时,前端代码未对返回对象中预期的列表字段做空值防御,直接访问 .length 导致整个 Vue 组件树崩溃。


故障 3:WebUI 网络配置删除后客户端断连

现象

在 NapCat WebUI 的“网络配置”界面误删正向 WebSocket 配置后,端口 3001 监听消失,外部客户端连接报错 502 Bad Gateway 或 404/401。

深度根因

NapCat 的网络拓扑配置保存在 config/onebot11_<uin>.json。当在管理界面删除对应条目时,websocketServers 数组被置空。此时 Nginx 的 /ws 代理无后端可连。


3. 解决步骤与规范操作

步骤 1:恢复 NapCat 正向 WebSocket 服务器

检查并编辑 NapCat 配置文件(位于 NapCat 目录下的 config/onebot11_<uin>.json),在 network.websocketServers 数组中添加正向服务:

{
  "network": {
    "websocketServers": [
      {
        "name": "ssqq-ws",
        "enable": true,
        "host": "127.0.0.1",
        "port": 3001,
        "messagePostFormat": "array",
        "reportSelfMessage": false,
        "token": "",
        "enableForcePushEvent": true,
        "debug": false,
        "heartInterval": 30000
      }
    ]
  }
}

重启 NapCat 服务并验证监听:

sudo systemctl restart napcat-<uin>.service
ss -tlnp | grep 3001

步骤 2:配置 Nginx WSS 反向代理(前缀匹配)

在 Nginx 配置中,为 WebSocket 端点配置前缀匹配规则(避免因尾部斜杠导致 401/404):

location ^~ /ws {
    proxy_pass http://127.0.0.1:3001;
    proxy_http_version 1.1;
    proxy_buffering off;
    proxy_read_timeout 86400s;
    proxy_send_timeout 86400s;
    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_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

测试并重载 Nginx:

sudo nginx -t && sudo systemctl reload nginx

步骤 3:修复 Stapxs QQ Lite 前端代码

编辑插件静态资源 plugins/napcat-plugin-ssqq/webui/dist/assets/js/main-*.js:

  1. 解除主页连接表单隐藏:
    将 l="1" 替换为 l=!1,确保在未连接时主页能够正常显示“连接到 OneBot”卡片。
  2. 自动预填当前站点的 WSS 地址:
    将:

    Le.address = k.sysConfig.address, Le.connectionHistory = du()

    替换为:

    Le.address = k.sysConfig.address || (location.protocol === "https:" ? "wss://" : "ws://") + location.host + "/ws", Le.connectionHistory = du()
  3. 开启后端信息展示与退出连接入口:
    在 ViewOptAccount 中将 o="1" 替换为 o=!1。

4. 验证与复用要点

  1. 测试 WSS 握手:

    python3 -c "
    import ssl, socket
    ctx = ssl.create_default_context()
    ctx.check_hostname = False; ctx.verify_mode = ssl.CERT_NONE
    s = socket.create_connection(('127.0.0.1', 443))
    ss = ctx.wrap_socket(s, server_hostname='napcat.example.com')
    req = 'GET /ws HTTP/1.1\r\nHost: napcat.example.com\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\n\r\n'
    ss.sendall(req.encode())
    print(ss.recv(1024).decode().split('\r\n')[0])
    ss.close()
    "
    # 输出为 HTTP/1.1 101 Switching Protocols 即代表握手正常
  2. 用户端使用流程:

    • 浏览器按 Ctrl + F5 强制刷新 NapCat 插件页面。
    • 主页直接呈现连接卡片,地址栏已自动填充 wss://napcat.example.com/ws。
    • 连接密钥留空,勾选“记住密码”与“自动连接”,点击“连接”即可进入完整聊天界面。

本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/AI apps