# 需求:后台用户管理-操作者账号体系(多登录账号+owner-admin角色)
状态: done(2026-10-13 已上线:admin-prod 925b0ff9+D1 迁移0012;operators 8 例+page-boot 4 例绿;线上 401 闸门与 UI 区块 curl 实证)
归属: account-center
来源: boss 直投(会话)
日期: 2026-10-13
建议: 做,boss 2026-10-13「现在加个用户管理菜单」:后台此前仅 ADMIN_PASSWORD 单一共享密码,无法给协作者开号、无审计身份;口径=客户账号叫会员,系统操作者才叫用户

## 现状

admin 后台(`admin-account.pinnadash.com`)此前只有一种登录方式:CF secret 里的共享 `ADMIN_PASSWORD`,签发不绑身份的会话 cookie。问题:①无法给协作者开独立账号,密码只能口头转交,轮换即全员失效;②会话不记「是谁在用」,后台操作无身份归属;③权限一刀切——任何拿到密码的人都能赠送会员/签发激活码,没有只读运营档。boss 2026-10-13 口径:面向客户的账号叫「会员」,「用户」留给系统操作者,本票即后者。

## 方案

(跨仓代做,account_center apps/admin + apps/api)

1. migration 0012 新建 `admin_users` 表(email 唯一 + pass_hash + role owner/admin + status active/disabled);
2. 口令存储走 PBKDF2-SHA256(150k 迭代,参数随哈希串走,日后升迭代数不锁死存量);
3. 登录双通道:共享密码仍有效(=bootstrap 身份,恒 owner,密码在 CF secrets 不动);username 填邮箱则按 `admin_users` 校验;
4. 会话 cookie 升级为 `exp.identity.sig`,HMAC 签名覆盖身份——改写 cookie 里的邮箱即签名不符失效;口令轮换(ADMIN_PASSWORD)仍天然作废旧会话;
5. 角色闸门:owner 才能列账号/开号/停用/重置口令/改角色;admin 只能改自己口令(需验旧口令);列表响应永不含 pass_hash;owner 不能停用自己对某账号的登录(409 cannot_disable_self);
6. 停用即时失权:每请求按库解析角色,不信任 cookie 里的角色快照;
7. 后台左导航新增「用户管理」节(单页+左导航纪律,不建新网页),仅 owner 登录态可见;含开通对话框、停用/启用、重置口令、改角色。

## 验收标准

- [x] 真 SQLite 测试覆盖登录双通道/口令错与停用拒绝/cookie 篡改/角色闸门 403/自停用 409/停用杀既有会话 —— apps/admin `operators.test.ts` 8 例绿(account_center commit `d9e3ca9`)
- [x] 后台页启动流程回归:首拉 3→4(含 `/v1/admin/operators/me` 决定「用户管理」显隐)—— `page-boot.test.ts` 4 例绿;`pnpm check` 全仓绿、api 223 例绿
- [x] prod 上线实证:D1 迁移 0012 apply ✅(`admin_users` 空表就位)、admin-prod 版本 `925b0ff9-94ba-4981-8fc1-fee1b422db41`;线上页含 operators UI 区块(curl 7 处命中)、未登录 `/v1/admin/operators/me`=401
- [x] 密钥零落地:ADMIN_PASSWORD 仍在 CF secrets,新账号口令只存 PBKDF2 哈希,任何文档/台账不写口令

## 实施状态

已上线(2026-10-13)。首个协作者账号待 boss 在后台「用户管理」页自行开通(owner 登录态下建号);bootstrap 共享密码通道保留,不强制迁移。
