Guanghu Domestic Migration d1e47f4565
Some checks are pending
自动更新代码和重启 / update-and-restart (push) Waiting to run
CI检查 + 自动部署 / check (push) Waiting to run
CI检查 + 自动部署 / deploy (push) Blocked by required conditions
重启聊天服务 / restart (push) Waiting to run
chore: import sanitized domestic snapshot for REPO-002
Source snapshot: ca48d3ddf926d79aa138306164169baf764bb829
2026-07-17 15:54:41 +08:00
..

光湖 Portal · 后端 (Express)

主权: TCS-0002∞ · ICE-GL∞ · 国作登字-2026-A-00037559 守护: 铸渊 · ICE-GL-ZY001 PR-4 · baton-004

这是什么

guanghulab.com 后端 —— 单进程 Express, 跑在 ZY-SVR-CN01 (广州 2C2G) 上, 由 pm2 守护, 内存上限 512M, max_restarts=20。

不是 通用 Web 框架。它的存在只是为了:

  1. 提供本地 SQLite 对话历史 (conversations.sqlite)
  2. 把浏览器的 /api/chat 请求字节级转发到 AutoDL 推理端 (cc-002)
  3. 切模型 = 转发到推理端 /v1/switch-model
  4. 给前端右栏喂本地快照 (人格库 / 模块注册表), 不调 GitHub API

文件结构

server/portal/
├── server.js                     ← Express 入口
├── package.json                  ← 仅 express + better-sqlite3
├── ecosystem.config.js           ← pm2 配置 (max_memory_restart=512M)
├── db/
│   └── init.js                   ← SQLite schema + WAL
├── lib/
│   └── inference-client.js       ← 推理端点热加载 + SSE 字节管道
├── routes/
│   ├── conversations.js          ← 列表 / 新建 / 历史
│   ├── chat.js                   ← /api/chat (SSE)
│   ├── active-model.js           ← 当前模型 / 切换
│   ├── persona-db.js             ← 人格层右栏 (只读快照)
│   └── manifest.js               ← 开发层右栏 (本地 manifest)
├── tests/
│   ├── strip-system.test.js      ← cc-002 三道关回归
│   └── routes-smoke.test.js      ← 路由冒烟 (mock 推理)
└── README.md

因果链落地点

因果链 在后端怎么体现
cc-002 三道关 lib/inference-client.js#_strip_system_messages + routes/chat.js 入口 filter + 推理端 server.py 入口 strip — 三层防御, 任意一层都能挡住 system role 注入
cc-003 不写死 inference-client.init 每次请求都 stat inference-endpoint.json mtime, 文件被 refresh-autodl-endpoint.yml 重写后下一次请求就用新值, 不需要 restart
cc-004 中文 所有 4xx/5xx body 走 {error, code, message:"中文"}, Awen 看截图就能懂
2C2G 内存约束 better-sqlite3 同步连接 (省一份 worker 内存), pm2 单 instance, body limit 256K, 单条消息 8K 上限

路由

方法 路径 用途
GET /api/health portal + 推理端探活, 顶栏健康胶囊用
GET /api/active-model 当前激活的模型
POST /api/active-model {name:"mother"|"coder"} 切换, 同步转发推理端
GET /api/conversations 对话列表 (200 条上限)
POST /api/conversations 新建
GET /api/conversations/:id/messages 历史 (1000 条上限)
POST /api/chat SSE 字节管道, 直转 AutoDL /v1/chat/completions
GET /api/persona-db 人格层右栏 (本地快照, 30s 缓存)
GET /api/manifest 开发层右栏 (本地 manifest, 30s 缓存)

静态:

  • GET /frontend/lighthouse-portal/index.html (兜底, nginx 上线后由 nginx 直发)
  • GET /_static/*frontend/lighthouse-portal/assets/* (兜底, nginx 上线后由 nginx 直发)

推理端点文件 (PR-3 写)

PORTAL_DATA_DIR/inference-endpoint.json:

{
  "scheme": "https",
  "host": "u123-456.autodl.com",
  "port": 12345,
  "bearer": "可选",
  "updated_at": "2026-05-09T..."
}

.github/workflows/refresh-autodl-endpoint.yml 在冰朔每次 AutoDL 重开后写入。 本进程不重启, 下次请求自动读新值 (mtime 变了就重 parse)。

启动

本地调试

cd server/portal
npm install
PORTAL_DATA_DIR=/tmp/portal-dev npm run dev
# 浏览器访问 http://127.0.0.1:3000/

生产 (pm2)

.github/workflows/deploy-domain-server.yml SSH 到 ZY-SVR-CN01 后执行:

cd /data/guanghulab/portal
npm ci --omit=dev
pm2 startOrReload ecosystem.config.js --update-env
pm2 save

nginx

server/setup/domain-cn/nginx/guanghulab.conf.template 已经追加反代:

location /_static/ {
    alias /data/guanghulab/portal/static/;
}
location / {
    proxy_pass http://127.0.0.1:3000/;
    # ... SSE 必需:
    proxy_buffering off;
    proxy_cache off;
    proxy_read_timeout 300s;
}

测试

cd server/portal
npm test

包含:

  • tests/strip-system.test.js_strip_system_messages cc-002 回归
  • tests/routes-smoke.test.js — Express 路由冒烟 (推理端 mock 成 503 时, /api/chat 优雅降级)