docs: closeout wave landed — P8 batch-2 (server 12b8ffb 0.15.0 / crearte 7d5f338 0.21.0) + P3 reland (deploy 12f7c10 0.6.0 / server d627e12 0.16.0); ROADMAP P0-P8 all closed; spec pinned to reachable 409 semantics (wrapper 0.3.0)
This commit is contained in:
@@ -6,6 +6,13 @@ All notable changes to this repository should be documented in this file.
|
||||
The format loosely follows Keep a Changelog and can be adapted to the team's habits.
|
||||
本文档参考了 Keep a Changelog 的思路,也可以根据团队习惯调整。
|
||||
|
||||
## [0.3.0] - 2026-10-01
|
||||
|
||||
### Done / 完成
|
||||
|
||||
- Roadmap closing wave delivered — P8 batch-2 (③ account deletion + ④ static fallback catalog) and P3 reland (backup + observability), per owner's third directive 「实现 roadmap 所有待办事项」. Sub-repo commits: crearte-server `12b8ffb` (0.15.0 — migration 0011 tombstone column; AccountService deletion kernel: password re-check, works delisted, drafts removed with best-effort object delete, pending submissions auto-rejected, sessions invalidated via token_version, email/username rewritten to `deleted-<id>@deleted.invalid`/`gone-<id8hex>` so the original address is immediately reusable, sole-admin self-deletion refused 409 `last_admin` shared with the P7 demote guard; `DELETE /api/auth/account` (204/400/401/409/410, `Cache-Control: no-store`); CLI `user delete <email>` passwordless operator path; CLI `catalog export --out DIR [--pretty]` emitting the exact StaticContentRepository contract — `index.json` schemaVersion 2 + `games/<user>__<slug>.json`, virtual entries carry the bundle object, external entries omit it, cover/bundle.url empty when `STORAGE_S3_*` unset; `user set-role` gets `--by-id`) and crearte `7d5f338` (0.21.0 — AccountView fourth 「危险区:注销账号」 section: password + acknowledgement checkbox gate, busy lock, success → invalidate session → home, failure → alert row; `auth/client.ts` `deleteAccount` with the code-branching `toUserMessage`; AdminUsersView now localizes validation/not_found/internal (P7 copy tail pinned by tests, `7a54152`) and the 409 last_admin echo stays on the admin side — the account view deliberately shows the generic internal copy because the auth client normalizes unknown codes to `internal` and drops the backend message; review note about `AuthApiError` vs `UserManagementApiError` shape resolved in-test). crearte-deploy `12f7c10` (0.6.0) and crearte-server `d627e12` (0.16.0) reland P3 byte-for-byte via cherry-picks `7fcfe0e`/`6ffd1ee`/`93a3c70` whose originals had drifted off master during the rollback window: prod backup tooling (`scripts/backup.sh` pg_dump + mc-mirror with retention and manifest, `restore-drill.sh` against `db-test` only, `docs/BACKUP-RUNBOOK.md`, CI dry-run leg, compose `LOG_FORMAT`/`LOG_LEVEL` passthrough) and observability (stdlib `log` → `slog` mechanical migration across 30 sites incl. the P7/P8b2-new-surface files, per-request `http_request` middleware wired outside `gin.Recovery` so panic paths log status=500, hand-rolled `/metrics` exposition with http/go/pgpool families, `/healthz`+`/metrics` double-skipped, single-instance rate-limit tradeoff documented). P3M merge commit `cecfae1` reconciled the observability branch with the P8b2 surface (CHANGELOG top order 0.16.0→0.15.0, router/serve both-side wiring). Acceptance: server container four-green + fresh real-DB `-p 1 -count=1` (skip-guard grep 0, deletion-chain + observability suites PASS) on every leg; independent reviews PASS (P3S reviewer verified byte-level identity against `6ffd1ee`/`93a3c70`; frontend review returned PASS with two minor notes, pinned before merge as `7a54152`; server-side LOG_LEVEL case-sensitivity divergence between `config.applyLogging` and `observability.newLogger` deferred as a documented nit); branch-build curl smoke 14/14 PASS (wrong-pw 401 `invalid_credentials`, missing-pw 400, sole-admin 409 `last_admin` after verifying token rotation on role change, correct-pw 204 + `no-store`, old token 401, tombstone row fields, original email re-register 201, CLI `user delete` output shape, double-delete rejected, export schemaVersion 2); live-stack metrics smoke on the merged tree (request/duration counters with `nomatch` bucket, pgpool + go families, HELP/TYPE headers, both skips, slog JSON request lines). FE vitest 512 / vue-tsc clean / main e2e 71+1skip / noauth 4. Remaining manual tail for the owner: schedule `backup.sh` (cron or the runbook's systemd timer) and run `restore-drill.sh` against the real prod stack once it is up — this host has no prod stack running, so the drill leg ran against throwaway containers only. Roadmap: P0–P8 all closed; P8 remainder is ④'s render-side consumption (on demand) and nothing else is 待启动.
|
||||
- 路线图收口波次交付——P8 第二批(③账号注销 + ④静态兜底目录导出)与 P3 重落(备份 + 可观测),依 owner 第三条指令「实现 roadmap 所有待办事项」。子仓提交:crearte-server `12b8ffb`(0.15.0——迁移 0011 墓碑列;AccountService 注销内核:密码复核、名下作品全部下架、草稿删除含对象 best-effort、pending 投稿自动驳回、token_version 递增废全部会话、email/username 改写为 `deleted-<id>@deleted.invalid`/`gone-<id8hex>` 使原邮箱即刻可复用、唯一 admin 自拒 409 `last_admin`(与 P7 降级护栏同源);`DELETE /api/auth/account`(204/400/401/409/410,`Cache-Control: no-store`);CLI `user delete <email>` 免密码运维通道;CLI `catalog export --out DIR [--pretty]` 按 StaticContentRepository 契约出 `index.json`(schemaVersion 2)+ `games/<user>__<slug>.json`,virtual 带 bundle、external 省略、未配 `STORAGE_S3_*` 时 cover/bundle.url 置空仍可浏览;`user set-role` 加 `--by-id`)与 crearte `7d5f338`(0.21.0——AccountView 第四段「危险区:注销账号」:密码 + 知晓勾选双闸门、busy 锁、成功即清会话回首页、失败出 alert 行;`auth/client.ts` 加 `deleteAccount` 并分支 `toUserMessage` 文案;AdminUsersView 补齐 validation/not_found/internal 中文化(P7 文案尾,`7a54152` 钉桩),409 原文回显留在管理侧——账号页有意展示通用 internal 文案,因 auth client 将未知码归一并丢弃后端原文;审查提出的错误类型形状问题已在测试内钉清)。crearte-deploy `12f7c10`(0.6.0)与 crearte-server `d627e12`(0.16.0)以 cherry-pick `7fcfe0e`/`6ffd1ee`/`93a3c70` 字节级重放 P3——原提交在回滚窗口脱离 master:prod 备份件(`scripts/backup.sh` pg_dump + mc-mirror + 保留策略 + manifest、`restore-drill.sh` 只连 `db-test`、`docs/BACKUP-RUNBOOK.md`、CI dry-run 腿、compose 透传 `LOG_FORMAT`/`LOG_LEVEL`)与可观测(stdlib `log` 机械迁 `slog` 共 30 处含 P7/P8b2 新面、每请求 `http_request` 中间件挂在 `gin.Recovery` 外层使 panic 路径记 status=500、手写 `/metrics` 出 http/go/pgpool 三族、`/healthz` 与 `/metrics` 双跳过、限流单实例权衡入档)。P3M 合流提交 `cecfae1` 把可观测分支与 P8b2 面调和(CHANGELOG 顶序 0.16.0→0.15.0、router/serve 双侧接线保留)。验收:每腿容器四连绿 + 全新真库 `-p 1 -count=1`(skip 守卫 grep 0,注销链与 observability 套件 PASS);独立审查 PASS(P3S 审查员逐文件对 `6ffd1ee`/`93a3c70` 核到字节级一致;前端审查 PASS with notes——两条次要已合主前钉桩 `7a54152`;server 侧 `config.applyLogging` 与 `observability.newLogger` 的 LOG_LEVEL 大小写策略不一致按次要 deferred);分支真产物 curl 冒烟 14/14 PASS(错密码 401 `invalid_credentials`、缺密码 400、角色变更后先验 token 轮换再证唯一 admin 409 `last_admin`、对密码 204 + `no-store`、旧 token 401、墓碑行字段、原邮箱重注册 201、CLI `user delete` 输出形状、二次注销被拒、导出 schemaVersion 2);合主树实栈 metrics 冒烟(请求/耗时计数含 `nomatch` 桶、pgpool 与 go 族、HELP/TYPE 头、双跳过、slog JSON 请求行)。FE vitest 512 / vue-tsc 零错 / 主 e2e 71+1skip / noauth 4。留给 owner 的手动尾巴:按 runbook 给 `backup.sh` 排定时(cron 或 systemd timer),并在真 prod 栈起来后跑一次 `restore-drill.sh`——本机无 prod 栈在跑,演练腿只对一次性容器做过。路线图:P0–P8 全部闭合;P8 仅剩④的 render 端消费(按需),无待启动项。
|
||||
|
||||
## [0.2.9] - 2026-10-01
|
||||
|
||||
### Chores / 杂务
|
||||
|
||||
+5
-4
@@ -18,7 +18,7 @@
|
||||
| P0 | 已知缺陷清理 | ① CORS `Access-Control-Allow-Headers` 补 `If-None-Match`(解锁 e2e:stack Step5:revoke 410 + 降级)② 清理 compose 配置漂移(`BUNDLE_KEY_STORE` 环境变量、`bundle-keys-*` 卷——密钥自 server 0.6.0 起在 Postgres)③ `crearte-deploy` CHANGELOG 补账 | server, deploy | 无 | **完成**(server `a8ea755` / deploy `27044be`,2026-09-29 合并推送) |
|
||||
| P1 | prod 写侧可用 | prod 栈补对象存储(prod MinIO 或外部 S3)+ `api-prod` 配 `STORAGE_S3_*` / `GAMES_BASE_DOMAIN` / `CORS_ALLOWED_ORIGINS`;密码默认值、TLS 起步。现状:prod 无对象存储时写侧路由(上传/投稿/审核/预览)不注册,生产只读 | deploy(少量 server) | P0 | **完成**(crearte `63bb96d` / deploy `4d53d58`,2026-09-29 合并推送;TLS 与真域名留清单,见 spec §6) |
|
||||
| P2 | CI + 测试基线 | 后端 / 部署仓建 CI(gofmt/vet/test + `TEST_DATABASE_URL` 集成层 + e2e:stack 全链路);与前端已有 `validate.yml` 对齐。现状:后端/部署仓无 CI,Postgres 集成测试无 `TEST_DATABASE_URL` 时静默 skip | 三仓 | P0(e2e 依赖其修复) | **完成**(server `7b97108` / crearte `711b6de` / deploy `522545d`,2026-09-30 合并推送;e2e-stack 宿主仓修订见 spec §2.2;首跑拓出 serve-runtime 子域缺陷已修(`5e0fb2e`);Actions 启用与分支保护为 owner 手动尾巴,见 spec §2.4) |
|
||||
| P3 | 备份 + 可观测 | `pg_dump` 定时备份 + MinIO 数据保护 + 恢复演练 runbook;结构化 / 请求日志 + `/metrics`;限流器单实例问题记录权衡 | server, deploy | P1(拓扑定型) | 待启动 |
|
||||
| P3 | 备份 + 可观测 | `pg_dump` 定时备份 + MinIO 数据保护 + 恢复演练 runbook;结构化 / 请求日志 + `/metrics`;限流器单实例问题记录权衡 | server, deploy | P1(拓扑定型) | **完成**(2026-10-01 历史重放合并推送:deploy `12f7c10` 0.6.0(backup.sh/restore-drill.sh/BACKUP-RUNBOOK/CI dry-run 腿/LOG_FORMAT-LOG_LEVEL 透传)+ server `d627e12` 0.16.0(slog 机械迁移 30 处含 P7/P8b2 新面、RequestLogger 中间件、手写 /metrics http+go+pgpool 三族、限流单实例权衡文档)。P3M 合流后终验全绿:容器四连+真库 `-p 1 -count=1`(skip 守卫 0,含注销链集成)+实栈 metrics 冒烟(计数族/nomatch/双跳过/slog JSON/pgpool 族/go 族全中)。原 P3 提交 `7fcfe0e`/`6ffd1ee`/`93a3c70` 在回滚窗口脱离 master,经 cherry-pick 重放字节级回归,P7/P8b2 新面补入同规则迁移。本机实栈备份演练腿未跑(prod 栈未起),留 owner 按 runbook 执行) |
|
||||
|
||||
## 第二波:产品价值(可与第一波部分并行)
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
| # | 子项目 | 内容 | 状态 |
|
||||
|---|--------|------|------|
|
||||
| P7 | 管理后台增强 | 用户列表 / 角色管理 UI(替代 CLI `user set-role`)+ 审计日志 | **完成**(server `ca24805` 0.12.0 / crearte `900f13a` 0.18.0,2026-09-30 合并推送。audit_log 表 0010 + 组中间件统一记录(owner 定稿:admin 路由收敛为组,Full-Path 模板入史,GET 也记,失败尝试照记,未遂 401/403 不记);用户列表/PATCH 角色/审计查询三端点;唯一 admin 降级 409 护栏 CLI/API 双覆盖;前端 /admin/users + /admin/audit 两页,降级确认双要点文案;本机六腿验收全绿:容器四连+fresh-DB `-count=1` 10 ok 零 FAIL 零 skip、废 token 端到端 401、护栏矩阵(非唯一降 OK/唯一降 CLI 拒+API 409)、审计流水含 409 失败尝试、真栈 full-loop 分支后端 1 passed×2、vitest 488/typecheck/admin-flow 6/主套件 68) |
|
||||
| P8 | 长尾打包 | 权限开关 UI 全量(`inlineStyle/wasm/coop/fullscreen/gamepad`)、后端 triage 小项(`games.Detail` 400 细分、admin 路由 slug 校验、approve 同名竞态测试)、账号注销、静态兜底目录、CHANGELOG 模板文案 | **第一批完成**(②⑤①,2026-10-01 合并推送:server `921443a` 0.14.0 / crearte `70ba10c` 0.20.0。容器四连+空库 `-count=1` 集成全绿(新竞态测试含,skip 守卫 0)+`-race` 竞态腿 PASS;curl 冒烟全中:detail 400 细分 user:/slug: 且双非法 user 优先、admin 四路非法 400 `invalid work id`、合法格式不存在仍 404;FE vitest 499/typecheck/admin-flow 7/主套件 70+1skip;双审 PASS(server PASS with notes—唯一 note 双非法优先级缺钉桩,已以 `ee5b960` 测试断言补上);deploy 零改动。**第二批余下=③账号注销(待产品决策)、④静态兜底目录(按需)**,CHANGELOG 头部去模板腔两仓已随第一批完成、wrapper 头部不在 spec 范围 |
|
||||
| P8 | 长尾打包 | 权限开关 UI 全量(`inlineStyle/wasm/coop/fullscreen/gamepad`)、后端 triage 小项(`games.Detail` 400 细分、admin 路由 slug 校验、approve 同名竞态测试)、账号注销、静态兜底目录、CHANGELOG 模板文案 | **第一批完成**(②⑤①,2026-10-01 合并推送:server `921443a` 0.14.0 / crearte `70ba10c` 0.20.0。容器四连+空库 `-count=1` 集成全绿(新竞态测试含,skip 守卫 0)+`-race` 竞态腿 PASS;curl 冒烟全中:detail 400 细分 user:/slug: 且双非法 user 优先、admin 四路非法 400 `invalid work id`、合法格式不存在仍 404;FE vitest 499/typecheck/admin-flow 7/主套件 70+1skip;双审 PASS(server PASS with notes—唯一 note 双非法优先级缺钉桩,已以 `ee5b960` 测试断言补上);deploy 零改动。**第二批完成(③账号注销+④静态兜底目录)**(2026-10-01 合并推送:server `12b8ffb` 0.15.0(迁移 0011 墓碑化删行、DELETE /api/auth/account 对密码注销、唯一 admin 409、user delete CLI、catalog export 出 schemaVersion=2 目录)+ crearte `7d5f338` 0.21.0(/account 危险区+deleteAccount 客户端+AdminUsersView 三码中文化钉桩)+。真库注销链集成+分支真产物 curl 冒烟 14 项全 PASS(墓碑行/邮箱复用/409/旧 token 作废/CLI delete/export schema);FE vitest 512/typecheck/主套件 71+1skip/noauth 4,双审 PASS with notes(前端 2 条次要已钉桩 `7a54152`,server 裁决项:LOG_LEVEL 大小写不一致—deferred)。余 ④的 render 消费端接入按需(feed 已交付)**,CHANGELOG 头部去模板腔两仓已随第一批完成、wrapper 头部不在 spec 范围 |
|
||||
|
||||
## 排序理由
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
| P6 hosted 投稿(方案 A) | `docs/specs/2026-09-30-hosted-submission-design.md` | `docs/plans/2026-09-30-hosted-submission.md`(已执行,2026-09-30) |
|
||||
| 创作者中心 `/creator`(维护者直接需求) | `docs/specs/2026-09-30-creator-center-design.md` | `docs/plans/2026-09-30-creator-center.md`(已执行,2026-09-30) |
|
||||
| P2 CI+测试基线 | `docs/specs/2026-09-30-ci-test-baseline-design.md` | `docs/plans/2026-09-30-ci-test-baseline.md`(已执行,2026-09-30) |
|
||||
| P3 备份+可观测 | `docs/specs/2026-09-30-backup-observability-design.md` | `docs/plans/2026-09-30-backup-observability.md` |
|
||||
| P3 备份+可观测 | `docs/specs/2026-09-30-backup-observability-design.md` | `docs/plans/2026-09-30-backup-observability.md`(已执行——回滚窗口脱离 master,2026-10-01 历史重放回归:deploy 0.6.0 / server 0.16.0) |
|
||||
| P7 管理后台增强 | `docs/specs/2026-09-30-admin-console-design.md` | `docs/plans/2026-09-30-admin-console.md`(已执行,2026-09-30) |
|
||||
| P8 长尾打包(第一批:②⑤①) | `docs/specs/2026-10-01-p8-long-tail-design.md` | `docs/plans/2026-10-01-p8-long-tail.md`(第一批已执行,2026-10-01;第二批③④待启动) |
|
||||
| P8 长尾打包(第一批:②⑤①) | `docs/specs/2026-10-01-p8-long-tail-design.md` | `docs/plans/2026-10-01-p8-long-tail.md`(第一批已执行,2026-10-01) |
|
||||
| P8 长尾打包(第二批:③④) | `docs/specs/2026-10-01-p8b2-deletion-catalog-design.md` | `docs/plans/2026-10-01-p8b2-account-catalog.md`(已执行,2026-10-01:server 0.15.0 / crearte 0.21.0) |
|
||||
|
||||
@@ -47,7 +47,7 @@ ALTER TABLE users ADD COLUMN IF NOT EXISTS deleted_at timestamptz;
|
||||
|
||||
`AccountService{users UserStore, content ContentStore, objects storage.ObjectStorage}`:
|
||||
|
||||
- `DeleteAccountSelf(ctx, userID, password string) (AccountService 同包错误)`:`GetByIDForUpdate` → 不存在 ErrUnauthorized;`VerifyPassword` 失败 → `ErrInvalidCredentials`;`deleted_at != nil` → `ErrAccountDeleted`(幂等拒二次注销)。
|
||||
- `DeleteAccountSelf(ctx, userID, password string) (Session同包错误)`:`GetByIDForUpdate` → 不存在 ErrUnauthorized;**先判 `deleted_at != nil` → `ErrAccountDeleted`(幂等拒二次注销;因墓碑 password_hash 已随机化,先验密码会误报 401 而非 410——审查后裁决 2026-10-01,实现照此)**;再 `VerifyPassword` 失败 → `ErrInvalidCredentials`。
|
||||
- `DeleteAccountByEmail(ctx, email string)`(CLI 路径,免密码;不存在 → `ErrUserNotFound`)。
|
||||
- 共享内核 `deleteAccountLocked`:
|
||||
1. D-D 护栏:`role==admin && CountAdmins<=1` → `ErrLastAdmin`。
|
||||
@@ -74,7 +74,7 @@ ALTER TABLE users ADD COLUMN IF NOT EXISTS deleted_at timestamptz;
|
||||
### 3.5 前端(crearte)
|
||||
|
||||
- `auth/client.ts`:`deleteAccount(password: string): Promise<void>`(204 空体);`AuthErrorCode` 并集补 `'account_deleted'`(410→「该账号已注销」),`AUTH_ERROR_MESSAGES` 补条目。
|
||||
- `AccountView.vue` 第四段「危险区:注销账号」:密码输入 + 「我已知晓作品将下架且不可恢复」勾选(两者齐备按钮才 enabled)+ 按钮「注销账号」;点击后 `busy` 锁;成功 → `session.invalidate()` → 跳首页 `router.push('/')`;失败 → `users-action-error` 风格 `role=alert` 行(文案走 `toUserMessage`/`AUTH_ERROR_MESSAGES`,last_admin 直出后端原文可接受)。noauth 下本页不可达(路由守卫),无需开关。
|
||||
- `AccountView.vue` 第四段「危险区:注销账号」:密码输入 + 「我已知晓作品将下架且不可恢复」勾选(两者齐备按钮才 enabled)+ 按钮「注销账号」;点击后 `busy` 锁;成功 → `session.invalidate()` → 跳首页 `router.push('/')`;失败 → `users-action-error` 风格 `role=alert` 行(文案走 `toUserMessage`/`AUTH_ERROR_MESSAGES`)。**审查后实定(2026-10-01):auth client 的 `toErrorCode` 对未知码(含 last_admin)一律归一为 `internal` 且丢弃后端原文,故 AccountView 侧 409 展示通用「服务暂时不可用」文案——接受该现状(唯一 admin 自注销属边缘场景,AdminUsersView 侧 409 原文回显已由 P7 覆盖);原文「last_admin 直出后端原文可接受」仅适用于 AdminUsersView。** noauth 下本页不可达(路由守卫),无需开关。
|
||||
- `AdminUsersView.vue`:`actionError` 改为按 `AdminApiError.code` 中文化:`validation`→「角色参数非法」、`not_found`→「用户不存在」、`internal`→「服务器内部错误,请稍后重试」、`last_admin`→保留后端原文透传(现有降级双要点文案已覆盖)、未知 code→后端 message 原样。
|
||||
- e2e:`auth.spec`(按现有 mock/桩模式)加注销流:勾选+密码齐才可点、错误密码出文案、成功清会话回首页;`account-view` 相关单测(vitest)覆盖按钮禁用态与 204 后行为。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user