包含本地/远程部署脚本、AI Agent 服务、Dashboard 与 QA 验证。 Co-authored-by: Cursor <cursoragent@cursor.com>
8.2 KiB
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。
常用命令
# 本地启停
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
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。
正确:
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.passwordPBKDF2 哈希 ✅ 标准api_tokens.token明文存储 ⚠️ 任何 PG 读权限可枚举所有 tokeninstance_configurations加密字段(如 LLM_API_KEY)通过 GET/api/instances/configurations/时被InstanceConfigurationSerializer.to_representationdecrypt 后明文回吐给前端 → 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 出新版后)
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。