# Transit API Transit API for managing bus routes, navigation, and GPS tracking for bus drivers. ## Project Structure ``` transit_api/ ├── index.js # Main API endpoints ├── middleware.js # JWT authentication middleware ├── db.js # PostgreSQL connection ├── schema.sql # Database schema setup ├── package.json # Dependencies └── .env # Environment variables ``` ## Setup ### 1. Install Dependencies ```bash npm install ``` ### 2. Set Up Database Schema Execute the following in PostgreSQL: ```bash # Via psql command line psql -U your_user -d your_database -f schema.sql ``` Or copy-paste the SQL queries directly into pgAdmin. ### 3. Environment Variables (.env) ```env PG_HOST=localhost PG_PORT=5432 PG_USER=your_user PG_PASS=your_password PG_DB=your_database JWT_SECRET=your_jwt_secret PORT=5001 ``` Note: `ADMIN_USER_CODES` has been moved to the `auth_api` service. Configure admin user codes in `auth_api/.env` as a comma-separated list. ### 4. Start API ```bash npm start ``` The API runs at `https://transit.xtransit.testinstance.nl` The service group also includes the Employee API at `https://employees.xtransit.testinstance.nl` and the central monitoring dashboard. See [`../MONITORING_UI.md`](../MONITORING_UI.md) for health checks, error inspection, and the controlled request tester. ## API Endpoints All endpoints require an `Authorization: Bearer {token}` header. **GET** `/health` - Checks whether the deployed transit API is running and the database is reachable. - Returns `200` with `{"status":"ok","database":"ok"}` on success. - Returns `503` if the API is running but the database is unreachable. ### Schedule Schedules are weekly templates. Each row in `transit.schedules` represents one departure time for one bus line and one `day_of_week`: - `1` to `5`: weekdays - `6`: Saturday - `0`: Sunday Driver assignments are stored separately in `transit.shifts` with `(schedule_id, driver_id)`, where `driver_id` references `workers.users(id)`. The current `shifts` shape represents the active assignment. To retain different assignments for different calendar weeks, add a `week_start` (or assignment date range) column and include it in the unique key. **GET** `/driver/schedules/daily?date=2026-09-18` - Returns only weekly schedule templates assigned to the authenticated driver through `transit.shifts`. **PATCH** `/driver/weekly-schedules/:scheduleId/status` - Updates the status of an assigned weekly schedule. **GET** `/admin/weekly-schedules` - Lists weekly templates and their current driver assignments. **GET** `/admin/shift-drivers` - Lists drivers from `workers.users` for shift assignment. **POST** `/admin/weekly-schedules` - Creates one template. Body: `{ "busLineId": 1, "dayOfWeek": 1, "startTime": "08:00", "endTime": "09:00", "driverId": 12 }`. **POST** `/admin/weekly-schedules/bulk` - Creates multiple departures in one transaction. The API derives each end time from the selected bus line duration. Body: `{ "busLineId": 1, "dayOfWeeks": [1,2,3,4,5], "startTimes": ["06:00", "06:30", "07:00"], "driverId": 12 }`. **PUT** `/admin/weekly-schedules/:scheduleId/shift` - Replaces the driver assignment. Pass `{ "driverId": 12 }` or `{ "driverId": null }`. The legacy `/admin/schedules` and `/schedule/daily` endpoints remain for older clients, but new clients must use the weekly schedule endpoints above. **GET** `/schedule/daily?date=2024-05-05` - Retrieve daily schedule for driver - Response: ```json { "date": "2024-05-05", "routes": [ { "id": 1, "startTime": "2024-05-05T08:00:00Z", "status": "scheduled", "busLine": { "lineNumber": 1, "startStop": "Central Station", "endStop": "Airport" }, "busType": { "name": "Standard Bus", "seatCapacity": 50, "licensePlate": "BUS-001" } } ] } ``` **GET** `/routes/:routeId` - Details of a specific route - Response: `{ "route": {...} }` ### Public Schedule Overview **GET** `/schedules/overview?date=2026-05-06&lineId=1` - Fetches a date-based overview of scheduled services - Weekly templates are selected by `day_of_week` and driver assignments by `shifts` - One-off / incidental services are only included on their exact date - Filters: `driverId`/`chauffeurId`, `stopId`/`busStopId`, `lineId`, `status` - Response: `{ "date": "2026-05-06", "summary": {...}, "schedules": [...] }` The legacy endpoint **GET** `/schedules` remains available for existing clients. Example: ```bash curl "https://transit.xtransit.testinstance.nl/schedules/overview?date=2026-05-06&chauffeurId=12&busStopId=3" ``` ### Navigation **POST** `/navigation/route` - Save calculated route from OSRM - Body: ```json { "routeId": 1, "startLat": 52.5200, "startLon": 13.4050, "endLat": 52.5300, "endLon": 13.4150, "totalDistance": 15000, "totalDuration": 900, "geojson": { "type": "LineString", "coordinates": [[13.4050, 52.5200], ...] } } ``` **GET** `/bus-lines/:busLineId/stops` - All stops of a bus line (for navigation waypoints) - Response: ```json { "stops": [ { "id": 1, "order": 1, "name": "Central Station", "latitude": 52.52, "longitude": 13.405 } ] } ``` ### GPS Tracking **POST** `/gps/track` - Save real-time position - Body: ```json { "routeId": 1, "latitude": 52.521, "longitude": 13.406, "speed": 45.5, "heading": 180, "accuracy": 5.0 } ``` **GET** `/gps/route/:routeId` - All recorded position points of a route - Response: ```json { "positions": [ { "id": 1, "latitude": 52.521, "longitude": 13.406, "speed": 45.5, "heading": 180, "recordedAt": "2024-05-05T08:15:00Z" } ] } ``` ### Route Status **PATCH** `/routes/:routeId/status` - Update route status - Body: `{ "status": "in_progress" }` - Valid statuses: `scheduled`, `in_progress`, `completed`, `cancelled` ### Admin All admin endpoints require, in addition to a valid JWT token, that your `userCode` is present in `ADMIN_USER_CODES`. **POST** `/admin/stops` - Create a reusable stop - Body: ```json { "name": "Central Station", "latitude": 52.52, "longitude": 13.405 } ``` **POST** `/admin/bus-lines/:busLineId/stops` - Link an existing stop to a bus line - Body: ```json { "stopId": 1, "stopOrder": 1, "estimatedArrivalMinutes": 0 } ``` ## Database Schema ### bus_types - `id` - Primary key - `name` - Bus type name (Standard Bus, etc.) - `seat_capacity` - Number of seats - `license_plate` - Unique license plate ### bus_lines - `id` - Primary key - `line_number` - Unique line number (1, 5, 12, etc.) - `start_stop` - Start stop name - `end_stop` - End stop name - `estimated_duration_minutes` - Expected route duration ### stops - `id` - Primary key - `name` - Stop name - `latitude`, `longitude` - Stop location ### route_stops - `id` - Primary key - `bus_line_id` - Which bus line the stop belongs to - `stop_id` - Reusable stop reference - `stop_order` - Stop sequence on that bus line - `estimated_arrival_minutes` - Optional ETA per stop ### routes - `id` - Primary key - `user_id` - Driver (references workers.users) - `bus_line_id` - Which line (references bus_lines) - `bus_type_id` - Which bus (references bus_types) - `start_time` - Scheduled start - `status` - scheduled/in_progress/completed/cancelled ### navigation_routes - `id` - Primary key - `route_id` - Link to route (unique) - `polyline_geojson` - GeoJSON LineString of the route - `total_distance_meters` - Route distance - `total_duration_seconds` - Estimated duration ### gps_tracking - `id` - Primary key - `route_id` - Which route - `latitude`, `longitude` - Current position - `speed_kmh` - Current speed - `recorded_at` - Timestamp ## Integration with Flutter App ```dart // In schedule_service.dart // Save navigation route await scheduleService.saveNavigationRoute( routeId: 1, navRoute: navigationRoute, ); // Track GPS await scheduleService.trackGPS( routeId: 1, currentPos: navigationPoint, speed: 45.5, heading: 180, ); // Update route status await scheduleService.updateRouteStatus(1, 'in_progress'); ``` ## Error Handling All endpoints return error responses: ```json { "error": "error_code", "details": "More information" } ``` Common errors: - `missing_token` - No Authorization header - `invalid_token` - Token verification failed - `missing_required_fields` - Required data missing - `invalid_status` - Invalid status value - `route_not_found` - Route not found - `unauthorized` - User doesn't own this resource