包含本地/远程部署脚本、AI Agent 服务、Dashboard 与 QA 验证。 Co-authored-by: Cursor <cursoragent@cursor.com>
144 lines
8.2 KiB
Markdown
144 lines
8.2 KiB
Markdown
# plane-gptproto-integration
|
||
|
||
Plane v1.3.0(AGPL-3.0 开源项目管理)自部署 + gptproto.com(OpenAI 兼容聚合 API)AI Agent 集成。本地 mac docker 验证后再上 YOUR_SERVER_IP 生产。
|
||
|
||
## 双环境部署架构
|
||
|
||
| 环境 | Web 入口 | AI 端点 | compose 路径 | 端口绑定 |
|
||
|---|---|---|---|---|
|
||
| 本地 mac | http://localhost:8090 | http://localhost:8100 | `deploy/local/` + `src/` git clone | `0.0.0.0:8090/8100` |
|
||
| 生产 YOUR_SERVER_IP | https://YOUR_SERVER_IP:8445 | https://YOUR_SERVER_IP:8446 | `/opt/plane/` (1Panel + OpenResty) | `127.0.0.1:8092/8101` |
|
||
|
||
生产走 1Panel + OpenResty 反代 + ufw + 自签 SSL(`/opt/1panel/www/ssl/luma/luma.{crt,key}`,已存在)。**容器只绑 127.0.0.1**,公网入口由 OpenResty `/opt/1panel/www/conf.d/plane.conf` 反代。生产端口规划遵循 `~/dev/PRODUCTION_DEPLOY_TEMPLATE.md`:公网用 8445/8446/8447 段,内部用 8090+。**8090 已被 1Panel openresty 占用**,本地 mac 用 8090 OK,生产改 8092。
|
||
|
||
## 常用命令
|
||
|
||
```bash
|
||
# 本地启停
|
||
cd deploy/local
|
||
docker compose -f src/deployments/cli/community/docker-compose.yml -f docker-compose.override.yml --env-file .env up -d --pull never
|
||
bash ../../qa/verify-local.sh # 6 项健康检查,exit=0 全过
|
||
|
||
# 生产 (ssh + docker compose)
|
||
ssh root@YOUR_SERVER_IP 'cd /opt/plane && docker compose -f src/deployments/cli/community/docker-compose.yml -f docker-compose.override.yml --env-file .env restart'
|
||
|
||
# AI Agent 改 main.py 后同步生产 + rebuild
|
||
rsync -az --exclude=.venv --exclude=__pycache__ ai-agent/ root@YOUR_SERVER_IP:/opt/plane/ai-agent/
|
||
ssh root@YOUR_SERVER_IP 'cd /opt/plane && docker compose -f src/deployments/cli/community/docker-compose.yml -f docker-compose.override.yml --env-file .env up -d --build --force-recreate plane-ai'
|
||
|
||
# pytest 本地 TDD
|
||
cd ai-agent && source .venv/bin/activate && pytest -v
|
||
```
|
||
|
||
## 非显然坑点(必读,本次会话血泪验证)
|
||
|
||
### 1. Plane v1.3.0 compose 路径变了
|
||
官方 selfhost 入口从旧版 `src/deploy/selfhost/docker-compose.yml` 搬到 `src/deployments/cli/community/docker-compose.yml`。Worker 用旧路径会 `no such file or directory`。
|
||
|
||
### 2. Docker Desktop 自动捕获 macOS 系统代理 → TLS bug
|
||
mac 上 `docker pull` 报 `tls: bad record MAC` / `version 300 vs 303` 几乎一定是你 Clash/V2Ray 占着 `127.0.0.1:1082`,Docker Desktop 自动用了它,本地代理拆 TLS 失败。修法:Docker Desktop → Settings → Resources → Proxies → **打开 Manual** 但三栏全留空 → **Quit Docker Desktop**(完整退出,Restart 不够,要 Quit)→ 重开。`http.docker.internal:3128` 在 `docker info` 里永远显示,是 Docker 内置 forwarder,跟你的代理设置无关。
|
||
|
||
### 3. OpenResty 反代 FastAPI uvicorn 必须显式 HTTP/1.1
|
||
```nginx
|
||
proxy_pass http://127.0.0.1:8101;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Connection "";
|
||
```
|
||
缺这两行 → uvicorn 等 keepalive 不释放 → 30s 必 ReadTimeout。已修进 `deploy/remote/plane.conf` 8446 server block。
|
||
|
||
### 4. gptproto 部分 model 当前不可用
|
||
`claude-sonnet-4-6` 60s timeout(gptproto 端挂),改用 `claude-sonnet-4-5-20250929` 或 `claude-haiku-4-5-20251001`。`chatgpt-4o-latest` 可能 503 overloaded。`.env` 里 `GPTPROTO_MODEL` 决定。
|
||
|
||
### 5. Anthropic 不支持 OpenAI 的 `response_format: json_object`
|
||
gptproto 转发给 Anthropic 时该参数被忽略,模型可能返回 ```` ```json ... ``` ```` 围栏或纯文本。`ai_agent/main.py::_extract_json` 已用 regex 兜底(先抓围栏再抓 `{...}`)。
|
||
|
||
### 6. plane-ai 进程残留死连接
|
||
切 model 或修 .env 后 ReadTimeout 不消失,必须 `docker compose up -d --force-recreate plane-ai`。普通 `restart` 或 `up -d` 不彻底。
|
||
|
||
### 7. `rsync --delete` 会清生产 src/
|
||
本地 staging 目录没 src/ 时,`rsync -az --delete /tmp/staging/ /opt/plane/` 会删服务器 git clone 的 src/。**绝不在生产路径用 --delete**,或显式 `--exclude=src/`。
|
||
|
||
### 8. 非标端口部署:OpenResty 必须用 `$http_host` 不是 `$host`
|
||
Plane backend `storage.py` 用 `request.get_host()` 构造 MinIO presigned upload URL。两层反代(OpenResty → Caddy → Django)必须把**带端口**的 Host 头透传到底:
|
||
- nginx `$host` 不带端口(`YOUR_SERVER_IP`)
|
||
- nginx `$http_host` 带端口(`YOUR_SERVER_IP:8445`)
|
||
|
||
错误用 `$host` → Django 得到 host 没端口 → presigned URL `https://YOUR_SERVER_IP/uploads`(走 443 撞别的服务) → CORS blocked。
|
||
|
||
正确:
|
||
```nginx
|
||
location / {
|
||
proxy_pass http://127.0.0.1:8092;
|
||
proxy_set_header Host $http_host; # ← 必须 $http_host 不是 $host
|
||
...
|
||
}
|
||
```
|
||
|
||
`APP_DOMAIN=YOUR_SERVER_IP:8445` 也要(影响 Plane CORS allow-origin 等),但单独改它不够,必须配合 OpenResty 修。
|
||
|
||
## Plane v1.3.0 数据库/安全实测
|
||
|
||
- `users.password` PBKDF2 哈希 ✅ 标准
|
||
- `api_tokens.token` **明文**存储 ⚠️ 任何 PG 读权限可枚举所有 token
|
||
- `instance_configurations` 加密字段(如 LLM_API_KEY)通过 GET `/api/instances/configurations/` 时被 `InstanceConfigurationSerializer.to_representation` **decrypt 后明文回吐**给前端 → F12 Network 可见
|
||
- 真 `GPTPROTO_API_KEY` 仅放 `.env`(chmod 600)+ 容器 env,**不入 Plane DB**
|
||
- god-mode AI Settings 永远留空(我们走独立 plane-ai 服务,Plane backend 用不到这个 key)
|
||
|
||
## API 端点 (FastAPI plane-ai)
|
||
|
||
| 端点 | 输入 | 行为 |
|
||
|---|---|---|
|
||
| `GET /healthz` | — | `{"status":"ok"}`,Dockerfile healthcheck 用 |
|
||
| `POST /ai/create-issue` | `{prompt, project_id, workspace_slug}` | gptproto 解析 → POST Plane `/api/v1/.../issues/` |
|
||
| `POST /ai/summarize` | `{issues:[...]}` | gptproto 中文摘要 |
|
||
| `POST /ai/search` | `{query, workspace_slug}` | Plane `/issues/search/?search=` + gptproto 排序 |
|
||
|
||
注意 search 用 `/issues/search/`(v1 public 端点)不是 `/search/`(internal app 端点要 session auth)。
|
||
|
||
## 文件清单
|
||
|
||
```
|
||
CLAUDE.md 本文件
|
||
AGENTS.md worker 共享上下文(开发期产物)
|
||
README.md 对外简介
|
||
docs/PLAN.md 完整设计 + 后门/隐私评估
|
||
docs/USER_GUIDE.md 给领导/团队的使用说明
|
||
deploy/local/.env 本地密钥(600, .gitignore)
|
||
deploy/local/docker-compose.override.yml 本地端口 0.0.0.0:8090/8100
|
||
deploy/local/pull-plane.sh git clone v1.3.0(idempotent)
|
||
deploy/local/src/ Plane 源码(git, .gitignore)
|
||
deploy/remote/docker-compose.override.yml 生产端口 127.0.0.1:8092/8101
|
||
deploy/remote/plane.conf OpenResty 反代(8445/8446 → 8092/8101,含 HTTP/1.1 修复)
|
||
ai-agent/ai_agent/main.py 4 端点 + _extract_json + httpx async
|
||
ai-agent/tests/ pytest TDD,respx mock 不真联网
|
||
qa/verify-local.sh 6 项健康检查
|
||
qa/checklist.md worker 签名 + 人工验收项
|
||
```
|
||
|
||
## 红线
|
||
|
||
- 不在 god-mode → AI Settings 填真 GPTPROTO_API_KEY(Plane backend 用不到 + 解密回显有 leak 风险)
|
||
- 不直接绑容器到 0.0.0.0:80(违反 `~/dev/PRODUCTION_DEPLOY_TEMPLATE.md`)
|
||
- 不 ssh 手动改生产 .env 不同步本地(本地 `deploy/remote/` 是 source of truth)
|
||
- 不拉 Plane master/latest,锁 `v1.3.0` + GitHub Release tag
|
||
- 不在生产路径用 `rsync --delete`
|
||
|
||
## 升级流程(Plane 出新版后)
|
||
|
||
```bash
|
||
ssh root@YOUR_SERVER_IP
|
||
cd /opt/plane/src
|
||
git fetch --depth 1 origin refs/tags/vX.Y.Z:refs/tags/vX.Y.Z
|
||
git checkout vX.Y.Z
|
||
cd ..
|
||
sed -i 's/^APP_RELEASE=.*/APP_RELEASE=vX.Y.Z/' .env
|
||
docker compose -f src/deployments/cli/community/docker-compose.yml -f docker-compose.override.yml --env-file .env pull
|
||
docker compose -f src/deployments/cli/community/docker-compose.yml -f docker-compose.override.yml --env-file .env up -d
|
||
# 本地同步: 改 deploy/remote/.env.template 的 APP_RELEASE
|
||
# 验证: 镜像无 :latest, ufw 无变, openresty conf 无需改
|
||
```
|
||
|
||
## 服务器现状 (YOUR_SERVER_IP, 1Panel v2.1.10)
|
||
|
||
8 核 / 31GB / 387GB(余 360GB+),Hostinger VPS。已部署兄弟项目:Luma 主站(443/8088)、harvester(8443/8089)、领航 BI(8444)、VideoFlow(9080-9443)。新增 Plane 占 8445(web)/8446(ai),内部 8092/8101。
|