update documentation
This commit is contained in:
+64
-46
@@ -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/`.
|
||||
|
||||
Reference in New Issue
Block a user