a1c68ec6b2
隐患:gateway 换 access_token 时微信看到的是本地宽带出网 IP(106.58.232.43, 动态会变),一变白名单就失效、二维码建不出来(40164)。 按微信官方推荐的「中控服务器」架构解决:腾讯云静态 IP 统一换 token,gateway 拉取 使用。微信 IP 白名单只限制换 token 这一步,拿 token 建二维码不查 IP —— 所以 换 token 挪到中控、建二维码仍在 gateway 本地,白名单只填腾讯云 IP,永不失效。 - wechat.PullToken:从中控 HTTPS 端点拉 token(Bearer 密钥鉴权),坏响应/403/空 token 一律报错不当成功;中控没给 expires_in 时按 7200 兜底。 - handler.accessToken:配了 WECHAT_TOKEN_URL 就只从中控拉、绝不自己 FetchAccessToken (微信要求单点刷新,多点各自换会互相顶掉 token);不配维持直连,零副作用可回退。 - compose:gateway 加 WECHAT_TOKEN_URL / WECHAT_TOKEN_SECRET(从宿主机 .env 注入)。 - deploy/wechat-token-zhongkong.md:腾讯云侧 cron 脚本 + nginx 配置 + 切换验证步骤。 本地验证(假中控 httptest + 真 gateway):建票时 Redis 缓存的是中控给的 token (FAKE_TOKEN_FROM_ZHONGKONG),gateway 未直连微信换 token;随后拿该 token 调 qrcode/create(假 token 报 40001 属预期)——证明「中控换 token → 本地建二维码」链路成立。 4 组 PullToken 单测覆盖正常/403/坏JSON/兜底。 真机验证(部署后):把本地 IP 从白名单删掉、只留腾讯云 IP,扫码仍能登录即坐实 qrcode 不受 IP 限制;若建二维码报 40164 则退回 tinyproxy 正向代理备选。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
106 lines
5.7 KiB
YAML
106 lines
5.7 KiB
YAML
# sundynix-agentix · 应用机 192.168.100.132
|
||
#
|
||
# cd deploy/132-app && cp .env.example .env # 填密钥/密码/MinIO 凭据
|
||
# docker compose up -d --build # 在 132 本地构建并运行(Gitea 自动部署也走这条)
|
||
#
|
||
# 说明:
|
||
# - 对外只暴露 gateway:3000(frp 映射)。gateway 已内嵌 admin 控制台(同源 serve UI+API),
|
||
# 无需单独 admin nginx 容器。dispatcher/mcp-go/mcp-py 只走 NATS,无对外端口。
|
||
# - 基础设施在 128(PG/NATS集群/Redis/Milvus/Neo4j),对象存储在 126(MinIO),env 全部指向它们。
|
||
# - 构建上下文为仓库根(../..,因 Go 服务 replace ../sundynix-shared)。
|
||
# - NATS_STREAM_REPLICAS=3:durable 流走 128 三节点集群,有 Raft quorum 容错。
|
||
|
||
services:
|
||
gateway:
|
||
build: { context: ../.., dockerfile: sundynix-gateway/Dockerfile }
|
||
image: sundynix/gateway:${TAG:-latest}
|
||
restart: unless-stopped
|
||
environment:
|
||
APP_ENV: production
|
||
GATEWAY_ADDR: ":8080"
|
||
NATS_URL: nats://192.168.100.128:4222,nats://192.168.100.128:4223,nats://192.168.100.128:4224
|
||
NATS_STREAM_REPLICAS: "3"
|
||
POSTGRES_DSN: postgres://sundynix:${POSTGRES_PASSWORD:?}@192.168.100.128:5432/sundynix?sslmode=disable
|
||
REDIS_ADDR: 192.168.100.128:6379
|
||
MINIO_ENDPOINT: 192.168.100.126:9000
|
||
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:?}
|
||
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:?}
|
||
MINIO_BUCKET: ${MINIO_BUCKET:-sundynix-docs}
|
||
SUNDYNIX_SECRET_KEY: ${SUNDYNIX_SECRET_KEY:?}
|
||
JWT_SECRET: ${JWT_SECRET:?}
|
||
ADMIN_USER_IDS: ${ADMIN_USER_IDS:-}
|
||
CORS_ALLOW_ORIGIN: ${CORS_ALLOW_ORIGIN:-*}
|
||
OTEL_EXPORTER_OTLP_ENDPOINT: http://192.168.100.128:4318
|
||
# 微信域名校验文件目录(容器内路径);未设则该路由不注册
|
||
WECHAT_VERIFY_DIR: /etc/sundynix/wechat-verify
|
||
# 微信 access_token 中控(腾讯云静态 IP 换 token,gateway 只拉不换)。
|
||
# 配了才从中控拉,避免本地动态出网 IP 触发 40164;不配则 gateway 直连微信换 token。
|
||
# 值放宿主机 .env:WECHAT_TOKEN_URL=https://.../token.json、WECHAT_TOKEN_SECRET=<共享密钥>
|
||
WECHAT_TOKEN_URL: ${WECHAT_TOKEN_URL:-}
|
||
WECHAT_TOKEN_SECRET: ${WECHAT_TOKEN_SECRET:-}
|
||
ports: ["3000:8080"] # frp 外网 → 132:3000 → 容器 8080
|
||
volumes:
|
||
# 微信支付证书(商户私钥 + 微信支付公钥):宿主机 132 的目录只读挂进容器。
|
||
# ⚠️ admin「系统配置 → 支付」里填的路径必须是**容器内路径**(/etc/sundynix/wechat-cert/...),
|
||
# 不是宿主机路径——容器看不到宿主机的 /home/workspace/...。私钥不进镜像、不进 git。
|
||
- /home/workspace/wechat-pay-cert:/etc/sundynix/wechat-cert:ro
|
||
# 微信域名校验文件(MP_verify_xxx.txt):配置 JS安全域名/网页授权域名时微信要求
|
||
# 能从域名根目录访问到。放宿主机、只读挂进来,不进镜像也不进 git。
|
||
- /home/workspace/wechat-verify:/etc/sundynix/wechat-verify:ro
|
||
|
||
dispatcher:
|
||
build: { context: ../.., dockerfile: sundynix-dispatcher/Dockerfile }
|
||
image: sundynix/dispatcher:${TAG:-latest}
|
||
restart: unless-stopped
|
||
environment:
|
||
APP_ENV: production
|
||
NATS_URL: nats://192.168.100.128:4222,nats://192.168.100.128:4223,nats://192.168.100.128:4224
|
||
NATS_STREAM_REPLICAS: "3"
|
||
SUNDYNIX_SECRET_KEY: ${SUNDYNIX_SECRET_KEY:?}
|
||
OTEL_EXPORTER_OTLP_ENDPOINT: http://192.168.100.128:4318
|
||
DISPATCHER_CONCURRENCY: ${DISPATCHER_CONCURRENCY:-8}
|
||
TASK_TOKEN_BUDGET: ${TASK_TOKEN_BUDGET:-200000}
|
||
|
||
mcp-go:
|
||
build: { context: ../.., dockerfile: sundynix-mcp-go/Dockerfile }
|
||
image: sundynix/mcp-go:${TAG:-latest}
|
||
restart: unless-stopped
|
||
environment:
|
||
APP_ENV: production
|
||
NATS_URL: nats://192.168.100.128:4222,nats://192.168.100.128:4223,nats://192.168.100.128:4224
|
||
NATS_STREAM_REPLICAS: "3"
|
||
POSTGRES_DSN: postgres://sundynix:${POSTGRES_PASSWORD:?}@192.168.100.128:5432/sundynix?sslmode=disable
|
||
REDIS_ADDR: 192.168.100.128:6379
|
||
MILVUS_ADDR: 192.168.100.128:19530
|
||
NEO4J_URI: neo4j://192.168.100.128:7687
|
||
NEO4J_USER: neo4j
|
||
NEO4J_PASS: ${NEO4J_PASSWORD:?}
|
||
# 报告源/产物 + KB 正文对象存储(本会话新加 blob)——指向 126,勿漏,否则 fallback localhost 连不上。
|
||
MINIO_ENDPOINT: 192.168.100.126:9000
|
||
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:?}
|
||
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:?}
|
||
MINIO_BUCKET: ${MINIO_BUCKET:-sundynix-docs}
|
||
SUNDYNIX_SECRET_KEY: ${SUNDYNIX_SECRET_KEY:?}
|
||
OTEL_EXPORTER_OTLP_ENDPOINT: http://192.168.100.128:4318
|
||
# 全文(bleve)索引落盘位置。不设则相对 CWD,写进容器可写层 → 每次发版重建容器就清零,
|
||
# 而混合检索只是静默少一路召回、不报错,很难发现。必须配合下面的持久卷。
|
||
BLEVE_PATH: /data/bleve
|
||
volumes:
|
||
- mcp-go-data:/data
|
||
|
||
mcp-py:
|
||
build: { context: ../../sundynix-mcp-py, dockerfile: Dockerfile }
|
||
image: sundynix/mcp-py:${TAG:-latest}
|
||
restart: unless-stopped
|
||
environment:
|
||
NATS_URL: nats://192.168.100.128:4222,nats://192.168.100.128:4223,nats://192.168.100.128:4224
|
||
# secure_sandbox(代码隔离执行)需访问宿主 Docker 拉一次性容器。安全敏感:等于把 132 宿主
|
||
# Docker 暴露给该服务(可逃逸);不需要代码执行工具可注释掉这两行。mcp-py 无对外端口。
|
||
volumes:
|
||
- /var/run/docker.sock:/var/run/docker.sock
|
||
|
||
# 全文索引持久化:随容器重建保留(向量在 Milvus、图谱在 Neo4j,都在 128;
|
||
# 只有 bleve 是跟着 mcp-go 走的本地索引,唯独它需要这个卷)。
|
||
volumes:
|
||
mcp-go-data:
|