update documentation

This commit is contained in:
2026-05-18 15:01:53 +02:00
parent 834025e560
commit 7018443b18
13 changed files with 756 additions and 725 deletions
+64 -46
View File
@@ -1,64 +1,82 @@
# TimeToLeave Architecture
This document outlines the architectural decisions, directory structure, and data flow of the TimeToLeave application.
TimeToLeave is an npm workspaces monorepo. The web app is both the browser UI and the backend proxy for external services; the mobile app calls that backend through the shared API client; shared domain logic lives in `packages/core`.
## 🏗️ Monorepo Structure
TimeToLeave uses an **npm Workspaces Monorepo** to manage its interconnected components. This allows for seamless code sharing and dependency management between the Web dashboard, the Mobile client, and the shared packages.
## Monorepo Structure
```text
TimeToLeave/
├── apps/
│ ├── web/ # Next.js 16 Web Dashboard & Backend Proxy
│ └── mobile/ # React Native 0.81 / Expo 54 Mobile Client
│ ├── web/ # Next.js 16 App Router UI and backend proxy
│ └── mobile/ # Expo 54 / React Native 0.81 app
├── packages/
│ ├── api-client/ # Unified API Client for Backend Proxies
│ └── core/ # Shared Domain Types, Logic, and Utilities
├── docs/ # Comprehensive Documentation
└── package.json # Root Workspace Configuration
│ ├── api-client/ # Shared client for /api/* backend routes
│ └── core/ # Shared types, defaults, parsing, scoring, formatting
├── docs/ # Documentation
└── package.json # Workspace scripts
```
## 🧩 Component Overview
## Components
### 1. Web Application (`apps/web`)
- **Role:** Primary dashboard for desktop users and the **Backend Proxy** for HAFAS/Geocoding APIs.
- **Framework:** Next.js 16 (App Router) with React 19.
- **Styling:** Tailwind CSS 4.
- **State Management:** React Context (`EventsProvider`, `ReminderSettingsProvider`).
- **Backend Proxy:** The Next.js API routes (`/api/*`) act as a server-side proxy. This is crucial because the HAFAS protocol and various geocoding APIs require server-side execution to protect API keys, handle CORS, and manage protocol-specific payloads.
### Web App: `apps/web`
### 2. Mobile Application (`apps/mobile`)
- **Role:** On-the-go companion app for real-time status checks and native device integration.
- **Framework:** React Native 0.81 via Expo 54.
- **Navigation:** React Navigation 7 (Native Stack Navigator).
- **Native APIs:**
- `expo-location`: Geolocation for finding nearby stations and calculating local transit (bike/walk) to the station.
- `expo-calendar`: Native integration to read local calendar events directly on the device.
- `expo-notifications`: Push notifications to alert the user when it's time to leave.
- `@react-native-async-storage/async-storage`: Persisting user settings (buffers, toggles) and cached events locally.
- Next.js 16 App Router with React 19 and Tailwind CSS 4.
- User-facing routes are `/` and `/calendar`.
- Route handlers under `apps/web/src/app/api/**/route.ts` proxy HAFAS, Nominatim, OSRM, Google Calendar, remote ICS, and Wiener Linien calls.
- `apps/web/src/proxy.ts` applies strict CORS and per-IP rate limiting to `/api/*`.
- Client state is kept in React context and persisted to `localStorage` through `useEventsStore` and `useReminderSettings`.
### 3. Core Package (`packages/core`)
- **Role:** The single source of truth for domain logic and TypeScript types across the entire monorepo.
- **Key Modules:**
- **Types:** Defines `Event`, `Journey`, `Station`, `BikeRoute`, `CountdownInfo`, and `WienerLinien` specific types.
- **HAFAS Time Parsing:** Specialized utilities to parse Vienna-centric (CET/CEST) timestamps. Handles Daylight Saving Time (DST) transitions accurately using `Intl.DateTimeFormat`.
- **Countdown & Status Logic:** Algorithms to translate raw journey data into human-readable statuses like *"Leave now"*, *"On time"*, or *"Delayed +12 min"*.
- **Formatting:** Standardized formatting for dates, times, distances, and durations.
### Mobile App: `apps/mobile`
### 4. API Client Package (`packages/api-client`)
- **Role:** A lightweight HTTP client that wraps the Web App's API routes.
- **Usage:** Used by both the Web frontend (for server/client data sync) and the Mobile client to communicate with the backend proxy.
- **Features:** Handles URL building, query parameters, and JSON serialization for HAFAS requests, calendar fetching, and routing requests.
- Expo 54 / React Native 0.81 with React Navigation 7.
- Screens cover event list, add/edit event, event detail, settings, and calendar import.
- Uses native APIs through `expo-calendar`, `expo-location`, `expo-notifications`, and `AsyncStorage`.
- Calls the web backend through `@timetoleave/api-client`; device builds should set `EXPO_PUBLIC_API_BASE_URL`.
## 🔄 Data Flow
### Core Package: `packages/core`
1. **Calendar Sync:** The user provides an `.ics` URL or uploads a file. The `ApiClient` sends this to the Web App's `/api/calendar` route, which parses the events and returns standardized `CalendarEvent` objects.
2. **Station Search:** The user searches for a station. The `ApiClient` triggers a HAFAS `LocMatch` request via `/api/hafas`.
3. **Journey Calculation:** Using the station `extId` and the event time, the `ApiClient` sends a `TripSearch` request to `/api/hafas`. The backend returns real-time journey data (`Journey[]`).
4. **Local Routing:** Using `expo-location` (mobile) or browser geolocation (web), the app calculates the bike/walk route from the user's home/location to the departure station via `/api/bike-route` or `/api/walk-route`.
5. **Real-Time Status:** The `packages/core` logic continuously compares the `Journey.rD` (real departure) against the current time and local travel duration to update the Leave Status dynamically.
- Owns the shared TypeScript model: events, calendar events, stations, journeys, routes, reminder settings, geocoding, and Wiener Linien types.
- Provides Vienna-aware HAFAS time conversion through `parseHafasTime()` and `hafasDateTime()`.
- Parses HAFAS journey responses, ranks journeys, formats dates/durations/distances, and computes countdown/status labels.
- Exports default origin constants for the Mödling fallback origin.
## ⚠️ Technical Constraints & Guidelines
### API Client Package: `packages/api-client`
- **Next.js Version:** The project uses **Next.js 16.2+**, which includes breaking changes compared to previous versions. Always refer to `node_modules/next/dist/docs/` when modifying Web routing or API conventions.
- **HAFAS Timezone:** HAFAS timestamps are strictly tied to `Europe/Vienna`. The `hafas-time.ts` module handles the bi-directional conversion between UTC `Date` objects and Vienna-local HAFAS strings. Never use standard `Date` methods for HAFAS times; always use `parseHafasTime()` and `hafasDateTime()`.
- Wraps the web backend routes from browser and mobile code.
- Supports base URL failover by accepting either one base URL or an array of base URLs.
- Builds HAFAS `TripSearch` and `LocMatch` requests, parses journey responses with `@timetoleave/core`, and includes an arrive-by fallback search window.
## Data Flow
1. Events enter through manual entry, remote ICS URL import, local ICS file parsing, Google Calendar on web, or native device calendars on mobile.
2. Events are normalized to `Event` or `CalendarEvent` objects and stored locally in the client.
3. The app resolves an origin station from saved settings, geolocation, or the shared default origin.
4. The event destination is geocoded with Nominatim.
5. HAFAS `LocMatch` resolves the nearest destination station.
6. HAFAS `TripSearch` fetches live journeys, with HAFAS time conversion handled by `packages/core`.
7. OSRM provides optional bike and final-walk routes.
8. Wiener Linien endpoints provide nearby stops and live departures around the destination.
9. Countdown and leave-by calculations combine event time, selected transport mode, journey arrival, final walk duration, bike route duration, and reminder buffers.
## External Integrations
| Service | Used for | Access path |
| --- | --- | --- |
| ÖBB HAFAS | Station lookup and journey search | `/api/hafas` |
| ÖBB GTFS ZIP | Optional train metadata enrichment | `apps/web/src/lib/oebb-gtfs.ts` |
| Nominatim | Destination geocoding | `/api/geocode` |
| OSRM | Bike and foot routes | `/api/bike-route`, `/api/walk-route` |
| Wiener Linien Darwin | Nearby stops and live monitor data | `/api/wienerlinien/*` |
| Google Calendar | Web OAuth calendar import | `/api/auth/google/*`, `/api/calendar/google` |
| Remote ICS providers | Calendar URL import | `/api/calendar` |
## Security and Operational Constraints
- Calendar URL imports validate provider domains, block private/reserved hosts, reject redirects, validate content type, and cap response size.
- HAFAS POST bodies are size-limited and only `TripSearch` and `LocMatch` are allowed.
- Coordinate APIs validate ranges and reject unrealistically distant route requests.
- API routes are rate-limited per client IP.
- CORS is allow-list based through `CORS_ALLOWED_ORIGINS`.
- Google OAuth tokens are stored in HTTP-only cookies.
- HAFAS time values must be treated as `Europe/Vienna` local strings.
- This project uses Next.js 16. Before changing framework behavior, routing, route handlers, or proxy/middleware code, read the relevant guide in `node_modules/next/dist/docs/`.