Commit Graph

11 Commits

Author SHA1 Message Date
浅念 eac8da6d6c feat(ov): emergency disable channel independent of sync
P2 Task 5(海外紧急停用入口):
- ov 新增 POST /v1/internal/emergency/disable|enable(InternalSyncGuard
  HMAC,albumToken/albumId 恰好其一、都缺省 400;副本不存在 404
  ALBUM_NOT_FOUND):直写 albums_ov.emergencyDisabled(唯一 ov 本地可写
  位),独立于同步版本链——更高版本 album.updated 重放不解除(e2e 断言
  entityVersion 推进而标志保持),恢复仅经显式 enable;幂等语义
  (alreadyDisabled/wasDisabled 入审计行)
- cn 侧不感知:无对应 cn 状态位、无反向同步(契约描述文档化)
- 审计:pino child(audit=emergency 字段常驻每行)结构化日志——偏差
  登记:计划原文「写 ov 侧日志文件」落地为字段标记而非独立文件通道
  (免新增文件 transport 依赖,P5 运维按字段路由聚合);注入令牌
  EMERGENCY_AUDIT_LOGGER,测试覆写为内存目的地断言字段
- isAlbumVisibleOv 共享判定(status=active && publicEnabled &&
  !emergencyDisabled——镜像 cn 公开语义叠加本地停用位):Task 6 公开
  API / Task 7 WS subscribe 的 404 ALBUM_NOT_PUBLIC 语义单一实现,
  判定矩阵单测锁定
- 契约 [internal] 增 emergency disable/enable 操作与 schema(gen 已跑、
  redocly 通过);e2e:token/id 定位、幂等、enable 恢复、未签名 401、
  均缺省 400、未知相册 404、同步事件不解除

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-18 16:19:02 +08:00
浅念 36082c5a74 feat(sync): reconciliation scheduler with digest and bootstrap healing
P2 Task 4(inbox 对账):
- services/sync 新增 SyncReconciler(runOnce 可测;main 双循环,间隔
  SYNC_RECONCILE_INTERVAL_MS env 可调,CN_API_URL 指向 cn 入口):每轮
  先按 album/category/photo/asset 逐类分页拉取 cn reconcile「当前状态
  事件」并直接签名推送到既有 content-batches 管道。sinceVersion 取 0
  (全量当前态)而非 per-type 已应用最大版本——cn reconcile 以类型级
  全局版本阈值过滤,类型内单实体滞后会被 max 掩蔽;ov 的 eventId 幂等
  + 版本单调把已追平实体全部判重(recon 确定性 eventId 重扫零成本),
  偏差登记:计划原文对账返回「缺失清单」,契约已定为可重放 SyncEvent
  数组(免建第二条拉取通道),openapi 描述同步更新
- cn 新增 POST /internal/sync/digest:每相册权威计数 {categoryCount,
  publishedPhotoCount, photoCount, assetCount};ov 比对副本计数(照片
  取 [publishedPhotoCount, photoCount] 区间口径——hidden/deleted 照片
  若从未以 published 态同步过 ov 合法无行,缺席即撤下),不一致相册
  自动触发 cn bootstrap 重建;ov-only 相册(cn 行已删,bootstrap 无法
  表达删除)告警登记不自动处置
- 收敛语义两处落位:① sync-inbox 版本比较改「严格更旧才拒绝」——
  同版本新 eventId(bootstrap/reconcile 快照重发)为当前态幂等重断言
  应用,修复 ov 缺失副本行;A26 不受影响(更旧版本仍拒绝,cn 侧
  entityVersion 与状态一一对应)② album.updated 快照载荷(bootstrap/
  reconcile)携带分类全量清单,ov 存在即整体替换 albums_ov.categories
  ——修复错过的 category 墓碑(Task 3 登记边界:cn 行已删,无现存实体
  事件可重放,唯全量替换可收敛)
- e2e(reconcile.e2e-spec):漏事件追赶(ov v1/cn v5 事件投后即失)→
  清扫追平;digest 修复缺失 photos_ov 行(bootstrap 重发 → 同版本
  重断言重建);digest 修复丢失的分类墓碑(全量清单替换收敛);稳态
  空转(applied=0、无 bootstrap);Task 3 套件保持绿

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-18 16:18:31 +08:00
浅念 65726702f3 feat(sync): cn event coverage, bootstrap and outbox sender
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-18 15:21:41 +08:00
浅念 24327c5147 feat(contracts): p2 realtime, likes, visits and internal sync contracts
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-18 13:34:35 +08:00
浅念 c2a9cc51f3 feat(contracts): p1b asset upload, management photo list, account lookup
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-18 08:23:59 +08:00
浅念 47fb81ad31 chore(api): p1a final-review hygiene items
- contracts: add NOT_FOUND error code (framework-level 404 for unknown
  routes); Error enum + regen committed
- api: 404 default mapping in AllExceptionsFilter now NOT_FOUND (was
  ALBUM_NOT_FOUND); business 404s unchanged via explicit ApiException codes
- api: extract configureApp (prefix/CORS/x-powered-by) shared by main-cn
  bootstrap and e2e; disable x-powered-by, asserted in e2e
- api: move parseCursor/parseLimit (+page-size bounds) from
  albums.controller to shared/pagination; members/uploads/public re-pointed
- infra: bind compose published ports to 127.0.0.1 (mysql/redis/minio);
  .env.example endpoints follow suit
- e2e: unknown-route 404 NOT_FOUND + x-powered-by absent cases added
  (api e2e 120 -> 122)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-18 08:18:30 +08:00
浅念 39cae26106 feat(api): password login, sliding renewal, revocable sessions
- POST /v1/auth/password|refresh|logout per contract operationIds;
  long-lived single token (32B hex, DB stores sha256 only), sliding
  renewal without rotation, logout sets sessions.revokedAt.
- SessionGuard: Bearer -> session -> user eligibility (disabled/locked/
  pending revoke access); all failures map to 401 AUTH_TOKEN_EXPIRED.
- Anti-enumeration: unknown account / wrong password / status-blocked
  share one 401 AUTH_INVALID_CREDENTIALS; unknown-account path runs a
  dummy argon2 verify to equalize timing.
- Login rate limiting (P1): in-memory account+IP sliding window,
  5 failures / 15 min -> 429; success clears. Contract amendment
  (registered): new ErrorCodes.AUTH_RATE_LIMITED added to errors.ts +
  openapi Error enum + 429 response on /auth/password; generated types
  regenerated and committed. P1a single-instance limiter; P5 needs a
  Redis-backed implementation.
- Global ValidationPipe (APP_PIPE) enforces class-validator DTOs ->
  400 VALIDATION_FAILED via the existing exception filter.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-17 21:17:16 +08:00
浅念 b4ee92de96 docs(contracts): locale fallback wording on public endpoints
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-17 18:11:21 +08:00
浅念 f6448bd006 fix(contracts): quality review fixes for p1a contracts
- status enum active|archived -> active|offline (align requirement docs)
- UploadSessionState session-state semantics documented (no failed state)
- maxLength: clientTaskId 128 / filename 255 / cursor 512
- PublicPhoto width/height required (layout placeholders)
- reusable Unauthorized/InternalError responses (hybrid with inline op-specific codes)
- PublicAlbum.translations -> translation (single resolved object)
- contentType enum [jpeg/png/heic] (heic provisional pending G2)
- minLength 1 on title/name; publicToken pattern ^[0-9a-z]{20,32}$
- CursorPageMeta hasMore/nextCursor invariant documented
- public locale: silent fallback to defaultLocale (tech doc section 11)
- plan: upload_sessions state comment aligned; P1b adds management photo list + account lookup endpoints

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-17 18:06:54 +08:00
浅念 1dae00eaef feat(contracts): p1a endpoint contracts
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-17 17:31:11 +08:00
浅念 656498d748 feat(contracts): openapi base, error codes, type generation
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-17 17:04:12 +08:00