Files
2026-05-18 15:01:53 +02:00

114 lines
5.2 KiB
Markdown

# Development Guide
## Prerequisites
- Node.js 20 or newer
- npm 9 or newer
- Android Studio or Xcode for native mobile builds
- Expo/EAS tooling when building or submitting mobile apps
## Install
```bash
npm install
cp .env.example .env
```
The root workspace owns dependency installation. Avoid installing separately inside workspaces unless you are intentionally changing that workspace's dependency list.
## Environment
| Variable | Default | Purpose |
| --- | --- | --- |
| `PORT` | `3000` through Next dev defaults | Web server port when the runtime honors it. |
| `HAFAS_URL` | `https://fahrplan.oebb.at/bin/mgate.exe` | ÖBB HAFAS endpoint. |
| `HAFAS_TIMEOUT_MS` | `10000` | HAFAS request timeout. |
| `HAFAS_VER`, `HAFAS_LANG`, `HAFAS_AID`, `HAFAS_CLIENT_*` | ÖBB app-compatible defaults | HAFAS request envelope metadata. |
| `NOMINATIM_URL` | `https://nominatim.openstreetmap.org` | Geocoding endpoint. |
| `NOMINATIM_USER_AGENT` | `TimeToLeave/2.0` | Required Nominatim user agent. |
| `OSRM_URL` | `https://router.project-osrm.org` | Bike and foot routing base URL. |
| `WIENER_LINIEN_API_URL` | `https://api.wienerlinien.at/darwin-v2` | Wiener Linien live data base URL. |
| `OEBB_GTFS_URL` | ÖBB 2026 GTFS ZIP | Optional HAFAS response enrichment source. |
| `CORS_ALLOWED_ORIGINS` | `http://localhost:3000` fallback | Allowed browser origins for `/api/*`. |
| `API_RATE_LIMIT_MAX_REQUESTS` | `120` | Per-IP API requests per window. |
| `API_RATE_LIMIT_WINDOW_MS` | `60000` | Rate-limit window length. |
| `DEPLOYMENT_URL` | local fallback in OAuth code | Public app URL for Google OAuth redirects. |
| `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, `GOOGLE_REDIRECT_URI` | none | Enables Google Calendar sync. |
| `APP_VERSION` | `0.1.0` | Version returned by `/api/health`. |
| `EXPO_PUBLIC_API_BASE_URL` | empty | Mobile backend URL. Required for physical devices unless the empty base URL is intentionally proxied. |
## Commands
| Command | Description |
| --- | --- |
| `npm run dev` | Start the web app with `next dev`. |
| `npm run build` | Build the web app. |
| `npm run start` | Start the built web app. |
| `npm run dev:mobile` | Start the Expo dev server. |
| `npm run android` | Run the Expo app on Android from the root script. |
| `npm run ios` | Run the Expo app on iOS from the root script. |
| `npm run lint` | Run ESLint across web, mobile, core, and api-client. |
| `npm run typecheck` | Run TypeScript checks across all workspaces. |
| `npm run test` | Run web Vitest tests and mobile Jest tests. |
Workspace-specific examples:
```bash
npm run test -w apps/web
npm run test -w apps/mobile
npm run typecheck -w packages/core
npm run typecheck -w packages/api-client
```
## Web Routes
| Route | Description |
| --- | --- |
| `/` | Departure desk with next upcoming event, train/bike mode selector, live journeys, route sections, reminders, and nearby Wiener Linien departures. |
| `/calendar` | Calendar import/management with URL, file, Google OAuth, and batch destination editing. |
The web add/edit event flow is implemented by `apps/web/src/app/add-event/AddEventModal.tsx`, not a standalone route.
## API Routes
| Endpoint | Methods | Notes |
| --- | --- | --- |
| `/api/health` | `GET` | Returns `{ ok, ts, version }`. |
| `/api/hafas` | `GET`, `POST` | GET is a simple trip search. POST validates and forwards `TripSearch` or `LocMatch`. |
| `/api/geocode` | `GET` | Requires `name`; optional `countrycodes`. Returns the first result. |
| `/api/bike-route` | `GET` | Requires `fromLat`, `fromLng`, `toLat`, `toLng`. |
| `/api/walk-route` | `GET` | Same coordinate contract as bike route, using OSRM foot profile. |
| `/api/calendar` | `GET` | Requires an allowed remote ICS `url`; optional `days`. |
| `/api/calendar/parse` | `POST` | Parses raw ICS text from the request body. |
| `/api/calendar/google` | `GET` | Requires Google OAuth token cookie; optional day horizon. |
| `/api/auth/google` | `GET` | Starts OAuth. |
| `/api/auth/google/callback` | `GET` | Completes OAuth. |
| `/api/auth/google/status` | `GET` | Reports configuration and connection state. |
| `/api/auth/google/disconnect` | `POST` | Clears OAuth token cookie. |
| `/api/wienerlinien/stops` | `GET` | Requires `lat`, `lng`; optional `radius`, capped server-side. |
| `/api/wienerlinien/monitor` | `GET` | Accepts repeated `stopIds`, capped server-side. |
## Docker
The web app includes Docker support:
```bash
cd apps/web
docker-compose up --build
```
## Testing Notes
- Web tests use Vitest, jsdom, and Testing Library.
- Mobile tests use Jest, jest-expo, and React Native Testing Library.
- Route, geocoding, HAFAS, calendar, routing, reminder, and UI component tests are present across the repo.
- Run `npm run typecheck` after changing shared types because both apps consume `packages/core`.
## Development Rules
- Use `@timetoleave/core` for shared behavior.
- Use `@timetoleave/api-client` for client-to-backend calls that are shared by web and mobile.
- Keep backend validation close to route handlers, then delegate external service behavior to `apps/web/src/lib`.
- Never parse HAFAS date/time values with ad hoc `Date` logic; use `parseHafasTime()` and `hafasDateTime()`.
- This repo uses Next.js 16. Read the relevant `node_modules/next/dist/docs/` material before changing Next.js framework code.