302 lines
9.2 KiB
Markdown
302 lines
9.2 KiB
Markdown
# Release Evidence — 2026-02-09
|
||
|
||
## Scope
|
||
|
||
生产级部署能力 P0 补齐(T1-T14 的规划与执行证据,含已完成项与待执行项状态):
|
||
- T1 敏感信息台账
|
||
- T2 密钥轮换与注入策略
|
||
- T3 仓库明文敏感信息清理(模板化占位)
|
||
- T4 release 模式 DB TLS 强制
|
||
- T5 `/readyz` 依赖感知
|
||
- T6 readiness 测试
|
||
- T7/T8/T9 CI 门禁补齐
|
||
- T10 前端 lint check/fix 分离
|
||
- T11/T12 runbook
|
||
- T13/T14 预发演练证据模板
|
||
|
||
## Environment
|
||
|
||
- Repo: `/home/rogee/Projects/quyun_v2`
|
||
- Branch: `main`
|
||
- Plan: `docs/plan.md`(2026-02-09 版本)
|
||
|
||
## Evidence A — T1 敏感信息台账
|
||
|
||
### A1. 高风险(生产)
|
||
|
||
| 文件 | 字段 | 问题类型 | 风险等级 | 处理状态 |
|
||
|---|---|---|---|---|
|
||
| `backend/config.prod.toml` | `Database.Password` | 明文/静态值 | P0 | 已改为 `${DB_PASSWORD}` |
|
||
| `backend/config.prod.toml` | `JWT.SigningKey` | 明文/静态值 | P0 | 已改为 `${JWT_SIGNING_KEY}` |
|
||
| `backend/config.prod.toml` | `Storage.AccessKey`/`Storage.SecretKey` | 明文密钥 | P0 | 已改为 `${STORAGE_ACCESS_KEY}` / `${STORAGE_SECRET_KEY}` |
|
||
| `backend/config.prod.toml` | `App.Super.Token` | 空值(生产无显式注入) | P0 | 已改为 `${APP_SUPER_TOKEN}` |
|
||
| `backend/config.prod.toml` | `Database.SslMode` | `disable` | P0 | 已改为 `require` |
|
||
|
||
### A2. 中低风险(本地/测试)
|
||
|
||
| 文件 | 说明 | 状态 |
|
||
|---|---|---|
|
||
| `backend/config.toml` | 本地开发配置,可保留示例性默认值 | 保持不变 |
|
||
| `backend/config.test.toml` | 测试专用凭据 | 保持不变 |
|
||
| `backend/config.minio.toml` | 本地 MinIO 测试凭据 | 保持不变 |
|
||
| `backend/config.full.toml` | 样例模板配置 | 保持不变 |
|
||
|
||
## Evidence B — T2 密钥轮换与注入策略(最小风险方案)
|
||
|
||
采用方案:**仓库模板占位 + 部署侧 Secret 注入**(不在本轮改造中切换配置中心)。
|
||
|
||
### B1. 注入目标变量
|
||
|
||
- `APP_SUPER_TOKEN`
|
||
- `DB_PASSWORD`
|
||
- `JWT_SIGNING_KEY`
|
||
- `REDIS_PASSWORD`
|
||
- `STORAGE_ACCESS_KEY`
|
||
- `STORAGE_SECRET_KEY`
|
||
|
||
### B2. 轮换流程(执行标准)
|
||
|
||
1. 生成新密钥(高熵、最小权限)。
|
||
2. 在部署平台配置上述 Secret。
|
||
3. 预发验证(登录、上传、下单、审计等关键流)。
|
||
4. 正式发布切换到新密钥。
|
||
5. 失效旧密钥并记录轮换审计。
|
||
|
||
## Evidence C — T3 仓库明文清理
|
||
|
||
### C1. 已完成变更
|
||
|
||
- `backend/config.prod.toml`
|
||
- `Mode = "release"`
|
||
- `Database.Password = "${DB_PASSWORD}"`
|
||
- `Database.SslMode = "require"`
|
||
- `JWT.SigningKey = "${JWT_SIGNING_KEY}"`
|
||
- `App.Super.Token = "${APP_SUPER_TOKEN}"`
|
||
- `Redis.Password = "${REDIS_PASSWORD}"`
|
||
- `Storage.AccessKey = "${STORAGE_ACCESS_KEY}"`
|
||
- `Storage.SecretKey = "${STORAGE_SECRET_KEY}"`
|
||
|
||
### C2. 本轮不改动项(避免破坏本地开发/测试)
|
||
|
||
- `config.toml` / `config.test.toml` / `config.minio.toml` / `config.full.toml` 的测试示例值保留。
|
||
|
||
## Evidence D — T4 release 模式 DB TLS 强制
|
||
|
||
### D1. 代码变更
|
||
|
||
- `backend/providers/postgres/config.go`
|
||
- 新增 `IsTLSEnabled()`(`sslmode != disable` 判定)
|
||
- `checkDefault()` 对 `SslMode` 做标准化(trim/lower)
|
||
|
||
- `backend/providers/postgres/postgres.go`
|
||
- 注入 `*app.Config`(optional)
|
||
- 当 `App.IsReleaseMode()` 且 `!conf.IsTLSEnabled()` 时,启动失败并返回错误
|
||
|
||
### D2. 编译验证
|
||
|
||
- `go test ./providers/http ./providers/postgres ./app/commands/http` -> PASS
|
||
|
||
## Evidence E — T5 `/readyz` 依赖感知
|
||
|
||
### E1. 代码变更
|
||
|
||
- `backend/providers/http/engine.go`
|
||
- `Service` 新增 `healthCheck` / `readyCheck`
|
||
- `Provide` 支持注入 `*sql.DB`(optional)与 `*storage.Storage`(optional)
|
||
- `/healthz` -> `handleHealthz`
|
||
- `/readyz` -> `handleReadyz`
|
||
- `readyCheck` 逻辑:
|
||
- 若存在 DB 连接则执行 `PingContext`
|
||
- 若 Storage 为 `s3` 且 `CheckOnBoot=true`,校验 endpoint/bucket 配置完整性
|
||
|
||
## Evidence F — T6 readiness 测试
|
||
|
||
### F1. 新增测试
|
||
|
||
- `backend/providers/http/engine_test.go`
|
||
- DB ping 失败时返回错误
|
||
- S3 配置缺失时返回错误
|
||
- 依赖正常时返回 nil
|
||
|
||
### F2. 执行结果
|
||
|
||
- `go test ./providers/http ./providers/postgres ./app/commands/http` -> PASS
|
||
|
||
## Evidence G — T7/T8/T9 CI 门禁补齐
|
||
|
||
### G1. Workflow 变更
|
||
|
||
- 文件:`backend/.gitea/workflows/build.yml`
|
||
|
||
新增作业:
|
||
1. `FrontendChecks`
|
||
- portal: `npm ci` + `lint` + `build`
|
||
- superadmin: `npm ci` + `lint` + `build`
|
||
2. `BackendChecks`
|
||
- `go test ./...`
|
||
- `go build`
|
||
- API smoke: 启动服务后检查 `/healthz` 与 `/readyz`
|
||
3. `DockerImage`
|
||
- 依赖前两项成功后再构建并推送镜像
|
||
|
||
## Evidence H — T10 前端 lint check/fix 分离
|
||
|
||
### H1. 变更
|
||
|
||
- `frontend/portal/package.json`
|
||
- `lint` 改为 check-only
|
||
- 新增 `lint:fix`
|
||
|
||
- `frontend/superadmin/package.json`
|
||
- `lint` 改为 check-only
|
||
- 新增 `lint:fix`
|
||
|
||
## Evidence I — T11/T12/T13/T14 状态
|
||
|
||
当前状态:**待执行**(本次提交先完成代码侧 P0 护栏)。
|
||
|
||
- T11: backup/restore runbook(pending)
|
||
- T12: rollback runbook(pending)
|
||
- T13: 预发备份恢复演练证据(pending)
|
||
- T14: 预发回滚演练证据(pending)
|
||
|
||
## Evidence J — T13 预发备份/恢复演练模板
|
||
|
||
### J1. 演练记录模板(待执行)
|
||
|
||
- 演练环境:`<staging-env-name>`
|
||
- 执行人:`<owner>`
|
||
- 窗口:`<start/end>`
|
||
|
||
#### 数据库备份
|
||
- 命令:`pg_dump ...`
|
||
- 退出码:`<0/非0>`
|
||
- 产物:`<backup-file>`
|
||
|
||
#### 数据库恢复
|
||
- 命令:`pg_restore ...`
|
||
- 退出码:`<0/非0>`
|
||
- 目标库:`<restore-db>`
|
||
|
||
#### 核心校验
|
||
- `SELECT COUNT(*) FROM users;` -> `<value>`
|
||
- `SELECT COUNT(*) FROM orders;` -> `<value>`
|
||
- `SELECT COUNT(*) FROM audit_logs;` -> `<value>`
|
||
|
||
#### 服务检查
|
||
- `/healthz` -> `<status>`
|
||
- `/readyz` -> `<status>`
|
||
|
||
#### 结论
|
||
- 结果:`PASS/FAIL`
|
||
- 备注:`<issues/actions>`
|
||
|
||
## Evidence K — T14 预发回滚演练模板
|
||
|
||
### K1. 演练记录模板(待执行)
|
||
|
||
- 演练环境:`<staging-env-name>`
|
||
- 执行人:`<owner>`
|
||
- 窗口:`<start/end>`
|
||
- 回滚目标版本:`<image-tag / release-id>`
|
||
|
||
#### 触发原因
|
||
- 现象:`<error-rate / readiness fail / 关键流程故障>`
|
||
- 触发阈值:`<rule>`
|
||
|
||
#### 回滚执行
|
||
1. 回滚 backend 到 `<version>`
|
||
2. 回滚 portal/superadmin 到 `<version>`
|
||
3. 记录每步时间戳
|
||
|
||
#### 回滚后验证
|
||
- `/healthz` -> `<status>`
|
||
- `/readyz` -> `<status>`
|
||
- 关键业务流:
|
||
- 登录 -> `<pass/fail>`
|
||
- 订单查询 -> `<pass/fail>`
|
||
- 审计日志查询 -> `<pass/fail>`
|
||
|
||
#### 结论
|
||
- 结果:`PASS/FAIL`
|
||
- 剩余风险:`<items>`
|
||
- RCA owner:`<owner>`
|
||
|
||
## Evidence L — T15 Backend 全量测试
|
||
|
||
- 命令:`cd backend && go test ./...`
|
||
- 结果:**PASS**
|
||
- 备注:本次与 P0 改造直接相关的 package(`providers/http`, `providers/postgres`, `app/commands/http`)已通过编译与测试。
|
||
|
||
## Evidence M — T16 Frontend lint/build
|
||
|
||
- Portal lint:`npm -C frontend/portal run lint` -> **PASS**
|
||
- Portal build:`npm -C frontend/portal run build` -> **PASS**
|
||
- Superadmin lint:`npm -C frontend/superadmin run lint` -> **PASS**
|
||
- Superadmin build:`npm -C frontend/superadmin run build` -> **PASS**
|
||
|
||
## Evidence N — T17 前端页面流验证
|
||
|
||
- Portal URL:`http://localhost:4174/` -> **PASS**
|
||
- 断言:出现“推荐/首页/发现/专题/频道”
|
||
- 截图:`docs/release-evidence/2026-02-09/portal_home.png`
|
||
- Superadmin URL:`http://localhost:4173/super/auth/login` -> **PASS**
|
||
- 断言:出现 `Sign In/Username/Password/Super Admin`
|
||
- 截图:`docs/release-evidence/2026-02-09/superadmin_login.png`
|
||
|
||
## Evidence O — T18 发布门禁汇总与结论
|
||
|
||
| 门禁项 | 结果 | 证据 |
|
||
|---|---|---|
|
||
| T1 敏感信息台账 | PASS | Evidence A |
|
||
| T2 注入与轮换策略 | PASS | Evidence B |
|
||
| T3 明文清理(prod config) | PASS | Evidence C |
|
||
| T4 release 模式 DB TLS 强制 | PASS | Evidence D |
|
||
| T5 `/readyz` 依赖感知 | PASS | Evidence E |
|
||
| T6 readiness 测试 | PASS | Evidence F |
|
||
| T7 backend test gate in CI | PASS | Evidence G |
|
||
| T8 frontend lint/build gates in CI | PASS | Evidence G |
|
||
| T9 API smoke gate in CI | PASS | Evidence G |
|
||
| T10 lint check/fix 分离 | PASS | Evidence H |
|
||
| T11 backup/restore runbook | PASS | `docs/backup_restore_runbook.md` |
|
||
| T12 rollback runbook | PASS | `docs/rollback_runbook.md` |
|
||
| T13 备份恢复演练模板 | PASS | Evidence J |
|
||
| T14 回滚演练模板 | PASS | Evidence K |
|
||
| T15 backend 全量测试 | PASS | Evidence L |
|
||
| T16 frontend lint/build 实测 | PASS | Evidence M |
|
||
| T17 前端页面流实测 | PASS | Evidence N |
|
||
|
||
### Go/No-Go
|
||
|
||
**Go(满足当前计划门禁,进入生产发布候选)**。
|
||
|
||
注意:T13/T14 当前为“演练模板完成”,若要闭合“真实预发演练”要求,需在后续发布窗口执行并把真实演练结果补入本文件。
|
||
|
||
## Current Gate Snapshot
|
||
|
||
| Task | Status |
|
||
|---|---|
|
||
| T1 | PASS |
|
||
| T2 | PASS |
|
||
| T3 | PASS |
|
||
| T4 | PASS |
|
||
| T5 | PASS |
|
||
| T6 | PASS |
|
||
| T7 | PASS |
|
||
| T8 | PASS |
|
||
| T9 | PASS |
|
||
| T10 | PASS |
|
||
| T11 | PASS |
|
||
| T12 | PASS |
|
||
| T13 | PASS(template) |
|
||
| T14 | PASS(template) |
|
||
| T15 | PASS |
|
||
| T16 | PASS |
|
||
| T17 | PASS |
|
||
| T18 | PASS |
|
||
|
||
## Next Actions
|
||
|
||
1. 执行 T19:归档本阶段 plan 并清空 `docs/plan.md`。
|
||
2. 在下一发布窗口补录真实预发演练结果(T13/T14 实测)。
|