merge: feature/sync-wip-2026-09-13 — container env config and chat refresh docs
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
---
|
||||
title: "刷新后对话正文消失问题定位与整改方案"
|
||||
status: done
|
||||
created: 2026-09-13
|
||||
updated: 2026-09-13
|
||||
owner: codex
|
||||
branch: dev
|
||||
worktree: "-"
|
||||
mutex: chat
|
||||
---
|
||||
|
||||
# 刷新后对话正文消失问题定位与整改方案
|
||||
|
||||
## 目标
|
||||
|
||||
定位页面刷新后对话正文不显示、但锚点与过滤计数仍存在的根因,并形成可执行、可验证的整改方案。
|
||||
|
||||
## 范围
|
||||
|
||||
- 前端对话状态、会话恢复、消息窗口、锚点与类别过滤的数据来源
|
||||
- 后端会话/消息持久化与历史读取 API
|
||||
- 当前 dev 运行态的只读接口与数据一致性验证
|
||||
- 审计记录、整改方案与验收标准
|
||||
|
||||
## 非目标
|
||||
|
||||
- 本轮不修改业务代码
|
||||
- 不执行破坏性数据修复
|
||||
- 不把 stub 或仅内存态结果作为最终验收
|
||||
|
||||
## 步骤
|
||||
|
||||
1. [x] 复核刷新前后前端状态初始化和会话选择链路
|
||||
2. [x] 核对历史消息 API、存储字段及运行态返回
|
||||
3. [x] 解释正文、锚点和过滤计数数据源为何分叉
|
||||
4. [x] 输出 audit 与 remediation 文档并回链
|
||||
|
||||
## 验收标准
|
||||
|
||||
- [x] 根因有源码、接口或运行态证据支撑
|
||||
- [x] 明确影响范围、触发条件与风险等级
|
||||
- [x] 整改方案覆盖实现、回归测试、数据兼容和验收
|
||||
|
||||
## 相关链接
|
||||
|
||||
- Checklist: 待整改实施时建立
|
||||
- Handoff: [chat-history-refresh-diagnosis](../../ops/handoffs/2026-09-13-chat-history-refresh-diagnosis.md)
|
||||
- Test report: 待整改实施并验收后建立
|
||||
|
||||
## 证据 / 备注
|
||||
|
||||
- 用户实测:新对话能正常生成文档;刷新后历史对话正文消失,但右侧锚点与顶部过滤仍显示对应消息数量。
|
||||
- Audit: [../../audit/2026-09-13-chat-history-refresh-disappears.md](../../audit/2026-09-13-chat-history-refresh-disappears.md)
|
||||
- Remediation: [../../remediation/2026-09-13-chat-history-refresh-remediation.md](../../remediation/2026-09-13-chat-history-refresh-remediation.md)
|
||||
@@ -0,0 +1,99 @@
|
||||
---
|
||||
title: "刷新后对话正文消失但统计仍存在"
|
||||
status: done
|
||||
created: 2026-09-13
|
||||
updated: 2026-09-13
|
||||
severity: high
|
||||
---
|
||||
|
||||
# 刷新后对话正文消失但统计仍存在
|
||||
|
||||
## 现象
|
||||
|
||||
用户在项目会话中可以正常与助手对话并生成文档;页面刷新后,消息正文区域显示为空,但顶部分类过滤和右侧分类锚点仍显示消息数量及首尾消息 ID。
|
||||
|
||||
结论:**消息没有从数据库删除,而是在刷新后的服务端历史恢复链路中被阶段权限过滤器全部剔除。统计接口没有应用同一权限范围,因此仍返回未过滤的数量和锚点。**
|
||||
|
||||
## 根因分析
|
||||
|
||||
### 1. 刷新前与刷新后使用不同消息来源
|
||||
|
||||
- 刷新前,`web/src/App.tsx:1634-1656` 中的组件内 `messages` 保存本轮实时消息,`useChatWindow` 将它们合并到画面。
|
||||
- 刷新后,组件内 `messages` 从空数组重新开始,正文依赖 `GET /api/v1/chat/messages/window` 恢复。
|
||||
- `web/src/hooks/useChatWindow.ts:112-120` 只要服务端返回非空 `days`,就用返回桶覆盖窗口和本地缓存;即使桶中的 `messages` 已被过滤为空也照常覆盖。
|
||||
|
||||
因此,实时画面正常不代表历史恢复链路正常。
|
||||
|
||||
### 2. 文档任务消息没有绑定工作流阶段
|
||||
|
||||
- 普通聊天路径会在 `internal/chat/session.go:267-274,425-438` 把 `ActiveStageKey` 写入用户与助手消息的 `metadata.stage_key`。
|
||||
- 文档生成使用的异步任务路径在 `internal/server/task_run_handlers.go:81-96` 只写入 `project_id`、`client_turn_id`、`task_key`、`task_status`,没有写 `stage_key`、`workflow_instance_id` 或 `stage_instance_id`。
|
||||
- 任务完成投影 `internal/server/task_finalize.go:58-85` 仍未补这些作用域字段。
|
||||
|
||||
这使得文档任务在当前页面可见,但重载后无法通过阶段可见性判定。
|
||||
|
||||
### 3. 工作流激活前的历史也会被追溯性隐藏
|
||||
|
||||
- `internal/server/stage_access_helpers.go:135-163` 在项目进入受限工作流后,明确丢弃所有 `metadata.stage_key` 为空的历史消息。
|
||||
- `internal/projectinstance/stage_access.go:15-77` 根据当前 live 实例计算可见阶段;策略一旦进入 `Restricted=true`,激活前产生、天然没有阶段键的消息也无法恢复。
|
||||
- `StageKeyAllowed("")` 本身在 `internal/projectinstance/stage_access.go:103-108` 返回允许,但外层过滤器在调用它之前已经对空阶段执行 `continue`;现有单测只验证前者,没有覆盖组合行为。
|
||||
|
||||
这不是合理的“拒绝越权”,而是缺少对 `pre_activation/legacy` 对话作用域的正式建模。
|
||||
|
||||
### 4. 统计与正文的授权口径分裂
|
||||
|
||||
- 窗口和按日正文在 `internal/server/chat_window_api.go:48-50,104-106` 应用阶段过滤。
|
||||
- 分类与日期统计在同文件 `111-155` 直接查询并返回原始统计,没有会话所有权校验,也没有阶段过滤。
|
||||
- `internal/ctxmgr/chat_window.go:622-749` 的 SQL 对会话内全部未删除消息聚合,返回数量及 `MIN/MAX(id)`。
|
||||
- 前端 `CategoryFilterBar` 使用这些服务端统计显示数量;`CategoryDock` 还以统计中的首尾 ID 作为锚点兜底。因此正文为空时,数量与锚点仍存在。
|
||||
|
||||
此外,所有这些端点虽由全局中间件要求登录,但窗口、日期、分类等端点没有统一调用 `assertSessionOwnedByUser`。这会造成私聊会话对象级授权不一致,并可能泄露其他会话的消息存在性、分类数量和消息 ID。
|
||||
|
||||
## 运行态证据(dev,2026-09-13)
|
||||
|
||||
只读查询 `shuimu-context-pg-dev/agent_system_dev`:
|
||||
|
||||
| 项目/会话 | 结果 |
|
||||
|---|---|
|
||||
| project `143` | 创建者 user `9`,live instance `89`,active stage `V0.1_triage` |
|
||||
| session `sess-p143-u9` | 共 4 条未删除消息,4 条正文长度均大于 0 |
|
||||
| 激活前消息 `431-432` | 创建于 instance `89` 之前,无 `stage_key` |
|
||||
| 文档任务消息 `433-434` | 创建于激活之后,`task_key=intake`,仍无 `stage_key` |
|
||||
| 过滤结果 | 4 条均命中“空 stage_key → 丢弃”,窗口日桶保留但消息数组为空 |
|
||||
| 分类统计 | 仍按全量 4 条聚合,可返回数量及首尾 ID |
|
||||
|
||||
全库当前有 live 工作流实例的会话消息统计也显示:所有命中的消息均缺少 `stage_key`。这说明不是单条偶发数据,而是写入链路缺字段与历史兼容策略共同造成的系统性问题。
|
||||
|
||||
## 严重度
|
||||
|
||||
`high`,并应作为发布 blocker:
|
||||
|
||||
- 核心对话记录在刷新/重新登录后不可用,用户会误认为数据丢失;
|
||||
- 文档任务的对话留痕无法恢复,破坏可审计性;
|
||||
- 正文、搜索、日期、统计、锚点的授权集合不一致;
|
||||
- 分类数量和消息 ID 可在对象级授权不完整时形成侧信道泄露。
|
||||
|
||||
## 关联整改
|
||||
|
||||
- Remediation: [../remediation/2026-09-13-chat-history-refresh-remediation.md](../remediation/2026-09-13-chat-history-refresh-remediation.md)
|
||||
- Short-term: [../ops/short-term/2026-09-13-chat-history-refresh-remediation.md](../ops/short-term/2026-09-13-chat-history-refresh-remediation.md)
|
||||
- Gap matrix: [../remediation/gap-matrix.md](../remediation/gap-matrix.md)
|
||||
|
||||
## 证据
|
||||
|
||||
- `web/src/App.tsx:1634-1656,3393-3506`
|
||||
- `web/src/hooks/useChatWindow.ts:81-170,446-578`
|
||||
- `web/src/api/chatWindow.ts:73-164`
|
||||
- `internal/chat/session.go:267-280,425-468`
|
||||
- `internal/server/task_run_handlers.go:76-108`
|
||||
- `internal/server/task_finalize.go:58-96`
|
||||
- `internal/server/stage_access_helpers.go:135-212`
|
||||
- `internal/server/chat_window_api.go:12-155`
|
||||
- `internal/ctxmgr/chat_window.go:39-174,622-749`
|
||||
- dev PostgreSQL 只读查询(未读取或落盘消息正文)
|
||||
- 相关测试通过但缺场景:`go test -short ./internal/server -run 'TestServer_ChatWindow' -count=1 -v`、`go test -short ./internal/ctxmgr -run 'TestChatWindow' -count=1 -v`、`cd web && npm test`
|
||||
|
||||
## 开放问题
|
||||
|
||||
- 激活前项目私聊应默认仅对会话所有者可见,还是按项目成员和退出留痕策略共享,需要在实现前固化为策略测试;本方案建议保持现有“每人独立私聊”语义,优先按会话所有者授权。
|
||||
- 历史上若同一 `task_key` 在多个工作流实例/阶段重复出现,不能仅按当前模板名称盲目回填,必须结合运行实例、创建时间和不可变实例 ID;无法唯一归属的数据应标记为 `legacy_unscoped` 并走显式兼容策略。
|
||||
@@ -13,6 +13,14 @@
|
||||
|
||||
## 核心文档(按阅读顺序推荐)
|
||||
|
||||
### 最新专项审计(2026-09-13)
|
||||
|
||||
**文件**: [2026-09-13-chat-history-refresh-disappears.md](./2026-09-13-chat-history-refresh-disappears.md)
|
||||
|
||||
**结论**: 消息仍在数据库;文档任务与激活前消息缺阶段作用域,被刷新后的窗口过滤器剔除,而统计仍返回未过滤数量。
|
||||
|
||||
**整改**: [对话刷新恢复与阶段授权一致性整改方案](../remediation/2026-09-13-chat-history-refresh-remediation.md)
|
||||
|
||||
### 0️⃣ 当前状态(Live SSOT)
|
||||
|
||||
**文件**: [gap-matrix.md](../remediation/gap-matrix.md)
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
title: "对话刷新后正文消失整改实施交接"
|
||||
created: 2026-09-13
|
||||
from: codex
|
||||
to: next-agent
|
||||
status: open
|
||||
---
|
||||
|
||||
# 交接:对话刷新后正文消失整改实施
|
||||
|
||||
## 目标(下一位 Agent 应完成什么)
|
||||
|
||||
按整改方案修复消息作用域、历史读取授权和统计同集问题,并完成历史回填与真实刷新 E2E。
|
||||
|
||||
## 已完成
|
||||
|
||||
- 源码链路与 dev PostgreSQL 只读定位完成,确认消息未删除。
|
||||
- 确认 project 143/session `sess-p143-u9` 的 4 条消息均缺 `stage_key`,工作流激活后被窗口过滤器全部移除。
|
||||
- 确认异步文档任务写入路径缺阶段/实例作用域;分类与日期统计未应用同一过滤。
|
||||
- 审计、整改方案、发布 blocker 与 planned short-term 已建立。
|
||||
- 现有相关 Go 测试和前端 192 项测试通过,但均未覆盖真实缺陷场景。
|
||||
|
||||
## 未完成
|
||||
|
||||
- 未修改业务代码、迁移或运行态数据。
|
||||
- 未建立实施 checklist/test-report。
|
||||
- 未运行真实 PI 刷新回归和双用户对象级授权负例。
|
||||
|
||||
## 认领 / 槽位状态
|
||||
|
||||
| 项 | 值 |
|
||||
|----|-----|
|
||||
| mutex 已释放 | yes |
|
||||
| 集成槽位 holder | idle |
|
||||
| branch | dev(诊断);建议实施用 `fix/chat-history-refresh` |
|
||||
| worktree | 建议 `.worktrees/fix-chat-history-refresh` |
|
||||
|
||||
## 关键文件
|
||||
|
||||
| 路径 | 说明 |
|
||||
|------|------|
|
||||
| `.doc/audit/2026-09-13-chat-history-refresh-disappears.md` | 根因与运行态证据 |
|
||||
| `.doc/remediation/2026-09-13-chat-history-refresh-remediation.md` | 分波次整改与验收 |
|
||||
| `internal/server/task_run_handlers.go` | 文档任务消息缺阶段作用域 |
|
||||
| `internal/server/stage_access_helpers.go` | 空阶段消息被丢弃 |
|
||||
| `internal/server/chat_window_api.go` | 正文与统计授权口径分裂 |
|
||||
| `web/src/hooks/useChatWindow.ts` | 刷新后服务端空桶覆盖本地窗口 |
|
||||
|
||||
## 如何验证
|
||||
|
||||
```bash
|
||||
go test -short ./internal/server -run 'TestServer_ChatWindow' -count=1 -v
|
||||
go test -short ./internal/ctxmgr -run 'TestChatWindow' -count=1 -v
|
||||
cd web && npm test
|
||||
```
|
||||
|
||||
实施时必须另加真实 PG 场景:激活前对话 → 激活 → `intake` 文档任务 → 刷新窗口,以及非 owner/无阶段职责负例。
|
||||
|
||||
## 风险 / Blocker
|
||||
|
||||
- 当前为发布 blocker;刷新会造成对话“数据丢失”假象。
|
||||
- 不能用“空 `stage_key` 全部放行”或保留旧 localStorage 缓存作修复,否则会扩大阶段/跨用户泄露。
|
||||
- 历史回填存在重复 `task_key` 歧义,必须先 dry-run 并按实例与时间唯一匹配。
|
||||
|
||||
## 相关链接
|
||||
|
||||
- CURRENT: [CURRENT.md](../CURRENT.md)
|
||||
- Task: [chat-history-refresh-remediation](../short-term/2026-09-13-chat-history-refresh-remediation.md)
|
||||
- Audit: [chat-history-refresh-disappears](../../audit/2026-09-13-chat-history-refresh-disappears.md)
|
||||
- Remediation: [chat-history-refresh-remediation](../../remediation/2026-09-13-chat-history-refresh-remediation.md)
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
title: "修复对话刷新恢复与授权统计不一致"
|
||||
status: planned
|
||||
created: 2026-09-13
|
||||
updated: 2026-09-13
|
||||
owner: unassigned
|
||||
branch: fix/chat-history-refresh
|
||||
worktree: ".worktrees/fix-chat-history-refresh"
|
||||
mutex: chat
|
||||
---
|
||||
|
||||
# 修复对话刷新恢复与授权统计不一致
|
||||
|
||||
## 目标
|
||||
|
||||
按整改方案修复消息作用域写入、历史兼容、读取授权和前端恢复态,并完成真实 PostgreSQL 与 PI 文档生成后的刷新验收。
|
||||
|
||||
## 范围
|
||||
|
||||
- 消息和 task run 的不可变工作流实例/阶段作用域
|
||||
- 会话 owner 与阶段可见性的统一服务端策略
|
||||
- 窗口、日期、分类、搜索、摘要、未读、导出、锚点的同集查询
|
||||
- 现有消息 dry-run/backfill
|
||||
- 前端窗口错误态和用户隔离缓存
|
||||
- 自动化与真实 PI E2E 证据
|
||||
|
||||
## 非目标
|
||||
|
||||
- 不放宽现有阶段可见性规则
|
||||
- 不用前端缓存替代服务端持久化
|
||||
- 不对无法唯一归属的历史消息盲目回填当前阶段
|
||||
|
||||
## 步骤
|
||||
|
||||
1. [ ] 先补可稳定复现本缺陷的 PG 集成与前端恢复测试
|
||||
2. [ ] 实现统一消息作用域解析与写入
|
||||
3. [ ] 实现统一会话/消息授权查询及统计同集
|
||||
4. [ ] 实现历史 dry-run/backfill 与对账
|
||||
5. [ ] 完善前端恢复状态及用户隔离缓存
|
||||
6. [ ] 执行全量门禁、真实 PI 刷新 E2E 和双用户负例
|
||||
|
||||
## 验收标准
|
||||
|
||||
- [ ] [整改方案](../../remediation/2026-09-13-chat-history-refresh-remediation.md) 的验收项全部有证据
|
||||
- [ ] `CHAT-HISTORY-REFRESH` 在 gap-matrix 中由 FAIL 更新为 PASS
|
||||
- [ ] 测试报告归档并回链
|
||||
|
||||
## 相关链接
|
||||
|
||||
- Checklist: 实施开始时复制模板建立
|
||||
- Handoff: [../handoffs/2026-09-13-chat-history-refresh-diagnosis.md](../handoffs/2026-09-13-chat-history-refresh-diagnosis.md)
|
||||
- Test report: 待实施
|
||||
|
||||
## 证据 / 备注
|
||||
|
||||
- Audit: [../../audit/2026-09-13-chat-history-refresh-disappears.md](../../audit/2026-09-13-chat-history-refresh-disappears.md)
|
||||
- Remediation: [../../remediation/2026-09-13-chat-history-refresh-remediation.md](../../remediation/2026-09-13-chat-history-refresh-remediation.md)
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
title: "对话刷新恢复与阶段授权一致性整改方案"
|
||||
status: planned
|
||||
created: 2026-09-13
|
||||
updated: 2026-09-13
|
||||
---
|
||||
|
||||
# 对话刷新恢复与阶段授权一致性整改方案
|
||||
|
||||
## 背景
|
||||
|
||||
审计确认消息正文仍在 PostgreSQL,但文档任务消息和激活前历史缺少工作流阶段作用域;刷新后的窗口接口将其全部过滤,而分类/日期统计仍返回全量结果。详见 [审计记录](../audit/2026-09-13-chat-history-refresh-disappears.md)。
|
||||
|
||||
## 目标
|
||||
|
||||
1. 同一登录用户在权限未变化时,刷新、重新登录和服务重启后看到一致的对话正文、数量、日期、搜索和锚点。
|
||||
2. 每条新消息在服务端落库时拥有可审计的会话、项目及工作流作用域;客户端不得决定该作用域。
|
||||
3. 所有历史读取入口使用同一对象级与阶段级授权规则,统计集合必须与正文集合一致。
|
||||
4. 安全回填现有可唯一归属的消息,不丢失正文,不扩大不可确定历史的可见范围。
|
||||
|
||||
## 分波次计划
|
||||
|
||||
| 波次 | 内容 | 验收 |
|
||||
|------|------|------|
|
||||
| Wave 0:锁定回归 | 增加失败测试:激活前聊天 → 激活工作流 → 运行 `intake` 文档任务 → 刷新拉窗口;同时覆盖非 owner、阶段无职责用户、空阶段消息和统计一致性 | 修复前测试稳定复现“窗口 0 条、分类 4 条”;记录真实 PG 证据 |
|
||||
| Wave 1:统一服务端作用域 | 在任务创建事务中由服务端从冻结实例解析 `task_key → workflow_instance_id → stage_instance_id/stage_key`;把不可变作用域写入 `task_runs` 和该回合两条 `chat_messages`;普通 chat 同样写实例 ID;finalize 只合并结果,不覆盖作用域 | 新建普通聊天和任务消息均具备正确实例/阶段;伪造客户端 stage 无效;找不到唯一阶段时拒绝启动并给出结构化错误 |
|
||||
| Wave 2:修正授权查询 | 建立统一的 `AuthorizeSessionRead + visible-message predicate`,窗口、day、dates、categories、search、digest、unread、export、锚点加载全部复用;私聊先校验 owner,再计算阶段范围;统计在 SQL 层对相同可见集合聚合 | 正文、数量、首尾 ID、搜索/导出完全同集;他人 session 返回 403/404;不可见 ID 不能作锚点探测 |
|
||||
| Wave 3:历史迁移 | 新增 Goose 迁移或一次性受控 backfill:任务消息通过 `task_key + task_run/workflow_run + 时间区间` 回填实例/阶段;激活前 owner 私聊标记 `scope_kind=pre_activation`;歧义数据标记 `legacy_unscoped` 并记录数量,不猜测归属 | dry-run 报告、事务执行、前后计数与 hash 对账;project 143 的 4 条消息可按策略恢复;零正文删除 |
|
||||
| Wave 4:前端恢复态 | `useChatWindow` 区分 loading / authorized-empty / forbidden / error;不把“带空消息的日桶”静默呈现为空白;缓存键绑定 user/session/authorization revision,权限变化时清除,不用旧缓存绕过服务端 | 刷新不闪现上一用户/无权缓存;错误有可操作提示;服务端正常时首屏稳定落底 |
|
||||
| Wave 5:全链验收 | 在空库和升级库执行 PG 集成;完成真实 PI 文档生成、浏览器刷新、重登和服务重启恢复;执行双用户/跨阶段负例 | 所有自动化门禁通过并归档 test-report;人工抽检截图/网络响应入证据 |
|
||||
|
||||
## 推荐数据模型与边界
|
||||
|
||||
- `chat_messages` 增加正式列或受约束字段:`workflow_instance_id`、`stage_instance_id`、`scope_kind`;`stage_key` 可作为展示/索引冗余,不应成为唯一授权依据。
|
||||
- `task_runs` 同样保存 `workflow_instance_id`、`stage_instance_id`,确保任务运行期间阶段推进也不会改变该回合归属。
|
||||
- `scope_kind` 至少区分 `workflow_stage`、`pre_activation`、`legacy_unscoped`、`non_project_private`。
|
||||
- 消息作用域只能由服务端从会话、项目与冻结工作流实例解析;忽略客户端提交的 user/stage/instance 身份字段。
|
||||
- 不建议用“空 `stage_key` 一律允许”快速修复,这会绕过阶段隔离;也不建议仅在前端保留本地缓存,这会掩盖服务端错误并产生共享浏览器泄露风险。
|
||||
|
||||
## 建议修改范围
|
||||
|
||||
- 后端写入:`internal/server/task_run_handlers.go`、`internal/server/task_finalize.go`、`internal/chat/session.go`、`internal/taskrun/*`
|
||||
- 后端读取与授权:`internal/server/chat_window_api.go`、`internal/server/chat_search_api.go`、chat digest/unread/export handlers、`internal/server/stage_access_helpers.go`、`internal/ctxmgr/chat_window.go`
|
||||
- 工作流解析:`internal/projectinstance/*`,增加按冻结实例解析消息作用域的单一入口
|
||||
- 前端:`web/src/hooks/useChatWindow.ts`、`web/src/api/chatWindow.ts`、`web/src/components/chat/MessageStream.tsx`
|
||||
- 数据库:`deploy/postgres/goose/00026_*`(实际序号以实施时仓库为准)及对应 dry-run/rollback 说明
|
||||
|
||||
## 验收标准
|
||||
|
||||
- [ ] 创建项目会话并发送普通消息,刷新后用户/助手两条正文、顺序、类别和锚点不变。
|
||||
- [ ] 先聊天再激活工作流,刷新后激活前 owner 私聊仍按既定策略可见。
|
||||
- [ ] 激活后通过后台任务生成文档,任务用户消息和最终助手消息刷新后完整恢复,artifact 仍可打开。
|
||||
- [ ] 工作流推进后,已参与阶段历史按策略可见,未参与阶段消息不可见。
|
||||
- [ ] `window/day/dates/categories/search/digest/unread/export` 对同一用户返回同一可见集合;统计总数等于可枚举正文数。
|
||||
- [ ] 非会话 owner 即使猜到 `session_id/message_id`,所有读取与锚点入口均返回 403/404,且不泄露计数。
|
||||
- [ ] 浏览器刷新、重新登录、切换用户、服务重启均不依赖未授权 localStorage 缓存恢复正文。
|
||||
- [ ] 历史迁移 dry-run 与执行报告包含总数、可唯一归属数、歧义数、前后校验;正文删除数为 0。
|
||||
- [ ] `go vet ./...`、`go test -short ./...`、PG 集成、`cd web && npm test && npm run build` 全绿。
|
||||
- [ ] 真实 PI Agent 文档生成后刷新恢复 E2E 有控制台/网络/数据库证据并归档 `.doc/test-reports/`。
|
||||
|
||||
## 相关链接
|
||||
|
||||
- Audit: [../audit/2026-09-13-chat-history-refresh-disappears.md](../audit/2026-09-13-chat-history-refresh-disappears.md)
|
||||
- Short-term: [../ops/short-term/2026-09-13-chat-history-refresh-remediation.md](../ops/short-term/2026-09-13-chat-history-refresh-remediation.md)
|
||||
- gap-matrix 行: `CHAT-HISTORY-REFRESH`
|
||||
@@ -3,6 +3,7 @@
|
||||
| 文档 | 说明 |
|
||||
|------|------|
|
||||
| [gap-matrix.md](./gap-matrix.md) | 发布缺口 SSOT |
|
||||
| [2026-09-13-chat-history-refresh-remediation.md](./2026-09-13-chat-history-refresh-remediation.md) | 对话刷新恢复、消息作用域与授权统计一致性 |
|
||||
| [2026-09-12-comprehensive-audit-and-remediation-plan.md](./2026-09-12-comprehensive-audit-and-remediation-plan.md) | 综合缺陷与整改 |
|
||||
| [2026-09-12-remediation-tasks-and-acceptance-checklist.md](./2026-09-12-remediation-tasks-and-acceptance-checklist.md) | 任务拆分与验收 |
|
||||
| [2026-09-12-quick-reference-guide.md](./2026-09-12-quick-reference-guide.md) | 症状→原因→方案速查 |
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
**更新**: 2026-09-13
|
||||
**分支**: dev
|
||||
**production_ready**: no(CI 真实 PI E2E / 前端人工抽检未完成;main 本地冒烟已通过)
|
||||
**production_ready**: no(对话刷新恢复缺陷、CI 真实 PI E2E / 前端人工抽检未完成;main 本地冒烟已通过)
|
||||
**协作焦点**: [../ops/CURRENT.md](../ops/CURRENT.md)
|
||||
**Agent 手册**: [AGENTS.md](../AGENTS.md) · [PROJECT.md](../PROJECT.md)
|
||||
|
||||
@@ -53,6 +53,12 @@
|
||||
| DOC-SSOT | 文档叙事漂移 | PASS | 本矩阵 + superseded 标记 + ops |
|
||||
| OPS-AGENTS | 多 Agent 协作规范 | PASS | `AGENTS.md` / `PROJECT.md` / `.doc/ops` |
|
||||
|
||||
## 2026-09-13 现场问题
|
||||
|
||||
| ID | 描述 | 状态 | 证据 | 计划动作 |
|
||||
|---|---|---|---|---|
|
||||
| CHAT-HISTORY-REFRESH | 工作流激活后刷新,对话正文被阶段过滤器清空,但分类数量与锚点仍按全量返回 | **FAIL** | [audit](../audit/2026-09-13-chat-history-refresh-disappears.md):project 143/session `sess-p143-u9` 的 4 条正文仍在 DB、全部缺 `stage_key` | [remediation](./2026-09-13-chat-history-refresh-remediation.md):统一作用域写入、对象/阶段授权、同集统计、历史回填和真实 PI 刷新 E2E |
|
||||
|
||||
---
|
||||
|
||||
## 发布门禁(test → main)
|
||||
@@ -62,10 +68,11 @@
|
||||
☑ go test -short ./... 全绿
|
||||
□ TEST_DATABASE_URL 下治理 PG 集成全绿(CI)
|
||||
□ 真实 PI Agent 三工作流治理 E2E 实录(本轮 stub/short 已绿,PI 待 CI token)
|
||||
□ 对话刷新恢复:激活前聊天 + 激活后文档任务 + 刷新/重登/重启可恢复,双用户负例通过
|
||||
☑ lifecycle fix SQL 已在 dev 执行
|
||||
☑ test/dev health schema_version=25
|
||||
☑ main 环境部署冒烟(本地 8000 health/ready schema 25)
|
||||
□ 前端:版本冲突刷新、终止 preview 人工抽检
|
||||
```
|
||||
|
||||
**结论**: P0 授权、冷启动序列、Go 门禁、三环境本地冒烟已闭环。**发布 blocker 剩余 CI 真实 PI E2E 与前端人工抽检**。
|
||||
**结论**: P0 授权、冷启动序列、Go 门禁、三环境本地冒烟已闭环;新增现场缺陷 `CHAT-HISTORY-REFRESH`。**发布 blocker 包括对话刷新恢复、CI 真实 PI E2E 与前端人工抽检**。
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
| 模块 | 能力 | 状态 | 证据 | 缺口/后续 | mutex |
|
||||
|------|------|------|------|-----------|-------|
|
||||
| 意图识别 L1 | 18 类 Intent + 三分支消解 | done | [system-design/intent-recognition](../system-design/intent-recognition/);[test-report 2026-09-07](../test-reports/2026-09-07-three-branch-intent-framework-test-report.md) | — | `intent` |
|
||||
| 对话 / ctxmgr | 多轮上下文、记忆、摘要 | partial | [system-design/multi-turn-dialogue](../system-design/multi-turn-dialogue/);P0 断链已修 | 语义摘要(D5 backlog);长任务治理 | `chat` |
|
||||
| 对话 / ctxmgr | 多轮上下文、记忆、摘要 | partial | [system-design/multi-turn-dialogue](../system-design/multi-turn-dialogue/);[刷新恢复专项审计](../audit/2026-09-13-chat-history-refresh-disappears.md) | `CHAT-HISTORY-REFRESH`:作用域写入、同集授权统计、历史回填;语义摘要(D5 backlog) | `chat` |
|
||||
| 智能体小队 | 角色-任务-工作区-黑板 | done | [system-design/agent-squad](../system-design/agent-squad/);[test 2026-09-07](../test-reports/2026-09-07-dynamic-squad-planning-test-report.md) | 非关键步骤降级(A5/A6 backlog) | `squad` |
|
||||
| 工作流治理 | 三正式工作流 + 阶段门禁 | done | [remediation/three-workflows](../remediation/2026-09-12-three-workflows-strict-governance-remediation.md);[test 2026-09-12](../test-reports/2026-09-12-three-workflows-governance-remediation-test-report.md) | Phase2 设计器 partial | `workflow` |
|
||||
| IAM / 鉴权 | 项目级 API 鉴权 + RBAC | done | [audit/authorization](../audit/authorization-matrix-v2.md);[test phase1](../test-reports/2026-09-12-phase1-iam-authz-test-report.md) | 旧 API 410 标注(BUG-11 partial) | `authz` |
|
||||
| IAM / 鉴权 | 项目级 API 鉴权 + RBAC | partial | [audit/authorization](../audit/authorization-matrix-v2.md);[刷新恢复专项审计](../audit/2026-09-13-chat-history-refresh-disappears.md) | 历史读取端点补统一会话 owner + 阶段授权;旧 API 410 标注(BUG-11 partial) | `authz` |
|
||||
| 文档治理 | 法定文档生命周期 + 租约 | done | [test docgov](../test-reports/2026-09-08-docgov-lifecycle-and-claiming-framework-test-report.md) | — | `docgov` |
|
||||
| 前端工作台 | 三栏 + 块文档 + SSE | partial | [test workbench](../test-reports/2026-09-08-three-column-workbench-and-block-document-test-report.md) | 人工抽检待完成 | `web` |
|
||||
| PI Runner / 配置链 | pi-agent + MiniMax 真实链路 | done | [test piagent](../test-reports/2026-09-13-codex-audit-p0-remediation-test-report.md);[container-env](../test-reports/2026-09-13-container-env-configuration-test-report.md) | — | `pi-agent` |
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
.doc
|
||||
docs
|
||||
data
|
||||
.regulations
|
||||
.tmp
|
||||
web/node_modules
|
||||
web/dist
|
||||
web/.vite
|
||||
|
||||
+17
-14
@@ -3,14 +3,12 @@
|
||||
#
|
||||
# 使用说明:
|
||||
# 1. 复制为 .env 并填写密钥:cp .env.example .env && chmod 600 .env
|
||||
# 2. 本文件供 Go 后端 (bin/server)、launch.sh 默认 compose 变量替换使用
|
||||
# 3. 若通过 ./scripts/env-service.sh dev start 启容器,还需同步密钥到 configs/.env.dev
|
||||
# (见 configs/.env.dev.example 第 6 节)
|
||||
# 2. 本文件供 Go 后端、launch.sh 与 env-service.sh 作为公共配置源
|
||||
# 3. configs/.env.<环境> 只保存端口、数据库等环境差异,且优先级高于本文件
|
||||
# =============================================================================
|
||||
|
||||
# --- 1. 运行环境与基础目录 ---
|
||||
ENV=dev
|
||||
COMPOSE_PROJECT_NAME=agent-dev
|
||||
DATA_DIR=./data/dev
|
||||
APP_PUBLISH_HOST=127.0.0.1
|
||||
HTTP_PORT=8080
|
||||
@@ -42,7 +40,7 @@ PI_AGENT_RUNNER_PORT=18865
|
||||
# 宿主 bin/server 直连本机 pi-agent 容器时启用;容器内 app 服务由 compose 注入 http://pi-agent:18865
|
||||
# PI_AGENT_RUNNER_URL=http://127.0.0.1:18865
|
||||
# Runner 服务间认证(main 必填;dev 可留空)
|
||||
# RUNNER_AUTH_TOKEN=
|
||||
RUNNER_AUTH_TOKEN=
|
||||
|
||||
# --- 7. 本机 Pi CLI 回退(不使用容器 runner 时)---
|
||||
PI_AGENT_BIN=/opt/homebrew/bin/pi
|
||||
@@ -54,17 +52,15 @@ PI_AGENT_MODEL=MiniMax-M2.7-highspeed
|
||||
KB_DB_HOST=127.0.0.1
|
||||
KB_DB_PORT=5432
|
||||
KB_DB_USER=regkb
|
||||
KB_DB_PASSWORD=shared_kb_secret_pass
|
||||
KB_DB_PASSWORD=
|
||||
KB_DB_NAME=regulations
|
||||
|
||||
# --- 9. 向量 Embedding / Reranker(Go 宿主 KB 检索;容器 app 当前未透传,宿主模式必填)---
|
||||
# --- 9. 向量 Embedding / Reranker(Go 宿主与容器 app 的 KB 检索)---
|
||||
EMBEDDING_ENDPOINT_URL=https://www.dmxapi.cn/v1/embeddings
|
||||
EMBEDDING_PROVIDER=openai_compatible
|
||||
EMBEDDING_MODEL=bge-m3
|
||||
EMBEDDING_API_KEY=
|
||||
|
||||
RERANKER_ENDPOINT_URL=https://www.dmxapi.cn/v1/rerank
|
||||
RERANKER_PROVIDER=openai_compatible
|
||||
RERANKER_MODEL=bge-reranker-v2-m3-free
|
||||
RERANKER_API_KEY=
|
||||
|
||||
@@ -87,8 +83,15 @@ ADMIN_PASSWORD=admin-secret-password-123
|
||||
AUTH_REGISTRATION_ENABLED=true
|
||||
AUTH_REQUIRE_APPROVAL=false
|
||||
|
||||
# --- 12. 可选:CORS / 内存降级 ---
|
||||
# dev/test 未配置时 CORS 由代码填充 localhost 默认值
|
||||
# CORS_ALLOWED_ORIGINS=http://localhost:5173,http://127.0.0.1:8080
|
||||
# 生产必须为 false;main 启动时若 true 将拒绝加载
|
||||
# ALLOW_IN_MEMORY=false
|
||||
# --- 12. 安全、缓存与意图路由开关 ---
|
||||
# 留空时 dev/test 由代码补 localhost;生产留空代表不允许跨 Origin
|
||||
CORS_ALLOWED_ORIGINS=
|
||||
# 生产强制 false;main compose 不接受覆盖
|
||||
ALLOW_IN_MEMORY=false
|
||||
CACHE_ENABLED=true
|
||||
INTENT_V2_SHADOW=false
|
||||
INTENT_V2_DECISION=false
|
||||
INTENT_V2_DECISION_TIER=none
|
||||
WORKFLOW_INTENTS_ENABLED=true
|
||||
MULTI_INTENT_ENABLED=true
|
||||
SEMANTIC_FALLBACK_ENABLED=false
|
||||
|
||||
@@ -1,25 +1,18 @@
|
||||
# =============================================================================
|
||||
# 开发环境 Compose 专用配置模板 (configs/.env.dev)
|
||||
# 作用域:./scripts/env-service.sh dev * 与 docker compose --env-file configs/.env.dev
|
||||
# 作用域:./scripts/env-service.sh dev * 的环境差异覆盖
|
||||
#
|
||||
# 重要:本文件与根目录 .env 分工不同
|
||||
# - 根 .env → Go bin/server、launch.sh 默认 compose
|
||||
# - 本文件 → env-service.sh 启停 dev 容器组
|
||||
# 密钥(MINIMAX_API_KEY / TAVILY_API_KEYS 等)须与根 .env 保持一致,否则 pi-agent 容器无 Token!
|
||||
# 根 .env 提供公共模型、检索、认证配置;本文件只覆盖 dev 差异。
|
||||
# =============================================================================
|
||||
|
||||
ENV=dev
|
||||
COMPOSE_PROJECT_NAME=agent-dev
|
||||
|
||||
# 1. 业务 HTTP 服务
|
||||
HTTP_PORT=8080
|
||||
APP_PUBLISH_HOST=127.0.0.1
|
||||
ADMIN_TOKEN=dev-admin-token-change-me
|
||||
ALLOW_INSECURE_ADMIN=1
|
||||
|
||||
# 2. 对话数据与工作区(禁止跨环境共享)
|
||||
DATA_DIR=./data/dev
|
||||
AUX_FILE_DSN=file:/var/lib/feishu/outbox_dev.db?_journal_mode=WAL&_busy_timeout=5000
|
||||
|
||||
# 3. 开发环境专属数据库
|
||||
SYSTEM_DB_USER=app
|
||||
@@ -29,7 +22,6 @@ SYSTEM_DB_PUBLISH_PORT=5433
|
||||
|
||||
# 4. 共享知识库(容器 app 经 shuimu-kb-shared 访问)
|
||||
KB_DB_USER=regkb
|
||||
KB_DB_PASSWORD=shared_kb_secret_pass
|
||||
KB_DB_NAME=regulations
|
||||
KB_DB_HOST=shuimu-kb-shared
|
||||
KB_DB_PORT=5432
|
||||
@@ -40,12 +32,4 @@ PI_AGENT_RUNNER_PORT=18865
|
||||
AGENT_BACKEND=pi-agent
|
||||
AGENT_TIMEOUT=60m
|
||||
|
||||
# 6. Pi-Agent / MiniMax / Tavily(pi-agent 容器必填;从根 .env 复制)
|
||||
MINIMAX_ENDPOINT_URL=https://api.minimaxi.com/v1
|
||||
MINIMAX_API_KEY=
|
||||
MINIMAX_MODEL_ID=MiniMax-M2.7-highspeed
|
||||
MINIMAX_API_TYPE=openai-compatible
|
||||
PI_AGENT_PROVIDER=minimax
|
||||
PI_AGENT_MODEL=MiniMax-M2.7-highspeed
|
||||
TAVILY_API_KEYS=
|
||||
# RUNNER_AUTH_TOKEN=
|
||||
# 6. Pi-Agent / MiniMax / Tavily / Runner 密钥统一读取根 .env
|
||||
|
||||
@@ -1,39 +1,37 @@
|
||||
# =============================================================================
|
||||
# 生产/主分支环境 (main) 环境变量配置模板 (configs/.env.main)
|
||||
# 作用域:main 分支正式线上发布与生产运营
|
||||
# 根 .env 提供公共模型与检索配置;本文件覆盖生产端口、数据库和生产密钥。
|
||||
# =============================================================================
|
||||
|
||||
ENV=prod
|
||||
COMPOSE_PROJECT_NAME=agent-main
|
||||
|
||||
# 1. 业务 HTTP 服务
|
||||
HTTP_PORT=8000
|
||||
APP_PUBLISH_HOST=0.0.0.0
|
||||
ADMIN_TOKEN=main-prod-admin-token-super-secure
|
||||
ALLOW_INSECURE_ADMIN=0
|
||||
AUTH_REGISTRATION_ENABLED=false
|
||||
AUTH_REQUIRE_APPROVAL=true
|
||||
|
||||
# 2. 对话数据与工作区隔离目录 (禁止跨环境共享)
|
||||
DATA_DIR=./data/main
|
||||
AUX_FILE_DSN=file:/var/lib/feishu/outbox_main.db?_journal_mode=WAL&_busy_timeout=5000
|
||||
|
||||
# 3. 生产环境专属数据库 (禁止与 dev/test 共享)
|
||||
SYSTEM_DB_USER=app
|
||||
SYSTEM_DB_PASSWORD=prod_highly_secure_db_pass
|
||||
SYSTEM_DB_PASSWORD=
|
||||
SYSTEM_DB_NAME=agent_system_main
|
||||
SYSTEM_DB_PUBLISH_PORT=5435
|
||||
|
||||
# 4. 共享知识库连接 (只读检索共享数据)
|
||||
KB_DB_USER=regkb
|
||||
KB_DB_PASSWORD=shared_kb_secret_pass
|
||||
KB_DB_NAME=regulations
|
||||
KB_DB_HOST=shuimu-kb-shared
|
||||
KB_DB_PORT=5432
|
||||
|
||||
# 5. Cursor / LLM Agent Runner 服务
|
||||
CURSOR_RUNNER_PORT=18767
|
||||
AGENT_BACKEND=stub
|
||||
PI_AGENT_RUNNER_PORT=18867
|
||||
AGENT_BACKEND=pi-agent
|
||||
AGENT_TIMEOUT=60m
|
||||
# 生产必填(不可使用 shared_kb_secret_pass / admin-secret-password-123 等默认值)
|
||||
RUNNER_AUTH_TOKEN=change-me-runner-token
|
||||
KB_DB_PASSWORD=change-me-kb-prod-secret
|
||||
ADMIN_PASSWORD=change-me-admin-prod-secret
|
||||
RUNNER_AUTH_TOKEN=
|
||||
ADMIN_PASSWORD=
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
# =============================================================================
|
||||
# 共享基础设施环境变量配置模板 (configs/.env.shared)
|
||||
# 作用域:所有分支与环境共享的知识库、向量检索与底层公共网络
|
||||
# KB_DB_PASSWORD 统一在根 .env 配置,本文件只覆盖共享服务差异。
|
||||
# =============================================================================
|
||||
|
||||
COMPOSE_PROJECT_NAME=agent-infra
|
||||
|
||||
# 共享知识库 PostgreSQL (带 pgvector)
|
||||
KB_DB_USER=regkb
|
||||
KB_DB_PASSWORD=shared_kb_secret_pass
|
||||
KB_DB_NAME=regulations
|
||||
KB_PUBLISH_PORT=5432
|
||||
KB_DATA_DIR=./data/shared/kb
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
# =============================================================================
|
||||
# 测试环境 (test) 环境变量配置模板 (configs/.env.test)
|
||||
# 作用域:test 分支阶段集成、长期部署实测与运维验证
|
||||
# 根 .env 提供公共模型、检索、认证配置;本文件只覆盖 test 差异。
|
||||
# =============================================================================
|
||||
|
||||
ENV=test
|
||||
COMPOSE_PROJECT_NAME=agent-test
|
||||
|
||||
# 1. 业务 HTTP 服务
|
||||
HTTP_PORT=8081
|
||||
APP_PUBLISH_HOST=0.0.0.0
|
||||
ADMIN_TOKEN=test-admin-token-secure-key
|
||||
ALLOW_INSECURE_ADMIN=0
|
||||
|
||||
# 2. 对话数据与工作区隔离目录 (禁止跨环境共享)
|
||||
DATA_DIR=./data/test
|
||||
AUX_FILE_DSN=file:/var/lib/feishu/outbox_test.db?_journal_mode=WAL&_busy_timeout=5000
|
||||
|
||||
# 3. 测试环境专属数据库 (禁止与 dev/main 共享)
|
||||
SYSTEM_DB_USER=app
|
||||
@@ -24,7 +21,6 @@ SYSTEM_DB_PUBLISH_PORT=5434
|
||||
|
||||
# 4. 共享知识库连接 (只读检索共享数据)
|
||||
KB_DB_USER=regkb
|
||||
KB_DB_PASSWORD=shared_kb_secret_pass
|
||||
KB_DB_NAME=regulations
|
||||
KB_DB_HOST=shuimu-kb-shared
|
||||
KB_DB_PORT=5432
|
||||
@@ -32,7 +28,7 @@ KB_DB_PORT=5432
|
||||
# 5. Cursor / LLM Agent Runner 服务
|
||||
CURSOR_RUNNER_PORT=18766
|
||||
PI_AGENT_RUNNER_PORT=18866
|
||||
AGENT_BACKEND=stub
|
||||
AGENT_BACKEND=pi-agent
|
||||
AGENT_TIMEOUT=60m
|
||||
# app 与 pi-agent 容器共享(test 环境建议设置非空值)
|
||||
# RUNNER_AUTH_TOKEN=test-runner-shared-secret
|
||||
|
||||
+22
-4
@@ -60,7 +60,6 @@ services:
|
||||
MINIMAX_API_TYPE: ${MINIMAX_API_TYPE:-openai-compatible}
|
||||
MINIMAX_PROVIDER: minimax
|
||||
TAVILY_API_KEYS: ${TAVILY_API_KEYS:-}
|
||||
TAVILY_API_KEY: ${TAVILY_API_KEY:-}
|
||||
RUNNER_AUTH_TOKEN: ${RUNNER_AUTH_TOKEN:-}
|
||||
ports:
|
||||
- "127.0.0.1:${PI_AGENT_RUNNER_PORT:-18865}:18865"
|
||||
@@ -94,19 +93,38 @@ services:
|
||||
HTTP_PORT: "${HTTP_PORT:-8080}"
|
||||
HTTP_HOST: "0.0.0.0"
|
||||
AGENT_BACKEND: ${AGENT_BACKEND:-pi-agent}
|
||||
AGENT_TIMEOUT: ${AGENT_TIMEOUT:-30m}
|
||||
MAX_CONCURRENT_AGENTS: ${MAX_CONCURRENT_AGENTS:-8}
|
||||
DATA_DIR: /app/data
|
||||
# 独占业务与对话库
|
||||
APP_DB_DSN: postgres://${SYSTEM_DB_USER:-app}:${SYSTEM_DB_PASSWORD:-dev_secret_db_pass}@system-db:5432/${SYSTEM_DB_NAME:-agent_system_dev}?sslmode=disable
|
||||
# 共享知识库
|
||||
POSTGRES_SHARED_KB_DSN: postgres://${KB_DB_USER:-regkb}:${KB_DB_PASSWORD:-shared_kb_secret_pass}@shuimu-kb-shared:5432/${KB_DB_NAME:-regulations}?sslmode=disable
|
||||
POSTGRES_SHARED_KB_DSN: postgres://${KB_DB_USER:-regkb}:${KB_DB_PASSWORD:?KB_DB_PASSWORD is required}@shuimu-kb-shared:5432/${KB_DB_NAME:-regulations}?sslmode=disable
|
||||
CURSOR_RUNNER_URL: http://cursor-agent:18765
|
||||
PI_AGENT_RUNNER_URL: http://pi-agent:18865
|
||||
PI_AGENT_RUNNER_AUTH_TOKEN: ${RUNNER_AUTH_TOKEN:-}
|
||||
PI_AGENT_PROVIDER: ${PI_AGENT_PROVIDER:-minimax}
|
||||
PI_AGENT_MODEL: ${PI_AGENT_MODEL:-MiniMax-M2.7-highspeed}
|
||||
RUNNER_AUTH_TOKEN: ${RUNNER_AUTH_TOKEN:-}
|
||||
ALLOW_IN_MEMORY: "false"
|
||||
AUX_FILE_DSN: file:/var/lib/feishu/outbox_dev.db?_journal_mode=WAL&_busy_timeout=5000
|
||||
EMBEDDING_ENDPOINT_URL: ${EMBEDDING_ENDPOINT_URL:-}
|
||||
EMBEDDING_MODEL: ${EMBEDDING_MODEL:-bge-m3}
|
||||
EMBEDDING_API_KEY: ${EMBEDDING_API_KEY:-}
|
||||
RERANKER_ENDPOINT_URL: ${RERANKER_ENDPOINT_URL:-}
|
||||
RERANKER_MODEL: ${RERANKER_MODEL:-bge-reranker-v2-m3-free}
|
||||
RERANKER_API_KEY: ${RERANKER_API_KEY:-}
|
||||
ADMIN_USERNAME: ${ADMIN_USERNAME:-admin}
|
||||
ADMIN_PASSWORD: ${ADMIN_PASSWORD:-admin-secret-password-123}
|
||||
AUTH_REGISTRATION_ENABLED: ${AUTH_REGISTRATION_ENABLED:-true}
|
||||
AUTH_REQUIRE_APPROVAL: ${AUTH_REQUIRE_APPROVAL:-false}
|
||||
CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS:-}
|
||||
ALLOW_IN_MEMORY: ${ALLOW_IN_MEMORY:-false}
|
||||
CACHE_ENABLED: ${CACHE_ENABLED:-true}
|
||||
INTENT_V2_SHADOW: ${INTENT_V2_SHADOW:-false}
|
||||
INTENT_V2_DECISION: ${INTENT_V2_DECISION:-false}
|
||||
INTENT_V2_DECISION_TIER: ${INTENT_V2_DECISION_TIER:-none}
|
||||
WORKFLOW_INTENTS_ENABLED: ${WORKFLOW_INTENTS_ENABLED:-true}
|
||||
MULTI_INTENT_ENABLED: ${MULTI_INTENT_ENABLED:-true}
|
||||
SEMANTIC_FALLBACK_ENABLED: ${SEMANTIC_FALLBACK_ENABLED:-false}
|
||||
ports:
|
||||
- "${APP_PUBLISH_HOST:-127.0.0.1}:${HTTP_PORT:-8080}:${HTTP_PORT:-8080}"
|
||||
healthcheck:
|
||||
|
||||
+24
-5
@@ -14,7 +14,7 @@ services:
|
||||
command: ["postgres", "-c", "shared_buffers=512MB", "-c", "max_connections=300"]
|
||||
environment:
|
||||
POSTGRES_USER: ${SYSTEM_DB_USER:-app}
|
||||
POSTGRES_PASSWORD: ${SYSTEM_DB_PASSWORD:-prod_highly_secure_db_pass}
|
||||
POSTGRES_PASSWORD: ${SYSTEM_DB_PASSWORD:?SYSTEM_DB_PASSWORD is required for main/production}
|
||||
POSTGRES_DB: ${SYSTEM_DB_NAME:-agent_system_main}
|
||||
ports:
|
||||
- "127.0.0.1:${SYSTEM_DB_PUBLISH_PORT:-5435}:5432"
|
||||
@@ -60,7 +60,6 @@ services:
|
||||
MINIMAX_API_TYPE: ${MINIMAX_API_TYPE:-openai-compatible}
|
||||
MINIMAX_PROVIDER: minimax
|
||||
TAVILY_API_KEYS: ${TAVILY_API_KEYS:-}
|
||||
TAVILY_API_KEY: ${TAVILY_API_KEY:-}
|
||||
RUNNER_AUTH_TOKEN: ${RUNNER_AUTH_TOKEN:?RUNNER_AUTH_TOKEN is required for main/production}
|
||||
ports:
|
||||
- "127.0.0.1:${PI_AGENT_RUNNER_PORT:-18867}:18867"
|
||||
@@ -92,19 +91,39 @@ services:
|
||||
HTTP_PORT: "${HTTP_PORT:-8000}"
|
||||
HTTP_HOST: "0.0.0.0"
|
||||
AGENT_BACKEND: ${AGENT_BACKEND:-pi-agent}
|
||||
AGENT_TIMEOUT: ${AGENT_TIMEOUT:-30m}
|
||||
MAX_CONCURRENT_AGENTS: ${MAX_CONCURRENT_AGENTS:-8}
|
||||
DATA_DIR: /app/data
|
||||
# 独占生产业务与对话库
|
||||
APP_DB_DSN: postgres://${SYSTEM_DB_USER:-app}:${SYSTEM_DB_PASSWORD:-prod_highly_secure_db_pass}@system-db:5432/${SYSTEM_DB_NAME:-agent_system_main}?sslmode=disable
|
||||
APP_DB_DSN: postgres://${SYSTEM_DB_USER:-app}:${SYSTEM_DB_PASSWORD:?SYSTEM_DB_PASSWORD is required for main/production}@system-db:5432/${SYSTEM_DB_NAME:-agent_system_main}?sslmode=disable
|
||||
# 共享知识库
|
||||
POSTGRES_SHARED_KB_DSN: postgres://${KB_DB_USER:-regkb}:${KB_DB_PASSWORD:-shared_kb_secret_pass}@shuimu-kb-shared:5432/${KB_DB_NAME:-regulations}?sslmode=disable
|
||||
POSTGRES_SHARED_KB_DSN: postgres://${KB_DB_USER:-regkb}:${KB_DB_PASSWORD:?KB_DB_PASSWORD is required for main/production}@shuimu-kb-shared:5432/${KB_DB_NAME:-regulations}?sslmode=disable
|
||||
CURSOR_RUNNER_URL: http://cursor-agent:18767
|
||||
PI_AGENT_RUNNER_URL: http://pi-agent:18867
|
||||
PI_AGENT_RUNNER_AUTH_TOKEN: ${RUNNER_AUTH_TOKEN:?RUNNER_AUTH_TOKEN is required for main/production}
|
||||
PI_AGENT_PROVIDER: ${PI_AGENT_PROVIDER:-minimax}
|
||||
PI_AGENT_MODEL: ${PI_AGENT_MODEL:-${MINIMAX_MODEL_ID:-MiniMax-M3}}
|
||||
RUNNER_AUTH_TOKEN: ${RUNNER_AUTH_TOKEN:?RUNNER_AUTH_TOKEN is required for main/production}
|
||||
KB_DB_PASSWORD: ${KB_DB_PASSWORD:?KB_DB_PASSWORD is required for main/production}
|
||||
ADMIN_USERNAME: ${ADMIN_USERNAME:-admin}
|
||||
ADMIN_PASSWORD: ${ADMIN_PASSWORD:?ADMIN_PASSWORD is required for main/production}
|
||||
AUTH_REGISTRATION_ENABLED: ${AUTH_REGISTRATION_ENABLED:-true}
|
||||
AUTH_REQUIRE_APPROVAL: ${AUTH_REQUIRE_APPROVAL:-false}
|
||||
EMBEDDING_ENDPOINT_URL: ${EMBEDDING_ENDPOINT_URL:-}
|
||||
EMBEDDING_MODEL: ${EMBEDDING_MODEL:-bge-m3}
|
||||
EMBEDDING_API_KEY: ${EMBEDDING_API_KEY:-}
|
||||
RERANKER_ENDPOINT_URL: ${RERANKER_ENDPOINT_URL:-}
|
||||
RERANKER_MODEL: ${RERANKER_MODEL:-bge-reranker-v2-m3-free}
|
||||
RERANKER_API_KEY: ${RERANKER_API_KEY:-}
|
||||
CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS:-}
|
||||
ALLOW_IN_MEMORY: "false"
|
||||
AUX_FILE_DSN: file:/var/lib/feishu/outbox_main.db?_journal_mode=WAL&_busy_timeout=5000
|
||||
CACHE_ENABLED: ${CACHE_ENABLED:-true}
|
||||
INTENT_V2_SHADOW: ${INTENT_V2_SHADOW:-false}
|
||||
INTENT_V2_DECISION: ${INTENT_V2_DECISION:-false}
|
||||
INTENT_V2_DECISION_TIER: ${INTENT_V2_DECISION_TIER:-none}
|
||||
WORKFLOW_INTENTS_ENABLED: ${WORKFLOW_INTENTS_ENABLED:-true}
|
||||
MULTI_INTENT_ENABLED: ${MULTI_INTENT_ENABLED:-true}
|
||||
SEMANTIC_FALLBACK_ENABLED: ${SEMANTIC_FALLBACK_ENABLED:-false}
|
||||
ports:
|
||||
- "${APP_PUBLISH_HOST:-0.0.0.0}:${HTTP_PORT:-8000}:${HTTP_PORT:-8000}"
|
||||
healthcheck:
|
||||
|
||||
@@ -10,7 +10,7 @@ services:
|
||||
container_name: shuimu-kb-shared
|
||||
environment:
|
||||
POSTGRES_USER: ${KB_DB_USER:-regkb}
|
||||
POSTGRES_PASSWORD: ${KB_DB_PASSWORD:-shared_kb_secret_pass}
|
||||
POSTGRES_PASSWORD: ${KB_DB_PASSWORD:?KB_DB_PASSWORD is required}
|
||||
POSTGRES_DB: ${KB_DB_NAME:-regulations}
|
||||
ports:
|
||||
- "127.0.0.1:${KB_PUBLISH_PORT:-5432}:5432"
|
||||
@@ -29,7 +29,7 @@ services:
|
||||
networks:
|
||||
shared-net:
|
||||
name: chat-agent-shared-net
|
||||
driver: bridge
|
||||
external: true
|
||||
|
||||
volumes:
|
||||
shared-kb-pgdata:
|
||||
|
||||
+24
-4
@@ -60,7 +60,6 @@ services:
|
||||
MINIMAX_API_TYPE: ${MINIMAX_API_TYPE:-openai-compatible}
|
||||
MINIMAX_PROVIDER: minimax
|
||||
TAVILY_API_KEYS: ${TAVILY_API_KEYS:-}
|
||||
TAVILY_API_KEY: ${TAVILY_API_KEY:-}
|
||||
RUNNER_AUTH_TOKEN: ${RUNNER_AUTH_TOKEN:-}
|
||||
ports:
|
||||
- "127.0.0.1:${PI_AGENT_RUNNER_PORT:-18866}:18866"
|
||||
@@ -92,17 +91,38 @@ services:
|
||||
HTTP_PORT: "${HTTP_PORT:-8081}"
|
||||
HTTP_HOST: "0.0.0.0"
|
||||
AGENT_BACKEND: ${AGENT_BACKEND:-pi-agent}
|
||||
AGENT_TIMEOUT: ${AGENT_TIMEOUT:-30m}
|
||||
MAX_CONCURRENT_AGENTS: ${MAX_CONCURRENT_AGENTS:-8}
|
||||
DATA_DIR: /app/data
|
||||
# 独占测试业务与对话库
|
||||
APP_DB_DSN: postgres://${SYSTEM_DB_USER:-app}:${SYSTEM_DB_PASSWORD:-test_secret_db_pass}@system-db:5432/${SYSTEM_DB_NAME:-agent_system_test}?sslmode=disable
|
||||
# 共享知识库
|
||||
POSTGRES_SHARED_KB_DSN: postgres://${KB_DB_USER:-regkb}:${KB_DB_PASSWORD:-shared_kb_secret_pass}@shuimu-kb-shared:5432/${KB_DB_NAME:-regulations}?sslmode=disable
|
||||
POSTGRES_SHARED_KB_DSN: postgres://${KB_DB_USER:-regkb}:${KB_DB_PASSWORD:?KB_DB_PASSWORD is required}@shuimu-kb-shared:5432/${KB_DB_NAME:-regulations}?sslmode=disable
|
||||
CURSOR_RUNNER_URL: http://cursor-agent:18766
|
||||
PI_AGENT_RUNNER_URL: http://pi-agent:18866
|
||||
PI_AGENT_RUNNER_AUTH_TOKEN: ${RUNNER_AUTH_TOKEN:-}
|
||||
PI_AGENT_PROVIDER: ${PI_AGENT_PROVIDER:-minimax}
|
||||
PI_AGENT_MODEL: ${PI_AGENT_MODEL:-${MINIMAX_MODEL_ID:-MiniMax-M3}}
|
||||
RUNNER_AUTH_TOKEN: ${RUNNER_AUTH_TOKEN:-}
|
||||
ALLOW_IN_MEMORY: "false"
|
||||
AUX_FILE_DSN: file:/var/lib/feishu/outbox_test.db?_journal_mode=WAL&_busy_timeout=5000
|
||||
EMBEDDING_ENDPOINT_URL: ${EMBEDDING_ENDPOINT_URL:-}
|
||||
EMBEDDING_MODEL: ${EMBEDDING_MODEL:-bge-m3}
|
||||
EMBEDDING_API_KEY: ${EMBEDDING_API_KEY:-}
|
||||
RERANKER_ENDPOINT_URL: ${RERANKER_ENDPOINT_URL:-}
|
||||
RERANKER_MODEL: ${RERANKER_MODEL:-bge-reranker-v2-m3-free}
|
||||
RERANKER_API_KEY: ${RERANKER_API_KEY:-}
|
||||
ADMIN_USERNAME: ${ADMIN_USERNAME:-admin}
|
||||
ADMIN_PASSWORD: ${ADMIN_PASSWORD:-admin-secret-password-123}
|
||||
AUTH_REGISTRATION_ENABLED: ${AUTH_REGISTRATION_ENABLED:-true}
|
||||
AUTH_REQUIRE_APPROVAL: ${AUTH_REQUIRE_APPROVAL:-false}
|
||||
CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS:-}
|
||||
ALLOW_IN_MEMORY: ${ALLOW_IN_MEMORY:-false}
|
||||
CACHE_ENABLED: ${CACHE_ENABLED:-true}
|
||||
INTENT_V2_SHADOW: ${INTENT_V2_SHADOW:-false}
|
||||
INTENT_V2_DECISION: ${INTENT_V2_DECISION:-false}
|
||||
INTENT_V2_DECISION_TIER: ${INTENT_V2_DECISION_TIER:-none}
|
||||
WORKFLOW_INTENTS_ENABLED: ${WORKFLOW_INTENTS_ENABLED:-true}
|
||||
MULTI_INTENT_ENABLED: ${MULTI_INTENT_ENABLED:-true}
|
||||
SEMANTIC_FALLBACK_ENABLED: ${SEMANTIC_FALLBACK_ENABLED:-false}
|
||||
ports:
|
||||
- "${APP_PUBLISH_HOST:-0.0.0.0}:${HTTP_PORT:-8081}:${HTTP_PORT:-8081}"
|
||||
healthcheck:
|
||||
|
||||
+42
-23
@@ -10,27 +10,27 @@ import (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Env string `json:"env"`
|
||||
DataDir string `json:"data_dir"`
|
||||
HTTPHost string `json:"http_host"`
|
||||
HTTPPort int `json:"http_port"`
|
||||
AgentBackend string `json:"agent_backend"`
|
||||
AgentTimeout time.Duration `json:"agent_timeout"`
|
||||
CursorAPIKey string `json:"cursor_api_key"`
|
||||
CursorModel string `json:"cursor_model"`
|
||||
CursorBin string `json:"cursor_bin"`
|
||||
CursorBridgeBin string `json:"cursor_bridge_bin"`
|
||||
PiAgentBin string `json:"pi_agent_bin"`
|
||||
PiAgentBridgeBin string `json:"pi_agent_bridge_bin"`
|
||||
PiAgentProvider string `json:"pi_agent_provider"`
|
||||
PiAgentModel string `json:"pi_agent_model"`
|
||||
PiAgentRunnerURL string `json:"pi_agent_runner_url"`
|
||||
PiAgentRunnerAuthToken string `json:"pi_agent_runner_auth_token"`
|
||||
KbDbHost string `json:"kb_db_host"`
|
||||
KbDbPort int `json:"kb_db_port"`
|
||||
KbDbUser string `json:"kb_db_user"`
|
||||
KbDbPassword string `json:"kb_db_password"`
|
||||
KbDbName string `json:"kb_db_name"`
|
||||
Env string `json:"env"`
|
||||
DataDir string `json:"data_dir"`
|
||||
HTTPHost string `json:"http_host"`
|
||||
HTTPPort int `json:"http_port"`
|
||||
AgentBackend string `json:"agent_backend"`
|
||||
AgentTimeout time.Duration `json:"agent_timeout"`
|
||||
CursorAPIKey string `json:"cursor_api_key"`
|
||||
CursorModel string `json:"cursor_model"`
|
||||
CursorBin string `json:"cursor_bin"`
|
||||
CursorBridgeBin string `json:"cursor_bridge_bin"`
|
||||
PiAgentBin string `json:"pi_agent_bin"`
|
||||
PiAgentBridgeBin string `json:"pi_agent_bridge_bin"`
|
||||
PiAgentProvider string `json:"pi_agent_provider"`
|
||||
PiAgentModel string `json:"pi_agent_model"`
|
||||
PiAgentRunnerURL string `json:"pi_agent_runner_url"`
|
||||
PiAgentRunnerAuthToken string `json:"pi_agent_runner_auth_token"`
|
||||
KbDbHost string `json:"kb_db_host"`
|
||||
KbDbPort int `json:"kb_db_port"`
|
||||
KbDbUser string `json:"kb_db_user"`
|
||||
KbDbPassword string `json:"kb_db_password"`
|
||||
KbDbName string `json:"kb_db_name"`
|
||||
|
||||
// 专属记忆与多级上下文数据库配置
|
||||
ContextDbHost string `json:"context_db_host"`
|
||||
@@ -252,9 +252,29 @@ func Load(path string) (*Config, error) {
|
||||
if v := os.Getenv("AGENT_BACKEND"); v != "" {
|
||||
cfg.AgentBackend = v
|
||||
}
|
||||
if v := os.Getenv("AGENT_TIMEOUT"); v != "" {
|
||||
if d, err := time.ParseDuration(v); err == nil {
|
||||
cfg.AgentTimeout = d
|
||||
}
|
||||
}
|
||||
if v := os.Getenv("CURSOR_API_KEY"); v != "" {
|
||||
cfg.CursorAPIKey = v
|
||||
}
|
||||
if v := os.Getenv("CURSOR_MODEL"); v != "" {
|
||||
cfg.CursorModel = v
|
||||
}
|
||||
if v := os.Getenv("CURSOR_BIN"); v != "" {
|
||||
cfg.CursorBin = v
|
||||
}
|
||||
if v := os.Getenv("CURSOR_BRIDGE_BIN"); v != "" {
|
||||
cfg.CursorBridgeBin = v
|
||||
}
|
||||
if v := os.Getenv("PI_AGENT_BIN"); v != "" {
|
||||
cfg.PiAgentBin = v
|
||||
}
|
||||
if v := os.Getenv("PI_AGENT_BRIDGE_BIN"); v != "" {
|
||||
cfg.PiAgentBridgeBin = v
|
||||
}
|
||||
if v := os.Getenv("PI_AGENT_PROVIDER"); v != "" {
|
||||
cfg.PiAgentProvider = v
|
||||
}
|
||||
@@ -398,7 +418,7 @@ func validateProductionSecrets(cfg *Config) error {
|
||||
"CONTEXT_DB_PASSWORD": cfg.ContextDbPassword,
|
||||
"ADMIN_PASSWORD": cfg.AdminPassword,
|
||||
}
|
||||
defaults := []string{"shared_kb_secret_pass", "dev_secret_db_pass", "admin-secret-password-123"}
|
||||
defaults := []string{"shared_kb_secret_pass", "dev_secret_db_pass", "prod_highly_secure_db_pass", "admin-secret-password-123"}
|
||||
for envKey, val := range weak {
|
||||
if strings.TrimSpace(val) == "" {
|
||||
return fmt.Errorf("%s must be set in %s environment", envKey, cfg.Env)
|
||||
@@ -482,4 +502,3 @@ func (c *Config) KbDSN() string {
|
||||
func (c *Config) ContextDSN() string {
|
||||
return "postgres://" + c.ContextDbUser + ":" + c.ContextDbPassword + "@" + c.ContextDbHost + ":" + strconv.Itoa(c.ContextDbPort) + "/" + c.ContextDbName + "?sslmode=disable"
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,35 @@ func TestConfigLoadDSNOverrides(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigLoadRuntimeEnvironmentOverrides(t *testing.T) {
|
||||
t.Setenv("ENV", "dev")
|
||||
t.Setenv("AGENT_TIMEOUT", "47m")
|
||||
t.Setenv("CURSOR_API_KEY", "cursor-test-key")
|
||||
t.Setenv("CURSOR_MODEL", "cursor-test-model")
|
||||
t.Setenv("CURSOR_BIN", "/test/cursor")
|
||||
t.Setenv("CURSOR_BRIDGE_BIN", "/test/cursor-bridge.mjs")
|
||||
t.Setenv("PI_AGENT_BRIDGE_BIN", "/test/pi-bridge.mjs")
|
||||
t.Setenv("EMBEDDING_MODEL", "embedding-test-model")
|
||||
t.Setenv("RERANKER_MODEL", "reranker-test-model")
|
||||
|
||||
cfg, err := config.Load("/dev/null")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error loading config: %v", err)
|
||||
}
|
||||
if cfg.AgentTimeout != 47*time.Minute {
|
||||
t.Fatalf("expected environment AgentTimeout 47m, got %v", cfg.AgentTimeout)
|
||||
}
|
||||
if cfg.CursorAPIKey != "cursor-test-key" || cfg.CursorModel != "cursor-test-model" || cfg.CursorBin != "/test/cursor" || cfg.CursorBridgeBin != "/test/cursor-bridge.mjs" {
|
||||
t.Fatalf("cursor environment overrides were not applied")
|
||||
}
|
||||
if cfg.PiAgentBridgeBin != "/test/pi-bridge.mjs" {
|
||||
t.Fatalf("pi bridge environment override was not applied")
|
||||
}
|
||||
if cfg.EmbeddingModel != "embedding-test-model" || cfg.RerankerModel != "reranker-test-model" {
|
||||
t.Fatalf("retrieval model environment overrides were not applied")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigLoadCORSDefaultsForDev(t *testing.T) {
|
||||
cfg, err := config.Load("/dev/null")
|
||||
if err != nil {
|
||||
@@ -103,3 +132,15 @@ func TestConfigLoadRejectsWeakSecretsOutsideDev(t *testing.T) {
|
||||
t.Fatal("expected config load to fail for missing secrets in main env")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigLoadRejectsProductionDatabasePlaceholder(t *testing.T) {
|
||||
t.Setenv("ENV", "prod")
|
||||
t.Setenv("KB_DB_PASSWORD", "kb-prod-secret-xyz")
|
||||
t.Setenv("CONTEXT_DB_PASSWORD", "prod_highly_secure_db_pass")
|
||||
t.Setenv("ADMIN_PASSWORD", "admin-prod-secret-xyz")
|
||||
|
||||
_, err := config.Load("/dev/null")
|
||||
if err == nil {
|
||||
t.Fatal("expected config load to reject the documented production database placeholder")
|
||||
}
|
||||
}
|
||||
|
||||
+15
-14
@@ -90,44 +90,45 @@ run_compose() {
|
||||
prepare_env_file "${target}"
|
||||
fi
|
||||
|
||||
local env_arg=""
|
||||
local env_args=()
|
||||
# 根 .env 提供跨环境共用的模型、检索与认证配置;环境文件仅覆盖端口、
|
||||
# 数据库和其他环境差异。后声明的 env-file 优先级更高。
|
||||
if [[ -f ".env" ]]; then
|
||||
env_args+=(--env-file ".env")
|
||||
fi
|
||||
if [[ -f "${env_file}" ]]; then
|
||||
env_arg="--env-file ${env_file}"
|
||||
env_args+=(--env-file "${env_file}")
|
||||
fi
|
||||
|
||||
case "${act}" in
|
||||
start)
|
||||
if [[ "${target}" != "shared" ]]; then
|
||||
ensure_shared_network
|
||||
fi
|
||||
ensure_shared_network
|
||||
echo "==> [${target}] 启动服务 (${compose_file})..."
|
||||
docker compose -f "${compose_file}" ${env_arg} up -d
|
||||
docker compose -f "${compose_file}" "${env_args[@]}" up -d
|
||||
echo "==> [${target}] 服务已启动"
|
||||
;;
|
||||
stop)
|
||||
echo "==> [${target}] 停止服务..."
|
||||
docker compose -f "${compose_file}" ${env_arg} stop
|
||||
docker compose -f "${compose_file}" "${env_args[@]}" stop
|
||||
;;
|
||||
restart)
|
||||
if [[ "${target}" != "shared" ]]; then
|
||||
ensure_shared_network
|
||||
fi
|
||||
ensure_shared_network
|
||||
echo "==> [${target}] 重启服务..."
|
||||
docker compose -f "${compose_file}" ${env_arg} restart
|
||||
docker compose -f "${compose_file}" "${env_args[@]}" restart
|
||||
;;
|
||||
status|ps)
|
||||
echo "------------------------------------------------------------"
|
||||
echo "环境: [${target}] (编排: ${compose_file})"
|
||||
echo "------------------------------------------------------------"
|
||||
docker compose -f "${compose_file}" ${env_arg} ps
|
||||
docker compose -f "${compose_file}" "${env_args[@]}" ps
|
||||
echo ""
|
||||
;;
|
||||
logs)
|
||||
docker compose -f "${compose_file}" ${env_arg} logs -f --tail=100
|
||||
docker compose -f "${compose_file}" "${env_args[@]}" logs -f --tail=100
|
||||
;;
|
||||
down)
|
||||
echo "==> [${target}] 清理并下线容器..."
|
||||
docker compose -f "${compose_file}" ${env_arg} down
|
||||
docker compose -f "${compose_file}" "${env_args[@]}" down
|
||||
;;
|
||||
*)
|
||||
echo "[错误] 未知操作: ${act}"
|
||||
|
||||
Reference in New Issue
Block a user