Commit graph

1075 commits

Author SHA1 Message Date
github-actions[bot]
1a655d328f
Merge pull request #49 from trails-cool/add-sentry-planner 2026-03-25 01:38:14 +00:00
Ullrich Schäfer
ef07915f38
Add Sentry error tracking to both apps
Planner (client + custom server) and Journal (client + entry.server):
- Client: @sentry/react with browser tracing + replay on error
- Server: @sentry/node for unhandled exceptions
- ErrorBoundary captures React errors via Sentry.captureException
- Disabled in dev, 10% trace sample rate in production

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 02:35:16 +01:00
github-actions[bot]
1819a17036
Merge pull request #48 from trails-cool/fix-yjs-sync-protocol 2026-03-25 01:26:55 +00:00
Ullrich Schäfer
cc041d3235
Fix Yjs WebSocket server to use sync protocol
The production server was sending raw Yjs updates but the client
(y-websocket's WebsocketProvider) expects the y-protocols sync protocol
with message type headers. This caused "Unexpected end of array" errors
on every connection.

Rewrite to match the Vite dev plugin: use y-protocols/sync for document
sync, y-protocols/awareness for presence, and lib0 for encoding.

Verified locally: server starts, WebSocket connects, sync messages
exchange correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 02:24:13 +01:00
github-actions[bot]
f078589463
Merge pull request #47 from trails-cool/fix-cors-callback 2026-03-25 01:16:39 +00:00
Ullrich Schäfer
a5d682d671 Fix CORS on Planner→Journal callback endpoint
The Save to Journal button makes a cross-origin POST from
planner.trails.cool to trails.cool. The browser's preflight OPTIONS
request was returning HTML (no CORS headers), blocking the save flow.

Add CORS headers to all responses and handle OPTIONS preflight in a
loader. Origin is restricted to PLANNER_URL.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 01:16:32 +00:00
github-actions[bot]
77d785ccde
Merge pull request #46 from trails-cool/mark-phase11-complete 2026-03-25 01:14:26 +00:00
Ullrich Schäfer
85b8454de7 Mark phase 1 MVP tasks 11.6-11.8 complete
- 11.6: i18n wired up in both apps (PR #44)
- 11.7: Responsive layout breakpoints added (PR #45)
- 11.8: Deployed to Hetzner, both apps live at trails.cool

All 11 task groups in phase 1 MVP are now complete.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 01:14:19 +00:00
github-actions[bot]
c48b811ea4
Merge pull request #45 from trails-cool/responsive-layout 2026-03-25 01:13:49 +00:00
Ullrich Schäfer
69f420e879
Add basic responsive layout for tablet and mobile
Planner: hide title on small screens, wrap header items, hide waypoint
sidebar below md breakpoint (map takes full width).
Journal: stack route stats and action buttons on mobile, responsive
header layout on route detail.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 02:11:22 +01:00
github-actions[bot]
bdb72ae141
Merge pull request #42 from trails-cool/ci-e2e-production-mode 2026-03-25 01:06:59 +00:00
Ullrich Schäfer
537d515df9 Run E2E tests against production build in CI
In CI, Playwright now starts apps with production commands (react-router-
serve, node --experimental-strip-types) instead of dev servers. This
catches runtime issues like missing exports and import resolution errors
before deploy. Locally, dev servers are still used for fast iteration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 01:04:38 +00:00
github-actions[bot]
ae8fc4560a
Merge pull request #43 from trails-cool/fix-planner-static-assets 2026-03-25 01:04:05 +00:00
Ullrich Schäfer
394daca38e
Serve static assets in planner's custom server
The custom server.ts only had the React Router request handler — static
assets in build/client/ were never served, causing 404s for all JS/CSS
in production. Add a static file handler with path traversal protection
and immutable caching for hashed assets.

Verified locally: HTML 200, assets 200, Cache-Control: immutable on
/assets/*.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 02:01:27 +01:00
github-actions[bot]
6afba179f3
Merge pull request #41 from trails-cool/fix-automerge-base-branch 2026-03-25 00:55:47 +00:00
Ullrich Schäfer
3a4119e00e Restrict automerge to PRs targeting main
Prevents stacked PRs from being auto-merged into feature branches.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:53:33 +00:00
github-actions[bot]
0bab72dcd7
Merge pull request #39 from trails-cool/fix-planner-production-server 2026-03-25 00:53:00 +00:00
Ullrich Schäfer
62b5fb998e
Fix planner production server + use .ts extensions everywhere
- Fix createRequestHandler → createRequestListener (@react-router/node
  removed the old API)
- Move noEmit + allowImportingTsExtensions to tsconfig.base.json so all
  packages and apps inherit them consistently
- Add explicit .ts extensions to all remaining relative imports across
  packages and apps

Verified locally: node --experimental-strip-types server.ts starts and
responds 200.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 01:41:33 +01:00
github-actions[bot]
655dc46ec8
Merge pull request #38 from trails-cool/fix-ts-extensions 2026-03-25 00:37:02 +00:00
Ullrich Schäfer
4d73de4e0d
Use explicit .ts extensions for Node-compatible imports
Node's --experimental-strip-types requires explicit .ts extensions on
relative imports. Add allowImportingTsExtensions to both app tsconfigs
and update server-side imports in planner and journal for consistency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 01:34:16 +01:00
Ullrich Schäfer
1abcc5c804
Merge pull request #37 from trails-cool/fix-production-deploy
Fix production deploy: TS support + DB schema
2026-03-25 01:27:01 +01:00
Ullrich Schäfer
afeefbf19d
Fix production deploy: TypeScript support + DB schema push
Planner crashed because Node.js couldn't handle .ts imports — add
--experimental-strip-types to the CMD. Journal crashed because the
database had no schemas — add drizzle-kit push to the deploy script.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 01:26:16 +01:00
Ullrich Schäfer
0fdcbf77d6
Merge pull request #36 from trails-cool/fix-cd-trigger
Fix CD: trigger after automerge + manual dispatch
2026-03-25 01:18:56 +01:00
Ullrich Schäfer
46c880973d
Fix CD: trigger on automerge + manual dispatch
GITHUB_TOKEN merges (from automerge action) don't trigger push
events on other workflows. Added:
- workflow_run trigger after Automerge completes
- workflow_dispatch for manual gh workflow run cd.yml

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 01:18:12 +01:00
github-actions[bot]
14656b6527
Merge pull request #35 from trails-cool/fix-docker-builds 2026-03-25 00:12:43 +00:00
Ullrich Schäfer
0a09df27f4
Fix Docker builds: missing deps + bump actions
Dockerfiles:
- Add db package.json to deps stage
- Copy full node_modules (including app-level) to runtime
- Copy packages/ dir for workspace imports at runtime

CD workflow:
- docker/login-action v3 → v4
- docker/build-push-action v6 → v7

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 01:10:01 +01:00
Ullrich Schäfer
21eb943393
Merge pull request #34 from trails-cool/deploy-to-hetzner
Enable CD deployment to Hetzner
2026-03-25 01:02:45 +01:00
Ullrich Schäfer
8e2f183288
Enable CD deployment to Hetzner (task 11.8)
CD workflow:
- Uncommented deploy step
- Copy docker-compose.yml + Caddyfile to server via SCP
- Login to ghcr.io with DEPLOY_GHCR_TOKEN (classic PAT, read:packages)
- Download Germany BRouter segments on first deploy (~750MB)
- Pull images, restart services, verify

Infrastructure:
- BRouter segments as bind mount (./segments) not Docker volume
- Added ORIGIN, PLANNER_URL, SESSION_SECRET, NODE_ENV to Journal
- Added NODE_ENV to Planner
- Disabled Garage for now (no media storage yet)

Required secrets: DEPLOY_HOST, DEPLOY_SSH_KEY, DEPLOY_GHCR_TOKEN

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 01:00:00 +01:00
github-actions[bot]
970f591a60
Merge pull request #33 from trails-cool/fix-playwright-summary 2026-03-24 23:48:55 +00:00
Ullrich Schäfer
6d8e570afc
Fix Playwright job summary: handle nested suite structure
Playwright JSON has file → describe → specs nesting.
Use stats.expected/unexpected for counts, iterate nested suites.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:46:21 +01:00
github-actions[bot]
a09f41e03d
Merge pull request #32 from trails-cool/playwright-job-summary 2026-03-24 23:41:02 +00:00
Ullrich Schäfer
074bcff4e5
Add Playwright job summary to CI
Generates a markdown summary table in GitHub Actions showing
passed/failed tests with timing per test. Uses the JSON reporter
to output results, then a Node script to format as markdown
and append to $GITHUB_STEP_SUMMARY.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:38:38 +01:00
Ullrich Schäfer
19ce2d1f1e
Merge pull request #31 from trails-cool/fix-automerge-workflow-run
Fix automerge: trigger after CI completes
2026-03-25 00:35:24 +01:00
Ullrich Schäfer
052ff6ac20
Fix automerge: add workflow_run trigger after CI completes
check_suite and status events don't reliably trigger for workflows
on the same commit (GitHub prevents infinite loops). Replaced with
workflow_run on CI completion, which fires after all CI jobs finish.

Added comments explaining why each trigger group exists.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:32:22 +01:00
Ullrich Schäfer
e4d4c790b3
Merge pull request #30 from trails-cool/ci-e2e-services
Add PostgreSQL + BRouter to CI E2E tests
2026-03-25 00:29:26 +01:00
Ullrich Schäfer
70d9d71a5c
Fix CI: wait for PostGIS extension before schema push
pg_isready reports true before PostGIS extension is initialized.
Added a second wait loop that queries PostGIS_Version() to ensure
the extension is fully loaded before running drizzle-kit push.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:21:25 +01:00
Ullrich Schäfer
1d45c57ae7
Cache PostGIS Docker image in CI
Replace service container with manual docker run + cached image tar.
Service containers pull the image before steps run (uncacheable).
Manual approach: cache the image as a tar, load on cache hit.

PostGIS image is ~400MB — first run pulls and saves, subsequent
runs load from cache in ~2s instead of ~19s.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:17:30 +01:00
Ullrich Schäfer
f2f5c6a83e
Cache Playwright browsers in CI
Playwright browser download (25s) is now cached by pnpm-lock hash.
On cache hit, only install system deps (fast). On miss, full install.

Current E2E timings (first run):
- Postgres container: 19s
- BRouter JAR: cached
- Berlin segment: 6s (will be cached next run)
- Playwright install: 25s → cached after first run
- BRouter startup: 9s
- Tests: 27s

Expected after caching: ~60s total (down from ~107s).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:15:26 +01:00
Ullrich Schäfer
f0afa8a4a7
Fix withDb: detect DataWithResponseInit from data() throws
React Router's data() throw creates a DataWithResponseInit object
(type + data + init), not a Response or ErrorResponseImpl.
Check for type === "DataWithResponseInit" to re-throw correctly.

Verified: nonexistent session → 404, DB down → 503.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:11:48 +01:00
Ullrich Schäfer
dee6f2806f
Add root ErrorBoundary to both apps, simplify withDb
Both apps now have proper ErrorBoundary exports in root.tsx:
- 404: "Page not found"
- 503: "Service temporarily unavailable"
- Other: shows error message

Simplified withDb re-throw check: anything with a "status" property
is treated as a React Router response (covers Response and
ErrorResponseImpl). DB errors throw a plain Response(503) that
the error boundary renders nicely.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:06:10 +01:00
Ullrich Schäfer
83009c2b9b
Fix withDb: re-throw React Router ErrorResponseImpl
React Router's data() throws ErrorResponseImpl, not Response.
Check for objects with status + data properties to catch both
Response and ErrorResponseImpl, preventing withDb from
swallowing 404s as 503s.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:03:15 +01:00
Ullrich Schäfer
b71117e4ca
Add PostgreSQL + BRouter to CI E2E tests
CI now runs the full stack for E2E tests:
- PostgreSQL as a service container + schema push via drizzle
- BRouter JAR + Berlin segment (E10_N50) with caching
- Both cached across runs (BRouter JAR + segment)

Removed all skip/workaround logic from tests — all 20 E2E tests
now run in CI with real services.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:58:01 +01:00
Ullrich Schäfer
cd6f6a6f18
Merge pull request #29 from trails-cool/planner-journal-handoff
Groups 10-11: Activity feed and E2E tests
2026-03-24 23:51:13 +01:00
Ullrich Schäfer
7d273b73c0
Fix CI E2E: check Planner DB, add Playwright HTML report
- Integration test DB check now hits Planner API (not Journal)
  so it correctly skips when no PostgreSQL in CI
- Playwright uploads HTML report artifact on all runs (not just failures)
- Install Chromium with --with-deps for CI compatibility
- Report retained for 30 days

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:47:43 +01:00
Ullrich Schäfer
2d0a4ac2c7
Fix withDb: catch all non-Response errors as DB failures
The previous version checked for specific error messages that
didn't match Drizzle's actual error text ("Failed query").
Now any error from a withDb-wrapped handler returns 503.
React Router Response objects are still re-thrown.

Verified: DB down → 503 JSON, DB up → 201, home page always 200.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:43:10 +01:00
Ullrich Schäfer
8fb7771223
Add withDb() helper for graceful DB error handling
Shared withDb() wrapper in @trails-cool/db catches database
connection errors and throws a 503 Response instead of crashing
the server. Re-throws React Router responses (redirects, data()).

Applied to Planner session routes — replaces manual try/catch.
Any route can use withDb(() => ...) for automatic 503 on DB failure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:39:33 +01:00
Ullrich Schäfer
cadcf753a7
Gracefully handle missing DB in Planner routes
Wrap session API and loader in try/catch to return 503 instead
of crashing the server when PostgreSQL is unavailable. This
prevents E2E test failures in CI where no DB is running.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:37:05 +01:00
Ullrich Schäfer
e86ff5a8a3
Fix E2E tests in CI: skip DB-dependent tests when no PostgreSQL
Planner session tests and integration tests now check for DB
availability before running. Skip gracefully in CI where no
PostgreSQL or BRouter is available.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:33:03 +01:00
Ullrich Schäfer
79e6ae6ea2
Add E2E and integration tests (Group 11)
Journal tests (6):
- Home page, auth pages render correctly
- No password fields on register/login
- Protected routes redirect to login

Planner tests (9):
- Session creation via API
- Map loads in session
- Yjs WebSocket connects (Connected status)
- Profile selector, export button, waypoint sidebar
- Session with initial GPX waypoints
- Expired session returns 404

Integration tests (5):
- GPX import returns parsed waypoints
- BRouter computes Berlin routes
- Routes pass through all waypoints (segment by segment)
- Rate limit headers present
- Rejects < 2 waypoints

Total: 32 unit tests + 20 E2E tests, all passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:20:04 +01:00
Ullrich Schäfer
a9c99844c6
Implement Journal activity feed (Group 10)
Activity CRUD:
- Activity list page (/activities) with date, distance, elevation
- Create activity with GPX upload, description, optional route link
- Activity detail with stats and date
- "Link to Route" action (select from existing routes)
- "Create Route from Activity" action (creates route from GPX)

Server logic in activities.server.ts with async GPX parsing.
Uses ClientDate component for SSR-safe date rendering.
All 6 Group 10 tasks complete.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:20:04 +01:00