Adds `server.warmup.clientFiles` so Vite eagerly transforms the
journal entry + every page route at server startup. Without it,
the first visit to a previously-unseen route triggers Vite's
"new dependencies optimized → reloading" path, which is a full
page reload — fine as a one-time blip in everyday dev, painful
when an e2e run is hitting many routes back-to-back on a fresh
server (each new route reload races with whatever the test was
doing).
This is a partial improvement, not a complete fix, for the local
auth e2e flakiness investigated earlier today. The deeper issue
there is a separate React-hydration-vs-Playwright-click race in
dev mode (production CI doesn't see it because production JS
hydrates faster). That needs a different fix at the test/fixture
layer; this commit just removes one source of noise from the
investigation by eliminating the dep-discovery reload.
Glob picks up `.tsx` files only — `.ts` route handlers under
app/routes/ are server-only API endpoints and Vite refuses to
transform them for the client (errors "Server-only module
referenced by client"). Page components alone drive the
dep-discovery we care about.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Most contributors don't need HTTPS=1 locally — plain HTTP is the
default and the right choice for everything except Wahoo OAuth
callback testing. WebAuthn (passkeys), magic links, sessions, the
Terms gate, SSE all work over HTTP because the WebAuthn spec treats
localhost as a secure context regardless of scheme. CI proves the
point: the e2e suite runs over plain HTTP and passes cleanly.
The original HTTPS=1 plumbing landed in 20b91ef (2026-04-05) bundled
into a Wahoo import fix, with no inline rationale. Once you've
forgotten the reason it tends to leak into the default workflow,
which then breaks the local e2e suite (Playwright always uses HTTP
baseURL; an https ORIGIN env mismatches what it sends) and creates
unnecessary divergence from CI.
Documenting the single legitimate use case so the next contributor
(or future-me) doesn't have to re-derive it from git blame:
- apps/journal/vite.config.ts — expanded the comment near the
basic-ssl plugin to spell out:
* What works on HTTP (everything except Wahoo)
* What needs HTTPS=1 (Wahoo OAuth specifically)
* The norm: don't add new HTTPS-only paths without writing them
down here too, so the assumption stays auditable
- apps/journal/.env.example — new tracked file showing the env vars
the journal app reads, with `ORIGIN=https://localhost:3000`
marked as HTTPS-only and accompanied by an explanation of the
ORIGIN/Playwright mismatch trap. Future contributors don't have
to discover this through a failing e2e run.
- CLAUDE.md — new "Local HTTPS dev (rare)" subsection under
Development Commands. Includes the exact command for Wahoo testing
(`HTTPS=1 ORIGIN=https://localhost:3000 pnpm --filter
@trails-cool/journal dev`), the turbo-doesn't-pass-HTTPS gotcha,
and the rule of thumb: don't set ORIGIN in your .env unless you
also always run with HTTPS=1.
No code/behavior changes; documentation only.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the notifications system end-to-end (4 types, payload-versioned
JSONB, SSE-based live unread badge, /notifications page, mark-read API,
fan-out job for activity_published, daily 90-day retention purge).
Bell icon in the navbar with unread badge.
Side-findings from exercising the change:
- Add 6-digit magic code to registration (mirrors login UX, mobile
paste-friendly), with `[Register Magic Link]` console line in dev so
the code is reachable without a real email transport.
- Manual passkey/magic-link toggle on the register form (login already
had it).
- Restrict ALPN to http/1.1 in HTTPS dev so React Router's
singleFetchAction CSRF check (Origin vs. Host) passes — Node doesn't
synthesize Host from h2's :authority. Plain HTTP dev unaffected.
- Followers/Following routes now use the locked-account rule from the
profile route (owner + accepted followers see the list; others 404).
Profile page renders the count chips as plain spans for viewers who
can't see the lists, so private profiles don't surface dead links.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Android 9+ blocks cleartext HTTP by default. Enable usesCleartextTraffic
so the emulator can reach the Journal dev server at http://10.0.2.2:3000.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bugs fixed:
- FIT parser returns timestamps as Date objects, not strings — convert
to ISO 8601 before passing to generateGpx (caused str.replace crash)
- FIT parser already converts semicircles to degrees — remove redundant
conversion that produced near-zero coordinates
- Wahoo CDN URLs are pre-signed S3 URLs — remove Bearer auth header
from download requests (caused 400 "Unsupported Authorization Type")
- Filter out third-party workouts (fitness_app_id >= 1000) since Wahoo
does not share their data via the API
UX improvements:
- Individual imports use fetcher (no page refresh), button shows
"Importing..." inline
- "Import all" button imports all unimported workouts on the page
sequentially with progress indicator
- Add @vitejs/plugin-basic-ssl for local HTTPS dev (opt-in via HTTPS=1)
Also adds unit test for FIT-to-GPX conversion with real fixture file,
and updates wahoo-import spec to reflect all behaviors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Disable Sentry Vite plugin telemetry (telemetry: false)
- Pass SENTRY_RELEASE as build-arg (not secret) since it's just the git sha
- Keep only SENTRY_AUTH_TOKEN as Docker secret
The release version was showing as *** because Docker secrets get masked
by GitHub Actions. Git sha is not sensitive — safe as a build arg.
The sourcemap warning from react-router is a known upstream issue (ships
without sourcemaps) and is harmless.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Set Sentry user context (id, username) in Journal root for all routes
- Tag Planner errors with session_id
- Use reactRouterV7BrowserTracingIntegration for route-aware traces
- Hidden source maps (no sourceMappingURL in bundles, .map deleted after
upload to Sentry)
- Privacy manifest at /privacy documenting all data collection
- robots.txt blocking all bots on both apps
- Suppress i18next promotional console log
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- @sentry/vite-plugin uploads source maps during Docker build
- Release tagged with git SHA (SENTRY_RELEASE) on both client and server
- Environment set to production/development for filtering
- CD passes SENTRY_AUTH_TOKEN + SENTRY_RELEASE as Docker build args
- docker-compose passes SENTRY_RELEASE to runtime containers
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>