Commit graph

213 commits

Author SHA1 Message Date
Ullrich Schäfer
e105382c8f
Wire Grafana secrets through CD pipeline
Pass GRAFANA_USER, GRAFANA_PASSWORD, and GRAFANA_PASSWORD_HASH
from GitHub Actions secrets to the deploy script.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 08:39:25 +01:00
Ullrich Schäfer
d9b7a150ca
Merge pull request #88 from trails-cool/observability
Add observability: health, logging, metrics, Grafana
2026-03-27 06:59:05 +00:00
Ullrich Schäfer
49aadd04a9
Add observability: health endpoints, structured logging, metrics, Grafana stack
Health endpoints:
- /api/health (Journal) and /health (Planner) with DB connectivity check
- Docker healthchecks updated to use app health endpoints

Structured logging:
- Pino with JSON output in production, pretty-print in dev
- Request logging middleware in Planner (method, path, status, duration)
- Replaced console.log/error with structured logger in email and auth flows

Prometheus metrics:
- prom-client with default Node.js metrics + custom histograms/gauges
- /metrics endpoints on both apps
- http_request_duration, planner_active_sessions, brouter_request_duration

Monitoring stack:
- Prometheus, Loki, Grafana containers in docker-compose
- Grafana provisioned with datasources, dashboards, and alert rules
- Caddy access logging (JSON to stdout for Loki)
- grafana.trails.cool with basic auth via Caddy

Dashboards and alerting:
- Overview: request rate, error rate, latency p50/p95/p99
- Planner: active sessions, connected clients, BRouter latency
- Infrastructure: memory, CPU, event loop lag
- Alerts: disk >80%, app down 2min, error rate >5%

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 22:59:44 +01:00
Ullrich Schäfer
ce964cae96
Merge pull request #87 from trails-cool/planner-route-interactions
Update OpenSpec artifacts for route interactions
2026-03-26 21:38:52 +00:00
Ullrich Schäfer
ba044610b7
Add route interactions: click-to-split, drag-to-reshape, colored route rendering
- Enrich BRouter response with per-point 3D coordinates and segment boundary
  tracking (EnrichedRoute interface)
- ColoredRoute component: plain, elevation gradient (green→yellow→red), and
  surface color modes with invisible wide polyline for click targeting
- Click-to-split: click on route polyline inserts waypoint at nearest point,
  mapped to correct segment via boundary indices
- MidpointHandles: draggable CircleMarkers at route segment midpoints for
  reshaping, hidden below zoom 12, opaque on hover
- Color mode toggle (select) synced via Yjs routeData
- i18n keys for color mode labels (en + de)
- Unit tests for segment boundary tracking (13 tests)
- E2E tests for enriched route response and color mode toggle

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 22:36:42 +01:00
Ullrich Schäfer
5aef99b5ce
Merge pull request #86 from trails-cool/planner-route-interactions
Add route interactions: split, reshape, and colored rendering
2026-03-26 22:18:27 +01:00
Ullrich Schäfer
b080a15fb1
Add route interactions: click-to-split, drag-to-reshape, colored route rendering
- Enrich BRouter response with per-point 3D coordinates and segment boundary
  tracking (EnrichedRoute interface)
- ColoredRoute component: plain, elevation gradient (green→yellow→red), and
  surface color modes with invisible wide polyline for click targeting
- Click-to-split: click on route polyline inserts waypoint at nearest point,
  mapped to correct segment via boundary indices
- MidpointHandles: draggable CircleMarkers at route segment midpoints for
  reshaping, hidden below zoom 12, opaque on hover
- Color mode toggle (select) synced via Yjs routeData
- i18n keys for color mode labels (en + de)
- Unit tests for segment boundary tracking (13 tests)
- E2E tests for enriched route response and color mode toggle

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 21:15:55 +01:00
Ullrich Schäfer
14a39dc7cc
Merge pull request #84 from trails-cool/merge-queue
Replace automerge action with GitHub merge queue
2026-03-26 18:28:23 +00:00
Ullrich Schäfer
ed4757382a
Use forked gitleaks-action with merge_group support
The upstream gitleaks/gitleaks-action@v2 errors on merge_group events.
Use our fork which includes https://github.com/gitleaks/gitleaks-action/pull/186.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 19:26:18 +01:00
Ullrich Schäfer
cce2ddea5c
Fix: gh pr merge --auto requires --merge flag
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 19:09:08 +01:00
Ullrich Schäfer
aed99ba423
Merge pull request #85 from trails-cool/fix-merge-queue-docs
Fix CLAUDE.md merge queue docs to use --auto
2026-03-26 19:02:46 +01:00
Ullrich Schäfer
eccc274652
Fix CLAUDE.md: use gh pr merge --auto, not --merge-queue
The --merge-queue flag doesn't exist in gh CLI. The correct flag is
--auto, which routes through the merge queue when it's enabled.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 19:02:27 +01:00
Ullrich Schäfer
746b09276c
Replace automerge action with GitHub native merge queue
The automerge GitHub Action (pascalgn/automerge-action) is removed in
favor of GitHub's built-in merge queue, which is now enabled on the
main branch ruleset.

- Delete automerge.yml workflow
- Add merge_group trigger to CI so it runs on queue candidates
- Remove workflow_run trigger for Automerge from CD
- Update CLAUDE.md to document gh pr merge --merge-queue

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 18:40:11 +01:00
github-actions[bot]
fe3621aab3
Merge pull request #83 from trails-cool/emails 2026-03-26 17:27:26 +00:00
Ullrich Schäfer
346d72a759
Fix SMTP_URL and SMTP_FROM not passed to containers during deploy
The CD workflow exported SENTRY_RELEASE but not the SMTP env vars,
so docker-compose substituted them as empty strings and emails
silently failed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 18:24:17 +01:00
github-actions[bot]
ef333a2105
Merge pull request #82 from trails-cool/transactional-emails-planner-features 2026-03-26 00:03:23 +00:00
Ullrich Schäfer
0a8dd0b766
Add transactional emails (SMTP) and planner features (no-go areas, notes, crash recovery)
Transactional emails:
- Add nodemailer SMTP email module with dev-mode console logging
- Magic link template and welcome template with HTML + plain text
- Wire sendMagicLink into login flow, sendWelcome into registration
- Update privacy page and deploy docs for SMTP configuration

Planner features:
- No-go areas: draw polygons on map (leaflet-geoman), synced via Yjs,
  passed to BRouter as nogos parameter, route recomputes on change
- Session notes: collaborative Y.Text textarea in sidebar tab
- Crash recovery: periodic localStorage save of Yjs state, restore on reconnect
- Rate limit session creation (10/IP/hour) in /new route

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 01:00:42 +01:00
github-actions[bot]
05b5f6febf
Merge pull request #81 from trails-cool/archive-completed-changes 2026-03-25 23:45:24 +00:00
Ullrich Schäfer
538a69e1c5
Archive app-navigation and planner-multiplayer-awareness, sync specs
Both changes fully implemented. Sync delta specs to main:
- map-display: add Planner home page nav and cursor rendering requirements
- planner-session: add session participant awareness requirement

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 00:42:55 +01:00
github-actions[bot]
589cb4ab15
Merge pull request #80 from trails-cool/security-hardening-verification 2026-03-25 23:34:14 +00:00
Ullrich Schäfer
b6908fb288
Fix picomatch audit vulnerability and complete security hardening verification
Override picomatch to 4.0.4 to resolve high-severity ReDoS (GHSA-c2c7-rcm5-vvqj),
verify all security hardening tasks (gitleaks, pnpm audit, Docker non-root,
Caddy headers, scanner blocking), sync specs, and archive the change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 00:31:27 +01:00
github-actions[bot]
433886772f
Merge pull request #79 from trails-cool/fix-i18n 2026-03-25 23:18:22 +00:00
Ullrich Schäfer
3f7499ff06 Fix i18n: move init to root.tsx and downgrade to stable versions
Move initI18n() from entry.client.tsx to root.tsx in both Planner and
Journal so i18n is initialized before any component renders. Downgrade
react-i18next to 16.6.1 and i18next to 25.10.4 to avoid hydration
mismatch issues introduced in newer versions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 23:18:15 +00:00
Ullrich Schäfer
30ae68dd69
Merge pull request #78 from trails-cool/fix-i18n-sync-init
Fix i18n: synchronous init before hydration
2026-03-25 22:58:58 +01:00
Ullrich Schäfer
18ac8a1ce3
Fix i18n: use initImmediate false for sync initialization
i18n.init() is async by default. With initImmediate: false, it
initializes synchronously so translations are ready before React
hydrates. Without this, useTranslation() runs before i18n is ready,
showing raw translation keys.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 22:58:51 +01:00
Ullrich Schäfer
59ef2a4b5e
Merge pull request #77 from trails-cool/fix-i18n-entry-client
Fix i18n: init before hydration in entry.client
2026-03-25 22:52:26 +01:00
Ullrich Schäfer
d9ef69c0a0
Fix i18n: init in entry.client.tsx before hydration
With custom entry.client.tsx, initI18n() in root.tsx runs too late —
the client hydrates before i18n is initialized, showing raw translation
keys. Move initI18n() to entry.client.tsx where it runs before
startTransition/hydrateRoot.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 22:52:20 +01:00
Ullrich Schäfer
ebe4ccd838
Merge pull request #76 from trails-cool/fix-node25-corepack
Fix Docker builds: corepack removed in Node 25
2026-03-25 22:43:00 +01:00
Ullrich Schäfer
871ed81869
Fix Docker builds: corepack removed in Node 25
Node 25 no longer ships corepack. Replace corepack enable/prepare with
npm install -g pnpm.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 22:42:54 +01:00
Ullrich Schäfer
462da3ac5d
Merge pull request #75 from trails-cool/fix-csp-blob-worker
Fix CSP: allow blob: for Yjs web worker
2026-03-25 22:40:11 +01:00
Ullrich Schäfer
5d542a9391
Fix CSP: allow blob: for Yjs web worker
y-websocket creates a Web Worker from a blob URL. The CSP script-src
needs blob: and an explicit worker-src directive to allow this.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 22:40:05 +01:00
Ullrich Schäfer
a5248872c7
Merge pull request #64 from trails-cool/dependabot/docker/apps/planner/node-25-slim
Bump node from 24-slim to 25-slim in /apps/planner
2026-03-25 13:21:32 +01:00
Ullrich Schäfer
17328833f4
Merge pull request #65 from trails-cool/dependabot/docker/apps/journal/node-25-slim
Bump node from 24-slim to 25-slim in /apps/journal
2026-03-25 13:21:17 +01:00
Ullrich Schäfer
fb13f7b8ef
Merge pull request #66 from trails-cool/dependabot/github_actions/actions/cache-5
Bump actions/cache from 4 to 5
2026-03-25 13:21:09 +01:00
Ullrich Schäfer
35c0c20310
Merge pull request #67 from trails-cool/dependabot/github_actions/pnpm/action-setup-5
Bump pnpm/action-setup from 4 to 5
2026-03-25 13:21:03 +01:00
Ullrich Schäfer
7239c1e1a5
Merge pull request #69 from trails-cool/dependabot/npm_and_yarn/production-9e71a89b93
Bump the production group with 8 updates
2026-03-25 13:20:54 +01:00
Ullrich Schäfer
aab7bda54a
Merge pull request #74 from trails-cool/increase-brouter-memory
Increase BRouter heap to 1GB
2026-03-25 13:17:13 +01:00
Ullrich Schäfer
c155bfcef5
Increase BRouter heap to 1GB
512MB heap was too tight for 720MB of Germany segments, causing the
thread-priority-watchdog to kill routing requests after 1 second when
segment loading triggered GC pressure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 13:17:05 +01:00
dependabot[bot]
dfba9b8170 Bump the production group with 8 updates
Bumps the production group with 8 updates:

| Package | From | To |
| --- | --- | --- |
| [@react-router/dev](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dev) | `7.13.1` | `7.13.2` |
| [@react-router/node](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-node) | `7.13.1` | `7.13.2` |
| [@react-router/serve](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-serve) | `7.13.1` | `7.13.2` |
| [i18next](https://github.com/i18next/i18next) | `25.10.4` | `25.10.9` |
| [react-i18next](https://github.com/i18next/react-i18next) | `16.6.1` | `16.6.6` |
| [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router) | `7.13.1` | `7.13.2` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.57.1` | `8.57.2` |
| [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `4.1.0` | `4.1.1` |


Updates `@react-router/dev` from 7.13.1 to 7.13.2
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dev/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/@react-router/dev@7.13.2/packages/react-router-dev)

Updates `@react-router/node` from 7.13.1 to 7.13.2
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-node/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/@react-router/node@7.13.2/packages/react-router-node)

Updates `@react-router/serve` from 7.13.1 to 7.13.2
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-serve/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/@react-router/serve@7.13.2/packages/react-router-serve)

Updates `i18next` from 25.10.4 to 25.10.9
- [Release notes](https://github.com/i18next/i18next/releases)
- [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/i18next/compare/v25.10.4...v25.10.9)

Updates `react-i18next` from 16.6.1 to 16.6.6
- [Changelog](https://github.com/i18next/react-i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/react-i18next/compare/v16.6.1...v16.6.6)

Updates `react-router` from 7.13.1 to 7.13.2
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router@7.13.2/packages/react-router)

Updates `typescript-eslint` from 8.57.1 to 8.57.2
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.57.2/packages/typescript-eslint)

Updates `vitest` from 4.1.0 to 4.1.1
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.1/packages/vitest)

---
updated-dependencies:
- dependency-name: "@react-router/dev"
  dependency-version: 7.13.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
- dependency-name: "@react-router/node"
  dependency-version: 7.13.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
- dependency-name: "@react-router/serve"
  dependency-version: 7.13.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
- dependency-name: i18next
  dependency-version: 25.10.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: production
- dependency-name: react-i18next
  dependency-version: 16.6.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: production
- dependency-name: react-router
  dependency-version: 7.13.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production
- dependency-name: typescript-eslint
  dependency-version: 8.57.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: production
- dependency-name: vitest
  dependency-version: 4.1.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-25 12:32:37 +01:00
dependabot[bot]
aa607609ec Bump pnpm/action-setup from 4 to 5
Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 4 to 5.
- [Release notes](https://github.com/pnpm/action-setup/releases)
- [Commits](https://github.com/pnpm/action-setup/compare/v4...v5)

---
updated-dependencies:
- dependency-name: pnpm/action-setup
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-25 12:32:36 +01:00
dependabot[bot]
355b5f7067 Bump actions/cache from 4 to 5
Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-25 12:32:35 +01:00
dependabot[bot]
2df9172161 Bump node from 24-slim to 25-slim in /apps/journal
Bumps node from 24-slim to 25-slim.

---
updated-dependencies:
- dependency-name: node
  dependency-version: 25-slim
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-25 12:32:34 +01:00
dependabot[bot]
c5508fc293 Bump node from 24-slim to 25-slim in /apps/planner
Bumps node from 24-slim to 25-slim.

---
updated-dependencies:
- dependency-name: node
  dependency-version: 25-slim
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-25 12:32:33 +01:00
Ullrich Schäfer
5f5f583722
Merge pull request #73 from trails-cool/fix-gitleaks-config
Fix .gitleaks.toml config syntax
2026-03-25 11:59:32 +01:00
Ullrich Schäfer
38e5b1ce25
Fix .gitleaks.toml config syntax
regexTarget is a string field, not an array of objects. Use the correct
allowlist format with regexes array.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 11:59:26 +01:00
Ullrich Schäfer
f009ae80b6
Merge pull request #72 from trails-cool/fix-gitleaks-permissions
Fix gitleaks permissions for dependabot PRs
2026-03-25 10:52:18 +01:00
Ullrich Schäfer
52382427ef
Fix gitleaks permissions for dependabot PRs
Gitleaks needs explicit contents:read and pull-requests:read permissions
to scan dependabot PR commits. Without them, the GitHub API returns 403.

Ref: https://github.com/gitleaks/gitleaks/issues/1343#issuecomment-2690243151

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 10:52:03 +01:00
Ullrich Schäfer
5201bbfca2
Merge pull request #68 from trails-cool/add-fastmail-dns
Add Fastmail DNS records
2026-03-25 10:15:49 +01:00
Ullrich Schäfer
0e98efe1ab Add Fastmail DNS records to Terraform
MX, SPF, DMARC, DKIM (4 keys), mail A record, and SRV records for
autodiscover, IMAP, JMAP, CalDAV, CardDAV, SMTP submission.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 09:11:42 +00:00
Ullrich Schäfer
dbc3491c1a
Merge pull request #71 from trails-cool/fix-gitleaks-license
Add GITLEAKS_LICENSE to gitleaks action
2026-03-25 10:10:10 +01:00