Skip to content

feat(extension): batched data sync with retry (Week 2 AC 5)#10

Merged
BODMAT merged 9 commits into
developmentfrom
feature/extension-data-sync
May 23, 2026
Merged

feat(extension): batched data sync with retry (Week 2 AC 5)#10
BODMAT merged 9 commits into
developmentfrom
feature/extension-data-sync

Conversation

@BODMAT

@BODMAT BODMAT commented May 23, 2026

Copy link
Copy Markdown
Owner

What

Closes Week 2 AC 5: extension batches captured events and notes, POSTs them to /api/v1/events every 30s with retry on network/5xx, plus immediate flush on session stop. Also closes Gaps 2/3/4 from the AC 2+3 plan (DB-side sessions API, normalized payload shape, note URL).

Changes

Code

  • dashboard/server/jwt.ts — new requireUser(req) helper that verifies the Bearer token via JWT_SECRET and throws UnauthorizedError on missing/invalid token.
  • dashboard/app/api/v1/events/route.ts — now calls requireUser and createEventForUser, which checks Session.userId matches the token's user.
  • dashboard/app/api/v1/sessions/route.ts and dashboard/app/api/v1/sessions/[id]/route.ts — new POST (creates a Session for the authenticated user) and PATCH /:id (sets endedAt, scoped to owner).
  • dashboard/server/sessions.ts + dashboard/server/schemas/sessions.ts — DB layer + Zod for the session id path param.
  • extension/src/types/session.ts, pending.ts, sync.ts — adds Session.dbSessionId, wire-shape PendingEvent, and SYNC_GET_STATUS message.
  • extension/src/background/session.ts — adds attachDbSessionId(id) helper.
  • extension/src/background/sync.ts — new module: alarm registration, flush() with per-event POST loop, getStatus().
  • extension/src/background/index.tsSESSION_START triggers DB-session create; SESSION_STOP does a final flush before clearing; PAGE_METADATA / NOTE_ADD now normalize payload at enqueue; AUTH_GET_STATUS returns the email decoded from the JWT.
  • extension/src/popup/ — email line under the logo, second-row sync status ("synced Xs ago" or "error: …").

Tooling / config

  • extension/src/manifest.ts — adds "alarms" permission (silent in Chrome's permission UI).
  • dashboard/next.config.ts — pins turbopack.root to the dashboard/ directory so the dev watcher does not crawl the whole monorepo (two lockfiles were forcing inference to the repo root).

Docs / plan

  • plans/week2/AC5-data-sync.md — full design rationale, commit order, and verification checklist.

Design decisions

  • Shared JWT helper applied to both /api/v1/sessions and /api/v1/events — without it, sessions endpoints would allow creating sessions for arbitrary userId, and the events route would accept anything from a Chrome-extension Origin. Formally bleeds into Week 3 AC 1, but the alternative is leaving a write-anything hole open for two weeks.
  • Note URL = worktrace://note/<uuid> — keeps CreateEventInput.url = z.url() strict (custom schemes are valid URLs) and avoids a Prisma migration to make url nullable.
  • content flattens at enqueue, not at flush[metaDescription, ...headings].filter(Boolean).join(" | "). One canonical shape in pendingEvents; sync just attaches sessionId.
  • 30s chrome.alarms + final flush on stop, no in-batch retries — the next alarm IS the backoff. Simpler than per-event timers; the service worker can be killed between ticks without losing state because the queue lives in chrome.storage.local.
  • 4xx drops the event, 401 stops the batch, 5xx keeps the queue — a poison payload otherwise blocks every subsequent event in the queue forever.
  • Email decoded from JWT in the service worker — no extra storage key, no extra request. Cleared automatically on logout because the JWT itself is cleared.

See plans/week2/AC5-data-sync.md for full reasoning.

Verification

  • npx tsc --noEmit (dashboard) — clean
  • npm run lint (dashboard) — clean (via lint-staged on each commit)
  • npm run build (extension) — clean
  • Manual test: signed in via Google, started a session, browsed a few pages, accumulated events in pendingEvents with the right shape, triggered flush, verified Events in DB with the correct sessionId. Added a note → arrived with url: worktrace://note/<uuid> and tag note. Email shown under the logo while authenticated, hides on logout.

Out of scope (future PRs)

  • JWT validation in Next.js middleware.ts for /dashboard/* — Week 3 AC 1.
  • Bulk events endpoint (POST /api/v1/events accepting an array) — current per-event POST in batches of 50 is enough for MVP.
  • Track endpoint — Week 2 bonus AC 6.
  • Note as a separate Prisma model — synthetic URL covers AC 5 needs.
  • Email + password auth — see plans/backlog/email-password-auth.md, explicitly post-Week 3.
  • Pinning the extension ID via manifest key so Google OAuth redirect URI stays stable across machines.
  • Google OAuth Consent Screen branding (App name, logo) — UX polish.

Closes

  • Week 2 AC 5 — see docs/Task.md (AC 5 section)
  • Gaps 2/3/4 from plans/week2/AC2-AC3-content-sessions.md

BODMAT added 9 commits May 23, 2026 12:46
Verifies Bearer token via JWT_SECRET, checks session ownership on POST.
POST creates a session for the authenticated user. PATCH /:id sets endedAt, scoped to the user's own sessions.
…retry

Adds dbSessionId to local Session, fire-and-forget POST /api/v1/sessions on start and PATCH on stop. Failures left for sync alarm to retry.
PageMetadata flattens to content via metaDescription + headings. Notes get worktrace://note/<uuid> URL so z.url() passes without schema changes.
Every 30s a chrome.alarms tick flushes up to 50 queued events. Final flush also fires on SESSION_STOP. 4xx drops poison events, 401 stops until re-login, network and 5xx keep the queue for the next tick.
Adds a SYNC_GET_STATUS message that returns lastSyncedAt, lastError and queueSize. Popup renders a second line under the queue count showing time since last sync or the error message.
Two lockfiles (root husky/lint-staged and dashboard) made Turbopack infer the repo root as workspace root, watching extension and unrelated paths and slowing dev. Setting turbopack.root scopes the watcher.
AUTH_GET_STATUS now returns the email decoded from the JWT payload. Popup renders it under the logo while authenticated, hides on logout.
@vercel

vercel Bot commented May 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
worktrace Ready Ready Preview, Comment May 23, 2026 10:52am

@BODMAT BODMAT merged commit c627da8 into development May 23, 2026
4 checks passed
@BODMAT BODMAT deleted the feature/extension-data-sync branch May 23, 2026 10:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant