System services
High-level services and how they communicate.
System architecture overview
Key directories
src/
├── app/api/ # API layer
│ ├── admin/ # Administrative endpoints
│ ├── strava/ # Strava integration endpoints
│ ├── activities/ # Activity data management
│ └── analytics/ # Analytics and metrics
├── components/ # React UI components
├── lib/ # Business logic and utilities
├── server/ # Background workers
└── types/ # TypeScript definitions
prisma/ # Database layer
├── migrations/ # Schema evolution history
└── schema.prisma # Current database schema
memory_bank/ # Documentation and decisions
scripts/ # Automation and tooling
.husky/ # Git hooks for quality assurance
.vscode/ # Development environment configuration
Service communication flow
Strava API ←→ Webhook Endpoints ←→ Job Queue ←→ Background Workers ←→ Database
↓ ↓
Web Interface ←→ API Routes ←→ Business Logic
Core services (stage 1)
1) API gateway (Next.js API routes)
Responsibility: OAuth handshakes, public API, admin endpoints.
Core Endpoints:
GET /api/activities— List activities with pagination and caching.GET /api/activities/[id]— Get detailed activity data (excluding large fields).GET /api/activities/[id]/thumbnail— Serve cached thumbnails with ETag support.GET /api/activities/[id]/map— Get activity polylines for maps.GET /api/activities/[id]/splits— Enhanced split data with real distance/duration.GET /api/activities/[id]/streams— Get activity streams (GPS, HR, pace data).GET /api/runs— Legacy endpoint for run data (compatibility).
Strava Integration:
GET /api/strava/auth— Initiate OAuth flow with Strava.GET /api/strava/activities— Fetch activities from Strava API.POST /api/strava/callback— OAuth callback handler with error management.GET /api/strava/status— Check connection status and athlete info.DELETE /api/strava/disconnect— Remove tokens, cleanup webhook subscriptions.POST /api/strava/logout— Clear authentication and redirect.POST /api/strava/sync— Manual sync trigger with token refresh.GET /api/strava/splits/[id]— Get activity splits from Strava.POST /api/strava/webhook— Real-time webhook endpoint for instant activity notifications.GET /api/strava/export— CSV export functionality.
Analytics & Performance:
GET /api/analytics/peak— Peak performance analytics.GET /api/analytics/best-efforts-cached— Pre-calculated best efforts with GPS analysis.
Notes:
- Return friendly errors (with "what to try next").
- Always include request id in responses/logs.
2) Auth and tokens
Responsibility: Strava OAuth, token refresh, revocation handling.
Behaviour:
- Refresh proactively with safety window (5 mins before expiry).
- On failure, mark token "degraded" and show reconnect UI.
- Audit every refresh event.
3) Ingest and sync
Responsibility: Reliable import of new/updated activities.
Flow:
- Strava → Webhook (activity created/updated).
- Job Queue enqueues fetch task.
- Fetcher calls Strava API, writes to DB.
- Dedupe by (stravaId, start_date ± small window, duration).
- Audit all writes (created/updated/skipped).
SLAs:
- New activity visible in < 30s.
- Idempotent replays.
4) Background workers (working)
- Thumbnail Worker: OpenStreetMap compositing + route overlays.
- Activity Fetch Worker: Real-time webhook processing (<30s sync).
- Strava Worker: Token refresh + polling fallback.
- Job Queue System: Retry logic + exponential backoff.
Thumbnail Worker Notes:
- Handle "Image to composite must have same dimensions or smaller" by resizing overlay to base.
- Retry + cleanup on failure; fall back to route-only.
- Store pointers to images, not blobs, in the core API responses.
5) Analytics (phase 2 - planned)
Responsibility: Precompute best efforts and rankings.
Jobs:
- Distances: 400m, 800m, 1km, 1mi, 5k, 10k, 21.1k, 42.2k, 50k.
- Windows: all-time, 2y, 1y, 3m.
- Outputs: rank tables per window.
Principles:
- Deterministic calculations (no hidden magic).
- "Explain this number" link from UI.
6) UI (web)
Responsibility: Fast, readable surfaces.
Pages:
- Feed with filters + persistence.
- Details page (AU 24h, splits, HR opt-in).
- Stats (Distance / Duration / Calories / Speed / Altitude × Year/Month/Week/Day).
- CSV export.
Performance:
- Exclude heavy fields.
Admin APIs
7 Admin endpoints (/api/admin/*)
Webhook Management:
GET /api/admin/webhooks— List existing webhook subscriptions (with cache support & ?refresh=true).POST /api/admin/webhooks— Create new webhook subscription.DELETE /api/admin/webhooks— Delete webhook subscription(s).
Smart caching architecture: The webhook management system uses intelligent database caching for optimal performance:
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ UI Request │───▶│ Cache Check │───▶│ Strava API │
│ │ │ (5min TTL) │ │ (if expired) │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │
▼ ▼
┌──────────────────┐ ┌─────────────────┐
│ Cached Response │◀───│ Update Cache │
│ ~10-50ms │ │ Store in DB │
└──────────────────┘ └─────────────────┘
Performance Benefits:
- Instant UI - Cache hits respond in 10-50ms vs 500-1000ms API calls.
- 95% fewer API calls - Only refresh on expiry or manual force refresh.
- Real-time updates - Cache immediately updated after create/delete operations.
- Athlete tracking - Maintains audit trail while respecting app-wide webhook nature.
Activity Job Queue:
GET /api/admin/activity-jobs— Get job queue statistics and recent jobs.POST /api/admin/activity-jobs— Create manual activity fetch jobs.DELETE /api/admin/activity-jobs— Clear completed/failed jobs.
Performance Analytics:
POST /api/admin/calculate-best-efforts— Calculate best efforts for all existing activities.POST /api/admin/clear-best-efforts— Clear all best efforts for recalculation.
Data Synchronization:
GET /api/admin/sync-streams— Get status of streams sync.POST /api/admin/sync-streams— Download GPS, HR, cadence, power, altitude data from Strava.POST /api/admin/resync— Force resync to populate new fields (updates fastestPaceSecPerKm).
Thumbnail Management:
GET /api/admin/thumbnails— Get thumbnail generation queue status.POST /api/admin/thumbnails— Queue thumbnail generation (single activity or bulk).- API p95 < 50ms.
Admin APIs
7 endpoints built.
/api/admin/webhooks→ webhook subscription management (GUI + API)./api/admin/activity-jobs→ monitor fetch queue with stats./api/admin/thumbnails→ bulk thumbnail regeneration./api/admin/calculate-best-efforts→ GPS-based performance analysis./api/admin/clear-best-efforts→ reset calculations./api/admin/sync-streams→ download GPS/HR data from Strava./api/admin/resync→ force refresh of derived fields.
Cross-cutting
Observability
- Structured logs with
req_id. - One dashboard: ingest → worker → DB health.
- Error taxonomy: user-facing vs internal.
Data
- Postgres primary.
- Prisma schema (activities, splits, tokens, audits, jobs).
- Backups: nightly dump + verify restore.
Security
- Secrets via K8s (mounted, not baked).
- RBAC for admin endpoints.
- Data minimisation & retention.
Deployment (phase 6 - planned)
- GitLab main; GitHub mirror.
- FluxCD reconciles clusters from Git.
- Dev in K8s mirrors prod shape.
Planned extensions (future stages)
Stage 2: Apple health worker
- Ingest metrics from HealthKit once iOS app is live.
Stage 4: Nutrition worker
- Store and summarise daily calories and sugar.
Stage 5: Music worker
- Log Apple Music/Spotify track metadata during runs.