Commit Graph

132 Commits

Author SHA1 Message Date
浅念 66d1eb3e9f feat(embed,ui04): E01 v2 contract skeleton — displayMode/scrollSpeed/reactionsEnabled, v2 loader entry and fixed 195x280 shell
- contracts: EmbedSlotSettings optional v2 fields (carousel default) + SlotSettingsPayload; gen + redocly green
- api: DTO validation + normalization full-set (cn manage + ov public projection share normalizeStoredSettings)
- embed-loader: params v2 parse/merge/url family, loader-v2.js fixed shell (border/radius/#10161E/safe-area, resize ignored, stack gap 12), dual-artifact build+sync; v1 source frozen
- viewer: EmbedPage v=2 branch, EmbedStream (viewport/track/real anchors/collapse/empty), stream.ts pure fns, i18n zh/en
- admin: types + utils (create default vertical-stream, clampScrollSpeed, v1/v2 snippet selection)
- tests: loader 36 (+9 v2), viewer 222, admin 82, api unit 51, v1 embed e2e 20, v2 E01 gate 2 (195x280 desktop+mobile cross-origin, no footer)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 12:50:28 +08:00
浅念 f8dbc3ab9b docs(ui04): E00 baseline — design refs, plan, v1 measurements and record skeleton
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 12:08:40 +08:00
浅念 f435d7bfc3 docs: UI-03 acceptance record
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 03:16:21 +08:00
浅念 8ee69598bf feat(mobile): UI-03 photos and share pages (17-20)
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 02:45:34 +08:00
浅念 ab51616c70 feat(mobile): UI-03 capture and upload pages (13-16)
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 02:06:23 +08:00
浅念 8bd09eefb9 feat(mobile): UI-03 forms and collaboration pages (09-12)
- Page 09 create: grouped form (Event details / Description & languages),
  required default-language selector, zh-CN/en tabs with per-locale
  title/description/venue/address, explicit timezone field, bottom save
  button kept keyboard-reachable, create mode has no Access tab
- Page 10 edit: Details/Access tabs sharing one draft; Access has four
  switches with explanations; original downloads constrained by downloads
  (disabled + forced off in PATCH); unsaved-changes PopScope confirm;
  save commits all changes in one minimal PATCH
- Page 11 members: Owner group + Collaborators list with initial avatars
  and per-row remove; full-width Invite colleague opens account-search
  bottom sheet (debounced prefix search, race guard, already-added state);
  remove confirm names the member
- Page 12 categories: extracted to standalone page with per-row edit/
  delete menu and New category sheet (language tabs + name + sort order);
  delete confirm mentions photo impact; collaborator browse-only; old
  inline section removed from album detail
- l10n: new form/members/categories keys both locales; regenerated
- tests: form 14, members 6, categories 7, detail nav 1; full suite green

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 01:23:02 +08:00
浅念 5715c608d8 feat(mobile): UI-03 album workspace pages (05-08)
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 00:57:59 +08:00
浅念 95e8417553 feat(mobile): UI-03 theme calibration and account pages (01-04)
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 00:28:39 +08:00
浅念 9d521ff9da docs(ui03): baseline screenshots and acceptance skeleton
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 00:10:50 +08:00
浅念 18c4c3cfbc docs(plan): UI-03 app redesign implementation plan
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 23:45:34 +08:00
浅念 2a489aa16a docs: UI-02 acceptance record and visual verification
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 23:10:58 +08:00
浅念 160e7e5f1a feat(viewer): UI-02 heart particles, interaction notices, share and download
Task 5 (V05 §5.4/§6): decorative heart particles on the lightbox stage
(local like clicks + others' WS bursts for the current photo), compact
cross-photo interaction notices above the action bar (click to locate),
navigator.share-first share orchestration with clipboard fallback for
album and photo deep links (lang param), and download failures attributed
to the photo that triggered them. Self-echo dedup stays in the realtime
router (own batches never produce particles/notices); particles cap at
12 with visibility/unmount cleanup and are suppressed under reduced
motion; notices cap at 3 with ~4s auto-dismiss and throttled SR summary.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 22:20:19 +08:00
浅念 89f45896b7 feat(viewer): UI-02 dark immersive photo viewer with thumbnail strip
V04 (方案 §5):PhotoLightbox 模板重构为深色沉浸布局——顶栏(关闭/位置
计数/⋯ 减少动画菜单)+ 舞台(contain 大图 + 边缘 ‹› + 粒子/互动占位层)+
缩略图条 + 底部操作栏;业务逻辑(序列导航/缩放手势/签名刷新/下载/点赞)
原样保留。

- 新增 viewer/position.ts:§5.2 位置计数真实回退(solo→照片预览;无
  total 未到末页→已加载 N 张;全部加载→i/n;不伪造总数)
- 新增 ThumbnailStrip:当前 ±3 窗口(≤7 项)、主色 2px 描边、thumb 变体、
  滚入可见(遵守减少动画)、点击选择不新增历史
- 新增 PhotoActionBar:Like(coral 心形+计数+连续点赞提示)/Share/
  Download 均分大触控;下载关闭整项移除;原图二级菜单(▾ + 长按)
- 模态可用性:body 滚动锁、焦点进入/困住/归还触发元素、Esc 先收菜单、
  菜单内左右键不抢占、dvh + 安全区、chrome 区手势/滚轮隔离
- 分享 V04 最小实现(复制当前深链,support/clipboard 从 AlbumHeader
  提取共用);navigator.share 编排属 Task 5
- zh-CN/en 新词条;vitest 新增 23 例(位置回退/缩略图/操作栏/焦点/滚锁)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 21:58:09 +08:00
浅念 3d89fcdb99 feat(viewer): UI-02 responsive waterfall with like badges
Task 3 (V03 瀑布流重排, 方案 §4.3):
- responsiveColumns 断点:<600px 双列 gap4 / 600-959px 三列 gap6 / ≥960px 四列
  gap8;PhotoFeed 经既有 ResizeObserver 容器测量整体重排
- 照片卡片去卡片化:图片边到边 + 4px 圆角,无白色厚包裹/无常驻文件名;
  右下角点赞角标(半透明暗底渐变、白心 16px、12px tabular-nums 计数),
  @click.stop 独立交互不冒泡开大图;计数经 Intl 分组
- 新照片提示改白色 pill(viewer.newPhotos,接口仅 hasNew 不伪造数量);
  refreshFromHint 不再强制回顶(用户浏览位置保持)
- 首屏骨架按 skeletonSources 竖/横/方比例走真实瀑布流放置(非均匀网格);
  宽高缺失/NaN/Infinity 回退 4:3 安全比例(无 NaN/无限高度)
- 保留懒加载 IO、游标分页、photoId 稳定键、翻页失败底部重试与已到底文案

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 21:34:56 +08:00
浅念 9c3915b387 feat(viewer): UI-02 visual foundation, top bar and album info
V01+V02 (Task 2 of the UI-02 viewer redesign plan):

- viewer-tokens.css: UI-02 §3.1 design tokens scoped to .viewer-shell
  (never :root), so embed routes consume none of them
- App.vue: normal routes get a sticky white top bar (PhotoLive brand +
  compact locale switcher + lightweight "..." menu holding the
  reduce-motion toggle); global 16px content padding removed so banner
  and photos run edge-to-edge; embed shell branch untouched
- LocaleSwitcher: compact "EN/中文 + caret" trigger with a proper menu
  (current language checked), 44px touch target, Escape/outside close
- AlbumHeader (new): banner (2.4:1 mobile, 320px desktop cap,
  object-fit contain, collapses on missing/failed), 2-line expandable
  title + share button (clipboard copy with selectable-text fallback),
  3-line expandable pre-line description, meta row (Intl-formatted
  view count with em-dash fallback for unknown, album-timezone dates
  with full range on title, venue), Latest/Popular 44px tabs and
  category pills from the API with horizontal scroll + overflow fade
- ActivityPage: delegates the info area to AlbumHeader; sequence
  reset/sort-lock/category-by-id semantics unchanged; desktop content
  capped at 1120px
- i18n: viewer.* strings (brand/share/views/readMore/latest/popular/
  allCategories/noPhotos/newPhotos/reduceMotion/settings) in zh-CN+en
- tests: viewer-shell (token source + normal/embed isolation) and
  album-header (8 cases) specs; activity/i18n specs updated for the
  menu switcher and the em-dash unknown view count; e2e selectors
  refreshed

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 21:22:36 +08:00
浅念 2cbd167cc0 docs(ui02): baseline screenshots and acceptance skeleton
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 21:09:03 +08:00
浅念 3327551f6b docs(plan): UI-02 viewer redesign implementation plan
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 21:01:59 +08:00
浅念 f31388c0c1 docs: UI-01 acceptance record and visual verification
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 20:15:36 +08:00
浅念 ac23a4c40e feat(admin): UI-01 embed slot config form and live preview
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 19:26:48 +08:00
浅念 ddf26a177f feat(embed,viewer): UI-01 complete carousel, loader config and accessibility
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 18:55:15 +08:00
浅念 1f67b7c818 feat(mobile): UI-01 upload center, capture and auxiliary pages
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 17:20:43 +08:00
浅念 f6929ae18f feat(mobile): UI-01 album cards, photo grid and full viewer
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 15:55:44 +08:00
浅念 c94b9e5e58 feat(mobile): UI-01 theme system, shared components and navigation
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 14:55:29 +08:00
浅念 8f4ad11bd5 feat(contracts,api): UI-01 cover, preview, scope=own and embed config extensions
契约扩展(全部可选/可空,旧客户端零感知)+ cn/ov 实现 + e2e 权限矩阵:
- AlbumSummary += cover(kind=cover state=effective 最新行的短期签名 URL,
  列表/详情按页批量组装非 N+1;未配置为 null)
- AdminPhoto += preview{thumb,browse}(变体齐备且未删除才签发;隐藏仅
  admin/owner;协作者仅自己上传,scope=own 服务端过滤 uploaderId,
  成员资格逐请求裁决)
- listAlbumPhotos += scope=all|own(缺省 all 行为不变:仅 owner/admin,
  协作者 403;own 任何有效成员)
- EmbedSlotSettings += imageFit/offsetX/offsetBottom/zIndex(cn PATCH 校验
  → slot-state 投影 → ov 公开回读;settings 为 Json 列免迁移,读侧
  normalizeStoredSettings 兜旧形状默认值)
- Flutter 模型同步(SignedUrl/PhotoPreview/AlbumPhotoScope,可空容错)

e2e:ui01-contracts 9 例(封面选取/字节取回/N+1 计数断言、preview 权限
矩阵、scope 过滤与 403/404/400)+ embed ⑨ 配置链路;全量 e2e 257 通过。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 14:09:35 +08:00
浅念 3b5f8d2d77 docs(ui01): baseline screenshots and acceptance record skeleton
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 13:08:57 +08:00
浅念 6754cd4d44 docs(plan): UI-01 implementation plan from spec analysis
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 12:56:35 +08:00
浅念 92e64b7f10 fix(api): seed-admin works in production containers (no dotenv/workspace)
Production image installs prod deps only — dotenv is a devDep, and the
pnpm-workspace.yaml root lookup throws in containers. Both now degrade
gracefully: env comes from container env vars. Hot-patched into the running
test deployment during 2026-09-21 deploy; committed for the next image build.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 11:32:07 +08:00
浅念 52d8a57751 docs(deploy): single-server dual-stack test deployment plan
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 10:27:02 +08:00
浅念 5cdf828ff5 docs: G1 gate closure recorded in master plan and P5 draft
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 10:17:16 +08:00
浅念 eb8983a06c feat(storage,g1): real-OSS support and G1 smoke — gate CLOSED
G1 (skipped at P1 for lack of credentials) executed 2026-09-21 against the
user-provided bucket live-album @ oss-cn-beijing:

Raw-SDK smoke (scripts/g1-oss-smoke.mjs, creds via runtime env only):
- HeadBucket / presign PUT direct upload / head byte+type verify /
  presign GET round-trip / copy staging→originals / idempotent replay /
  DeleteObject cleanup — all pass.
- S3-compat lifecycle query unsupported → console manual check remains the
  only open G1 sub-item (need: confirm no lifecycle rule hits photo-live/).

Adapter-level (production path, createStorageFromEnv → real OSS):
- presignPut+PUT 200, head verify, presignGet round-trip equal,
  copyIfAbsent idempotent (created:true→false, same etag),
  forbid-overwrite guard throws UPLOAD_VERIFY_FAILED — all pass.

Findings locked in:
- Addressing: OSS S3-compat requires virtual-host style with BASE endpoint
  (path-style → 403 signature mismatch). StoragePort adapter gains optional
  MinioStorageConfig.forcePathStyle + S3_FORCE_PATH_STYLE env (default true
  keeps local MinIO behavior); required-field validation fixed to string
  fields only (boolean false ≠ missing).
- .env.production.example-cn S3 section updated to the confirmed shape
  (beijing endpoint/region, virtual-host, bucket live-album).

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 10:16:33 +08:00
浅念 c26d6b9a73 fix(deploy): review fixes — smoke photoId field, web healthcheck wget, outbox table name, cdn key shape
Reviewer findings on p5-prep batch:
1. BLOCKING: prod-smoke.sh visibility grep used "id" but API items
   serialize photoId (cn public.service / ov public-ov.service) — every
   healthy deployment would false-FAIL release gate ④. 4 sites fixed.
2. web/web-ov healthchecks used curl on nginx:alpine (absent) → perpetual
   false-unhealthy; switched to busybox wget.
3. RUNBOOK rollback rule cited nonexistent sync_outbox → outbox_events.
4. cdn-checklist invalidation sample key shape aligned to actual
   variantsObjectKey layout (.../albums/{albumId}/variants/{photoId}/{version}/{variant}.jpg).

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 02:03:44 +08:00
浅念 d01cecd2f6 docs: p5 preparation complete and implementation draft
- docs/plans/2026-09-21-P5-真实环境接入.md 草案(头部标注待 G5 凭证评审
  转正):T1-T12 任务序(每任务 产出/依赖用户输入/关口)——G1 OSS 冒烟→
  国内起栈→ov 部署+bootstrap 对账→域名证书→CDN(含图片加速三选一)→
  跨境 MySQL 通道定案(白名单+SSL/隧道/HTTP 化三选项,含改造量评估)→
  G4 用户源→故障演练→压测 D05→G2 真机→G6 试运行→发布交接;用户输入
  汇总表 + 决策 D03-D13 状态速查 + 风险表(跨境链路/双层签名/MySQL
  8.0.30 门槛/镜像仓库通道等)
- 总纲 P5 行注记:准备件完成 2026-09-21,实装待 G5 + G4
- P5-prep 计划全项勾选回填(评审项注明自审已修 + 独立 reviewer 由编排方追加)
- 自审修正:monitoring 统计副本表名(无 stat_snapshots 表,副本位在
  albums/photos 行)与 outbox_events.occurredAt/photo_jobs.updatedAt 列名、
  jq 管道括号;cdn 失效多 URL 换行分隔;k6 helpers 请求体用 ArrayBuffer
  原生形态、read-feed 身份串延迟生成(init 上下文 VU 编号恒 0)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 01:56:56 +08:00
浅念 f71a887f17 test(loadtest),docs: monitoring, cdn checklist and k6 framework
- monitoring.md: 指标→实现映射(1Panel+云监控+结构化日志,不引入额外监控
  系统);pino 字段表如实列自代码(访问日志 req.id/res.statusCode/
  responseTime + sync/worker 自定义 msg 字段);六类告警 jq 检索语句与
  阈值建议总表;日志保留(compose json-file 10m×3 已设 + 每日归档建议)
- cdn-checklist.md: 全步骤待凭证执行;图片加速三选一架构核对(双层签名
  冲突分析,推荐 c 起步=CDN 仅加速 viewer 静态、presigned 直链不变,
  图片收益后评估 b=CDN 鉴权+私有桶回源授权);缓存规则对齐源站 immutable;
  失效/预热 CLI 样例与多地域冷缓存测试点模板
- loadtest/: k6 框架——read-feed.js(N 观众四 mix:latest/hot 分页+大图+
  点赞+浏览,ramp 阶段)、upload-burst.js(M 并发上传全链含 complete 与
  发布/同步可选轮询,兼作数据种子)、lib/helpers.js(登录/建册/上传全链
  +内嵌 JPEG fixture 与 prod-smoke 同源)、README(运行方式/参数表/
  P95 输出解读对齐计划 §6 候选指标;D05 未定→阈值占位注释)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 01:52:47 +08:00
浅念 a79fff5b9c docs(deploy): runbook with smoke script and emergency channel
P5-prep Task 2(docs/plans/2026-09-21-P5-准备件.md):

- infra/deploy/RUNBOOK.md:发布前检查单(备份/水位快照(outbox_events
  deliveredAt、sync_state appliedVersion)/镜像 tag/env diff/磁盘);发布步骤
  (迁移先行 → ov 后 cn 逐容器替换 → 四点冒烟 → 观察);回滚步骤(镜像回旧
  tag、迁移不回滚的向前兼容原则、outbox/inbox/水位账本硬性保留、冻结 sync
  的故障处置);紧急停用通道(ov emergency HMAC 调用样例,openssl dgst 可
  复制片段,签名公式与 internal-sync.ts 实现交叉验证一致);故障速查三类
  (同步积压/计数差异/CDN 缓存与隐藏照片失效窗口)。
- scripts/prod-smoke.sh:四点冒烟(两地域 health → 登录 → 时间戳相册 + 内嵌
  800x600 JPEG 真实 presign PUT → complete → cn/ov 公开读轮询 ≤60s),
  PASS/FAIL 摘要 + 软删照片清理;--dry-run 打印调用序列、--skip-ov/--ov
  跳过 ov 段、--wait-seconds 可调;仅依赖 bash+curl+openssl+base64。

验证:bash -n 通过;--dry-run / --skip-ov / 用法错误三路径本地实测输出正确
(含密码 JSON 转义);真实栈未起(无凭证,符合准备件边界),首次真实验证
点在 README-1Panel.md §7 演练步骤。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 01:40:04 +08:00
浅念 250d4355b9 build(deploy): production images, compose stacks and 1panel guide
P5-prep Task 1(docs/plans/2026-09-21-P5-准备件.md):

- docker/server.Dockerfile:单镜像 photo-live-server 多角色(api-cn/api-ov/
  worker/sync/migrate-deploy/seed-admin,entrypoint.sh 分发;migrate-deploy
  支持 cn|ov|all 区域参数)。三阶段:全量 install(锁文件冻结)→ prisma
  generate 双库 + 逐包构建;--prod 依赖层;runtime=node:20-alpine+openssl/curl
  + 全局 prisma@5.22.0(对齐锁文件)+ 双库 client 镜像内重建(db-cn 落 pnpm
  store .prisma、db-ov 落包内 generated/)。sharp 走 musl 预编译验证通过。
- docker/web.Dockerfile + infra/deploy/nginx.conf.template:photo-live-web
  (nginx:alpine + envsubst 模板)。viewer(/,history 回退) + admin(/admin/,
  hash 路由) + /v1 反代 API_UPSTREAM(含 /v1/realtime WS 升级)+
  /assets/、/admin/assets/、/embed/v1/ immutable 长缓存(admin assets 的
  alias 前缀剥除坑已实测修复)。VITE_* 为构建期参数(VIEWER_BASE/
  EMBED_ORIGIN 随域名构建传入)。
- infra/deploy/docker-compose.cn.yml / -ov.yml:cn=api-cn+worker+sync+web,
  ov=api-ov+web-ov(核实架构:sync 仅 cn 侧,inbox/emergency 内嵌 api-ov,
  无 ov 侧 sync 进程);健康检查 /v1/health、unless-stopped、json-file
  10m×3、外部 1panel-network 复用 1Panel MySQL/Redis、SERVER_IMAGE/
  WEB_IMAGE 环境变量化镜像 tag(回滚杠杆)。
- .env.production.example-cn/-ov:生产变量全量清单(cn 25 键/ov 24 键,
  DB/Redis/OSS/SYNC/AUTH/限速/域名分组注释,源自 services+packages
  process.env 全量审计);.gitignore 放行 example,真实 .env.production-*
  永不入库。
- infra/deploy/README-1Panel.md:镜像导入双路径(save tar 导入 / 仓库 pull)、
  MySQL 建库授权 SQL(utf8mb4_0900_bin,<8.0.30 回退 utf8mb4_bin——D12)、
  Redis 连接、1Panel 编排、OpenResty 反代+证书、migrate-deploy 一次性容器、
  首次部署顺序(DB→迁移→ov 栈→cn 栈→seed→冒烟)。
- 根 package.json pnpm.onlyBuiltDependencies 增补 sharp/prisma/@prisma/
  engines/@prisma/client(镜像构建所需 install 脚本白名单)。

本地验证:两镜像构建成功(server 988MB / web 97.7MB);compose config 校验
通过;server 镜像内逐服务 require(sharp/argon2/双 Prisma client/bullmq/
ioredis)与双引擎 so.node、migrate-deploy 角色、无 .env 泄漏均实测;web 镜像
容器实测路由(viewer 回退/admin 子路径/assets 与 embed immutable/WS 代理路径
502 语义)全通过。未起真实栈(无真实 DB,符合准备件边界)。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 01:39:55 +08:00
浅念 12059d9a0e docs(plan): p5 preparation plan
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 01:00:52 +08:00
浅念 22188ce3ee docs: p4b acceptance record
- P4b 验收记录(A11/A22 简化级/A23 组件/A12+T15 证据、偏差 16 项、
  P5 移交清单、测试统计 web 655 +66 / mobile 280 零漂移)
- 总纲 P4b 进度注记:P1-P4b 全部本地阶段完成,仅余 P5 真实环境接入
  (G5/G6 + D03/D05/D08/D09/D10/D13)
- P4b 计划勾选回填 + 完成头注

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 23:59:21 +08:00
浅念 7385785504 feat(i18n): T15 full-stack language audit with coverage assertions
- viewer/admin 错误码→文案映射按契约 ErrorCodes 枚举全集登记(机器守护
  spec 遍历枚举断言无缺漏 + 映射键在 zh/en 词条均存在 + locale 键集
  递归相等);mobile 镜像补齐 DOWNLOAD_DISABLED(P4a 增补漏登)
- 漏译修复:viewer.photoRemoved en 侧缺失(审计测试捕获);
  DOWNLOAD_DISABLED 观众侧专属文案 + useDownload 专属提示接线;
  admin apiMsg 增 RATE_LIMITED/ALBUM_NOT_PUBLIC/DOWNLOAD_DISABLED/
  SYNC_AUTH_FAILED 四码词条
- 运行时抽查:admin 主旅程 en 版 e2e(登录→相册管理全英文 + 偏好
  持久);embed iframe lang=en 渲染与深链跟随 e2e(zh 侧既有用例覆盖)
- contracts 新增 ./errors 浏览器安全子路径(errors.ts 纯 TS);
  admin 增 @photo/contracts devDep(仅测试消费)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 23:44:27 +08:00
浅念 0238f505c4 feat(admin): embed slot management with code snippet
P4b Task 5:/system/embed-slots 管理页(Task 8/9 页面模式:useCursorList
游标累积 + 对话框治理)。列表(名称/绑定相册/启停/configVersion/更新时间,
启停开关行内 PATCH → configVersion+1 toast 提示)+ 新建/编辑对话框
(设置表单位置/尺寸/间隔/语言/默认收起,intervalMs 契约区间夹取;编辑按
整体替换语义提交完整 settings)+ 绑定相册对话框(相册标题/ID 搜索选中 →
确认框携带 A22 文案「configVersion 将递增,官网无需改动」;清空即解绑)+
嵌入代码卡({EMBED_ORIGIN}/embed/v1/loader.js + data-slot 令牌,一键复制;
EMBED_ORIGIN=VITE_EMBED_ORIGIN 缺省本机 viewer dev,.env.example 登记)。

- API 层 src/api/embedSlots.ts + types/api/embedSlot.d.ts(契约镜像)
- vitest 9 例:表单→载荷(缺省+夹取)、编辑载荷收敛、snippet 拼接、
  环境基址兜底、绑定变更分类、zh/en 键一致 + A22 短语断言
- Playwright e2e(真实 cn+ov 后端):新建→绑定(gov-photos 直种 +
  seed-fixture 新增 sync-flush 模式经 bootstrap/真实发送管线投影 photos_ov)
  →嵌入代码卡含 token→ov 公开 feed 返回相册照片→换绑 configVersion+1 +
  feed 切换(A22)→停用 feed 404;admin playwright 配置补 ov webServer
- i18n zh/en(embedSlotsManage.* + menus.system.embedSlots)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 23:25:59 +08:00
浅念 8f2c21d25a feat(viewer): embed carousel iframe with visibility pause
P4b Task 4:宿主 loader 以 iframe 载入的轮播页(/embed/:slotToken,裸布局)。

- src/embed/feed.ts:纯函数集——A22 旧响应裁决(dispatch seq + configVersion
  双闸:过期/回看丢弃、等值差分合并、升值全量重置)、照片队列差分(新照片
  追加队尾、缺席淘汰、thumb 签名刷新)、interval/高度夹取、轮询退避;
- src/embed/useEmbedFeed.ts:取数编排(If-None-Match → 304 零处理)、轮询
  (查询 interval 覆盖 → EMBED_POLL_INTERVAL_MS,连续 5 失败 ×5 退避,成功
  归零;404/410 静默 collapse 隐藏自身)、A23 不可见暂停(document 隐藏 +
  IntersectionObserver 滚出视口 + 本地收起;恢复立即校准)、轮播 intervalMs
  切换;__embedPollMs e2e 注入点;
- src/embed/postMessage.ts:上行 resize/collapse(referrer origin 收敛,缺失
  退 '*'——载荷无敏感数据,宿主侧 loader source+origin 双校验)、下行 expand
  (event.source === window.parent 白名单);
- EmbedPage.vue:交叉淡入淡出(仅 thumb 小图省流量)、点击 window.open 单图
  深链(noopener)、计数圆点、「照片即将上线」空态卡、×收起;lang 查询
  (auto→相册 defaultLocale)驱动嵌入词条子集;
- api client:fetchPublicEmbedSlot(304/ETag 分支,不走 apiRequest);
- e2e:宿主夹具页 public/__test__/embed-host.html(仅测试资产)+ embed.spec 7
  用例(轮换/新标签深链/收起记忆/窄屏 sm/轮询增量/换绑重置/空态/幽灵槽);
  helpers 增展示位 admin API 种子 + flushOutbox;
- vitest 18 用例(裁决矩阵/队列差分/夹取/退避/304/可见性状态机)。

配套修正:api CORS exposedHeaders ETag(跨源浏览器可读,304 语义成立);
contracts 新增浏览器安全子路径 ./constants(根出口 re-export internal-sync
的 node:crypto 会打进浏览器包);eslint ignore embed-loader 产物拷贝
(Task 3 起 viewer lint 红,先修)。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 23:02:07 +08:00
浅念 ddb4c8175f feat(embed): versioned loader with postMessage whitelist
packages/embed-loader:手写零依赖加载器(esbuild bundle+minify→dist/v1/loader.js,
IIFE target es2018 4.7KB)。自发现 EMBED_ORIGIN(currentScript 兜底扫描)、
data-slot 必填缺失静默 no-op、__photoliveEmbedV1 标记去重+同槽去重、
窄屏 <480px 强制 sm+默认收起、postMessage 双向白名单(source+origin 严格
校验,resize 夹取 60-640)、sessionStorage 收起记忆、零全局零 console
全静默。sync 脚本携 sha256 校验拷贝至 viewer publicDir(两份产物入库);
src/params.js 为 loader/单测共用参数源(11 vitest 用例)。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 22:31:40 +08:00
浅念 aee4b7ac46 feat(api): embed slots with ov replica and public feed
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 22:22:19 +08:00
浅念 eb51069a12 feat(contracts): embed slots admin and public endpoints
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 21:49:09 +08:00
浅念 e9a7dc8740 docs(plan): p4b review fixes (etag recipe, sandbox origin, narrow screen)
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 21:41:19 +08:00
浅念 ec7837ebb0 docs(plan): p4b implementation plan (pending review)
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 21:38:37 +08:00
浅念 17d09bbb01 docs: p4a acceptance record
P4a Task 10 收口:回归门全绿(web lint/typecheck/build 9/9 包任务,
单测 271 + e2e 318 = 589;mobile analyze 0 issue + 280/280 零漂移)。
验收记录含 A16/A17/A18/A20/A23/A29 证据、D06 工作默认登记、契约
增补汇总(36→51 operations + DOWNLOAD_DISABLED)、偏差 14 项、
G4 待真实接口清单(UserSourcePort 替换点 + D08 字段确认)。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 21:21:08 +08:00
浅念 f08d68bebf feat(admin): batch ops, transfer, watermark, usage and qr share pages
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 21:10:30 +08:00
浅念 618d1d796c feat(admin): account governance and user sync pages
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 20:26:53 +08:00
浅念 3681999667 feat(api): scheduled user sync with mock source (T30 semantics)
P4a Task 7 —— cn 侧用户定期同步(mock 源先行,G4 真实接口到位后仅替换
UserSourcePort provider,引擎/契约/表不变)。

数据模型(db-cn 迁移 20260920114149,utf8mb4_0900_bin 逐表校对):
- user_sync_runs:批次账本(trigger cron|manual、state running|success|
  partial|failed、cursor 水位、counts JSON、起止时间与错误摘要);
- user_sync_errors:单批次坏行明细(runId 索引);
- external_user_mappings:本地↔外部 ID 映射(@@unique([sourceSystem,
  externalUserId]),sourceVersion 逐行高水位);
- user_sync_state:单行状态(lastRunAt/lastSuccessAt/cursor/runCount)。

同步引擎(UserSyncEngine):
- Redis SETNX 分布式互斥(TTL 10min,锁值=runId,Lua CAS 释放);
  定时 tick 与手动触发共用,运行中批次复用 runId(202);
- 游标增量 + 周期全量对账(RECONCILE_EVERY_N_RUNS 默认 10);
- 逐页单事务提交(run counts+cursor / state cursor),分页中断→failed
  且水位不前进、会话不动;
- 红线遵守:sync 不写 password_hash/role/album_members/localLocked;
  空/部分页不误伤停用;上游恢复不解除本地锁定;
- 新用户按 pending 建(随机不可用哨兵哈希,激活+重置后方可登录);
- 停用即撤销该用户全部会话;本地预建同名账号走关联而非重建。

Admin API(/admin/user-sync):GET status|runs|errors?runId=、POST runs
(202 {runId});runs 为 startedAt+ID 倒序游标分页。

auth.service:登录对哨兵/损坏哈希加固(等代价 dummy verify,
401 而非 500,时序平坦)。

测试:user-sync.e2e-spec 12 用例(首轮全量/改名幂等/停用撤会话/
恢复不解锁/新增/空页/分页中断/部分无效/手动互斥/增量+对账/API 形状
与权限/pending 登录门);api e2e 22 套件 239 用例全绿。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 20:01:17 +08:00
浅念 f4a1ba7a14 feat(api): admin account governance endpoints
P4a Task 6: GET /admin/users 双模式收口(无 accountPrefix 进入管理列表
模式:全量用户、query/status 过滤、createdAt+ID 倒序键集游标、仅 admin;
前缀模式 P1b 语义保留)+ PATCH / reset-password / activate / deactivate
治理端点(sourceStatus 只读拒绝、自我保护、会话撤销、审计、生成初始
密码)。附带修复 full-chain e2e 的 job-done 断言竞态(轮询条件补齐
photo_jobs 终态)。api e2e 214 → 227 全绿。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 19:31:00 +08:00