docs(plan): p4b review fixes (etag recipe, sandbox origin, narrow screen)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-09-20 21:41:19 +08:00
parent ec7837ebb0
commit e9a7dc8740
@@ -20,7 +20,7 @@
- [ ] 新增:
- **管理端**(tag embed-slots):`GET /admin/embed-slots`(cursor;items: EmbedSlot{id, publicToken, name, enabled, albumId?, albumTitle?, configVersion, settings Json{position left|right, size sm|md, intervalMs 3000-30000, lang auto|zh-CN|en, collapsedDefault bool}, updatedAt});`POST /admin/embed-slots` {name, settings?} → 201;`PATCH /admin/embed-slots/{id}` {name?, enabled?, settings?, albumId?|null 绑定/解绑} → 200(albumId 变更 → configVersion+1;绑定校验相册管理权:操作者须为该相册 owner/admin 或 slot 授权成员——**简化决策:P4b 权限=仅公司 admin 可管理展示位**(需求"管理员统一管理展示位,创建者只操作被授权展示位"——被授权语义复杂度高,登记偏差:embed_slot_members 表建好但 P4b 不出授权 UI/API,P5/后续按需补));`DELETE` 不做(软性 enabled=false 即下线)
- **公开端**(ov):`GET /public/embed-slots/{token}?lang=` → 200 {slotToken, configVersion, album: {publicToken, title(resolved), defaultLocale}|null, photos: [{photoId, thumb{url,expiresAt}, width, height}](近期已发布 ≤20,按 publishedAt DESC)};404 NOT_FOUND(不存在/停用/未绑定相册→同样 404 防探测,description 说明);**ETag/If-None-Match 支持**(configVersion+照片 max entityVersion 为 ETag;304 支持——省流量,A23)
- **公开端**(ov):`GET /public/embed-slots/{token}?lang=` → 200 {slotToken, configVersion, album: {publicToken, title(resolved), defaultLocale}|null, photos: [{photoId, thumb{url,expiresAt}, width, height}](近期已发布 ≤20,按 publishedAt DESC)};404 NOT_FOUND(不存在/停用/未绑定相册→同样 404 防探测,description 说明);**ETag/If-None-Match 支持**(ETag = `"{configVersion}-{photoCount}-{maxPublishedAtMs}-{maxPhotoId}"`——评审修正:含 photoCount 与 publishedAt,新照片发布必变;纯 max entityVersion 会因新照片小版本号而 304 陈旧);304 支持——省流量,A23
- [ ] constants: EMBED_* 轮询参数(EMBED_POLL_INTERVAL_MS 默认 30000 env 可调)
- [ ] gen + lint + 全回归绿;⏸ 快速确认点
- [ ] Commit `feat(contracts): embed slots admin and public endpoints`
@@ -37,9 +37,10 @@
## Task 3: embed-loader(packages/embed-loader)
- [ ] `packages/embed-loader/src/loader.js`(手写 ~200 行,无构建或极简 esbuild 打包——决策:esbuild 已在依赖树(vite),加 devDep esbuild 打包+minify,产物 `dist/v1/loader.js` 提交入库):
- 扫描 `script[src*="/embed/v1/loader.js"]` 自身 data-*(data-slot/data-lang/data-position/data-interval 覆盖)→ 防重复(window.__photoliveEmbed loaded 标记 + 同 slot 去重)→ 每槽创建固定定位 iframe(right/left, 尺寸 per settings.size 映射 px,z-index 2147483000,box-shadow 圆角,sandbox 属性 allow-same-origin? NO——iframe 加载项目域需 scripts+popups:sandbox="allow-scripts allow-popups allow-popups-to-escape-sandbox",src= `{EMBED_ORIGIN}/embed/{slot}?lang=...&pos=...&interval=...`——EMBED_ORIGIN 从 loader.js 自身 src 推导(document.currentScript.origin),支持 CDN 部署)
- 扫描 `script[src*="/embed/v1/loader.js"]` 自身 data-*(data-slot/data-lang/data-position/data-interval 覆盖)→ 防重复(window.__photoliveEmbed loaded 标记 + 同 slot 去重)→ 每槽创建固定定位 iframe(right/left, 尺寸 per settings.size 映射 px,z-index 2147483000,box-shadow 圆角,sandbox=`"allow-scripts allow-popups allow-popups-to-escape-sandbox allow-same-origin"`——**评审修正:必须含 allow-same-origin**,否则 iframe 源为 opaque "null",loader 的 origin 相等白名单会拒掉自己 iframe 的 resize/collapse 消息且 iframe 内 API 跨源需 ACAO *;嵌入 iframe 对宿主天然跨源,allow-same-origin 仅使其保有项目域源,白名单按「消息 origin === iframe contentWindow 的源」严格校验,src= `{EMBED_ORIGIN}/embed/{slot}?lang=...&pos=...&interval=...`——EMBED_ORIGIN 从 loader.js 自身 src 推导(document.currentScript.origin),支持 CDN 部署)
- **窄屏规则**(评审缺口:需求 4.8 移动端可用状态):viewport <480px → 强制 collapsedDefault=true + 尺寸降为 sm;Playwright 移动视口用例覆盖
- postMessage 白名单:监听 message(origin 必须等于 iframe origin)仅接受 {photoliveEmbed: true, type: resize|collapse|expand, ...}→ 调整 iframe 高度/收起态(收起=高度 0+展示小把手按钮 div——把手也在 loader 侧,点击 expand);其余消息忽略。iframe 内不发其他命令
- 收起记忆 sessionStorage per slot(会话内不反复弹出,需求 9)
- 收起记忆 sessionStorage per slot(会话内不反复弹出,需求 9);初始轮询失效上限(连续失败 N=5 次退避至 5×interval,参数 env 可调——评审建议)
- 不污染宿主:零全局(仅一个标记属性)、无样式注入到宿主元素(把手 div inline style)、动态卸载 API 可选(P4b 略)
- [ ] 单测(node 环境模拟 DOM 太薄——用 Playwright 宿主页测试替代,包内 vitest 仅测 src 解析/参数合并纯函数)
- [ ] viewer 静态服务:apps/viewer/public/embed/v1/loader.js(构建时从 packages/embed-loader/dist 拷贝——vite publicDir 静态放 public/ 即可,脚本 build 步骤 copy+校验 hash;dev 直接可访问)
@@ -76,7 +77,7 @@
## Task 7: 回归门 + P4b 验收记录
- [ ] 全仓回归绿 + mobile 不降级;记录数字
- [ ] `docs/plans/progress/P4b-验收记录.md`(A11/A22/A23 组件/A12+T15 证据、偏差表[embed_slot_members 建而未用/权限简化 admin-only/ETag 简化实现/esbuild devDep]、P5 移交[真实域名/CDN 预热/展示位授权 UI 按需])
- [ ] `docs/plans/progress/P4b-验收记录.md`(A11/A22(**简化级:展示位权限 admin-only**,镜像 P2 A25 部分模式)/A23 组件/A12+T15 证据、偏差表[embed_slot_members 建而未用/权限简化 admin-only/ETag 四元组实现/esbuild devDep]、P5 移交[真实域名/CDN 预热/展示位授权 UI 按需/**embed 页 frame-ancestors CSP 配置**/初始轮询失效上限参数化])
- [ ] 总纲 P4b 进度注记(P4b 完成 → **P1-P4b 全部完成,仅余 P5 真实环境接入**);`git tag p4b`;Commit `docs: p4b acceptance record`
---