# EDR Platform - Ethio-Djibouti Railway Passenger API Enterprise-grade NestJS REST API for the Ethio-Djibouti Railway passenger booking and management platform. Built with TypeScript, PostgreSQL, and Prisma ORM. ## ๐Ÿš€ Features ### ๐Ÿ†• NEW: Age-Based Pricing, Verifayda 2.0 & Multi-Currency #### Age-Based Pricing - **ADULT** (โ‰ฅ5 years): Pay 100% of base fare - **CHILD** (<5 years): First child travels FREE, subsequent children pay 100% - Automatic age calculation from date of birth - Example: 2 adults + 3 children = 4ร— base fare (first child free) #### Verifayda 2.0 Integration - Real-time Ethiopian national ID verification - Retrieves passenger data from government database - National IDs NOT stored (policy compliant) - Non-Ethiopians use passport (no verification required) - Booking fails if verification unsuccessful #### Multi-Currency Support - **Transaction Currency**: ETB (Ethiopian Birr) - **Display Currencies**: ETB, DJF (Djiboutian Franc), USD (US Dollar) - Real-time exchange rate conversion - Prices shown in user's preferred currency - Exchange rates: ETBโ†’DJF=3.25, ETBโ†’USD=0.018 ### Core Modules - **Authentication & Authorization** - Dual authentication system: - **Passenger Auth**: JWT-based auth with OTP verification, password reset, account lockout - **Corporate IAM**: Integration with @tria-plc corporate identity system for back-office operations (agents, supervisors, admins) - Role-based access control (RBAC) with granular permissions - **Age-Based Pricing** - Smart passenger categorization: - **ADULT** (โ‰ฅ5 years): Full fare - **CHILD** (<5 years): First child free, subsequent children full fare - Automatic age calculation from date of birth - **Verifayda 2.0 Integration** - Ethiopian national ID verification: - Real-time verification via government API - Retrieves passenger data (name, DOB, nationality) - National IDs NOT stored (policy compliant) - Non-Ethiopians use passport (no verification) - **Multi-Currency Support** - Display prices in multiple currencies: - **ETB** (Ethiopian Birr) - Transaction currency - **DJF** (Djiboutian Franc) - Display option - **USD** (US Dollar) - Display option - Real-time exchange rate conversion - **Booking Management** - Complete booking lifecycle: - **Guest Booking**: Book without login, optional account creation - **Saved Passengers**: Store passenger details for quick rebooking - Modification, cancellation, refunds, and fare breakdown - Multi-segment journey support - **Payment Integration** - Multi-provider support (Telebirr, CBE Birr, eBirr, Card, Wallet) with webhook handling - **Seat Management** - Real-time seat inventory: - Seat holds with 5-minute expiry - Seat releases and blocking with coach/class management - Segment-based seat availability (partial journey bookings) - Auto-assign seats with contiguous algorithm - CSV import/export for seat configurations - **Ticketing** - QR code and barcode generation, PDF tickets, gate validation with audit logs - **Agent Operations** - Counter booking, shift management, commission tracking, and reconciliation - **Passenger Services** - Profile management, traveler profiles, saved routes, and preferences - **Loyalty Program** - Points accumulation, tier management (Bronze/Silver/Gold/Platinum), and rewards - **Wallet System** - Balance management, top-up, transaction ledger - **Live Tracking** - Real-time trip status, location updates, delay notifications, crowd signals - **Notifications** - Multi-channel (Email, SMS, Push) with templating engine - **Support System** - FAQ management, live chat conversations - **Reports & Analytics** - Revenue reports, occupancy analytics, agent sales tracking - **Route Management** - Route configuration, stops, fare rules, baggage allowance ### Technical Features - **Security** - Password hashing (bcrypt), JWT tokens, rate limiting, audit logging - **Validation** - Request validation with class-validator, DTO transformation - **Documentation** - Auto-generated Swagger/OpenAPI docs at `/api-docs` - **Error Handling** - Global exception filters with standardized error responses - **Database** - PostgreSQL with Prisma ORM, migrations, and comprehensive seeding - **Scheduling** - Cron jobs for automated tasks (seat release, report generation) - **Event System** - Event-driven architecture with @nestjs/event-emitter ## ๐Ÿ“‹ Prerequisites - **Node.js** >= 20.x - **pnpm** >= 9.x (`npm install -g pnpm`) - **PostgreSQL** >= 15.x - **Git** ## ๐Ÿ› ๏ธ Installation & Setup ### 1. Clone Repository ```bash git clone cd edr-platform ``` ### 2. Install Dependencies # EDR Platform Monorepo for the **Ethio-Djibouti Railway** digital platform. Hosts two product lines โ€” **Freight Management** and **Passenger Management** โ€” each with a NestJS API plus React portal and back-office web apps, sharing TypeScript types, NestJS utilities, and a React component library. --- ## Tech Stack ### Backend | Layer | Tech | | ---------------- | ----------------------------------------------- | | Runtime | Node.js โ‰ฅ 20 | | Framework | NestJS 11 (modular architecture) | | Language | TypeScript 5 (strict mode, project-wide) | | ORM | TypeORM 0.3 (UUID PKs, soft deletes, `snake_case` columns) | | Database | PostgreSQL 16 (one DB per domain) | | Validation | class-validator + class-transformer | | API docs | Swagger via `@nestjs/swagger` | | Messaging | `@nestjs/microservices` (inter-service ready) | | Testing | Jest + Supertest | ### Frontend | Layer | Tech | | ---------------- | ----------------------------------------------- | | Framework | React 18 + Vite 5 | | Language | TypeScript 5 (strict) | | Routing | React Router v6 | | Styling | Tailwind CSS v4 (`@tailwindcss/vite`) + `tailwind-merge` + `class-variance-authority` | | UI primitives | Radix UI (meta `radix-ui` package, shadcn-style components) | | Icons | lucide-react | | State / Data | Zustand (client state) ยท TanStack Query (server state) | | HTTP | Axios | | Auth UI | `@tria-plc/iamui-common` (external IAM) | ### Shared Packages | Package | Purpose | | ---------------------- | -------------------------------------------------------------------- | | `@edr/types` | Shared TypeScript interfaces and enums | | `@edr/api-common` | NestJS decorators, filters, interceptors, pipes, `BaseEntity`, `BaseRepository` | | `@edr/ui-common` | Shared React components (`DashboardLayout`, `Sidebar`, `Button`, `Modal`, etc.) and theme tokens | | `@edr/eslint-config` | Shared ESLint configs (base / nestjs / react) | | `@edr/tsconfig` | Shared TypeScript configs | | `@edr/prettier-config` | Shared Prettier configuration | ### Tooling - **pnpm 9** โ€” workspace package manager (sole supported PM) - **Turborepo 2** โ€” task orchestrator with caching - **Husky + lint-staged + commitlint** โ€” pre-commit ESLint/Prettier and conventional-commit enforcement - **Docker Compose** โ€” local Postgres instances + production stack (see `infrastructure/`) - **Nginx** โ€” reverse proxy / static asset server in production ### Planned Integrations - **MinIO** โ€” S3-compatible object storage for documents (object keys already shaped under `edr-freight/{linkedType}/{ref}/{filename}`) --- ## Architecture ### High-level layout ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ EDR Platform (monorepo) โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Freight domain โ”‚ Passenger domain โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ freight-portal โ”‚ โ”‚ โ”‚ passenger- โ”‚ โ”‚ โ”‚ โ”‚ (React) โ”‚ โ”‚ โ”‚ portal (React)โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ โ”‚ โ”‚ freight- โ”‚ โ”‚ โ”‚ passenger- โ”‚ โ”‚ โ”‚ โ”‚ backoffice โ”‚ โ”‚ โ”‚ backoffice โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ–ผ โ”‚ โ–ผ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ freight-api โ”‚ โ”‚ โ”‚ passenger-api โ”‚ โ”‚ โ”‚ โ”‚ (NestJS) โ”‚ โ”‚ โ”‚ (NestJS) โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ–ผ โ”‚ โ–ผ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ postgres- โ”‚ โ”‚ โ”‚ postgres- โ”‚ โ”‚ โ”‚ โ”‚ freight โ”‚ โ”‚ โ”‚ passenger โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ Shared (workspace) packages @edr/types ยท @edr/api-common ยท @edr/ui-common ยท @edr/{eslint,tsconfig,prettier}-config ``` ### Domain isolation - **One database per domain.** `postgres-freight` (port 5433, db `edr_freight`) and `postgres-passenger` (port 5434, db `edr_passenger`). No cross-database joins. Cross-domain data flows only through API calls or message queues. - **Each domain owns its data model.** Freight bookings/consignments/shipments/trains/invoices/documents live only in the freight DB; passenger journeys/tickets live only in the passenger DB. ### NestJS module pattern (per feature) ``` modules// entities/.entity.ts // extends BaseEntity (UUID, timestamps, soft delete) dto/-.dto.ts // class-validator DTOs .module.ts // wires controller + service + repository .controller.ts // HTTP layer only โ€” no business logic .service.ts // business logic .repository.ts // extends BaseRepository; services inject this, NEVER `Repository` directly ``` Conventions enforced across the codebase: - All entities have UUID primary keys (`@PrimaryGeneratedColumn('uuid')`). - All entities inherit `createdAt` / `updatedAt` / `deletedAt` from `BaseEntity` (soft delete). - DB columns use `snake_case` via `@Column({ name: '...' })`; TS properties stay `camelCase`. - **No `synchronize: true`** in production โ€” schema changes go through TypeORM migrations. - ESLint + Prettier run on pre-commit via Husky + lint-staged. - Conventional-commits enforced via commitlint. ### Frontend application structure ``` apps/edr-freight-web/portal/src/ App.tsx // routes + sidebar definition main.tsx // React Router + QueryClient providers components/ Breadcrumbs.tsx // shared local UI ui/ // shadcn-style primitives (Button, Input, Dialog, Label, Textarea) pages/ customers/ // CustomersPage + CustomerDetailPage + NewCustomerPage (dialog) bookings/ // ... + multimodal Transport Legs editor consignments/ tracking/ // Shipment grid + table view toggle trains/ // Fleet roster billing/ // Invoices documents/ // MinIO-shaped document library hooks/ // TanStack Query hooks (per feature) services/ // Axios clients (per feature) store/ // Zustand stores lib/ // utilities (`cn` helper, formatters) ``` Each feature folder typically contains: `*Page.tsx` (list), `*DetailPage.tsx`, `New*Page.tsx` (create/edit dialog with `mode: "create" | "edit"`), `Delete*Dialog.tsx`, and a `*.mock.ts` seed file used by the current mock UI. ### Shared layout (`@edr/ui-common`) `DashboardLayout` provides the sidebar + header shell shared across all freight and passenger web apps: - **Sidebar** โ€” brand-tinted, icon-led navigation. Main brand color: `#10B981` (`rgb(16, 185, 129)` โ€” emerald-500). Icon containers use the filled style: brand-color background with a white icon. - **Header** โ€” language picker, notifications, user dropdown (click-driven, click-outside / Escape close); host apps opt into a light/dark theme toggle via `enableThemeToggle` (Tailwind class-based, persisted in `localStorage`). ### Auth integration (planned) Authentication is provided by an external `@edr/iamui-common` / `@tria-plc/iamapi-common` package. **Do not** implement login/JWT/password logic in this repo. Use placeholder TODO comments next to controllers and `@CurrentUser` decorators (in `@edr/api-common`) until the integration ships. --- ## Apps & Ports | App | Package name | Purpose | Port | | ------------------------------ | --------------------------- | ---------------------------------------- | ---- | | `edr-freight-api` | `@edr/freight-api` | NestJS API for freight | 3001 | | `edr-freight-web/portal` | `@edr/freight-portal` | React frontend for freight customers | 5173 | | `edr-freight-web/backoffice` | `@edr/freight-backoffice` | React frontend for freight employees | 5183 | | `edr-passenger-api` | `@edr/passenger-api` | NestJS API for passengers | 3002 | | `edr-passenger-web/portal` | `@edr/passenger-portal` | React frontend for passenger customers | 5174 | | `edr-passenger-web/backoffice` | `@edr/passenger-backoffice` | React frontend for passenger employees | 5184 | `edr-freight-web` and `edr-passenger-web` are grouping folders, not workspace packages. Each holds independent `portal/` and `backoffice/` workspace packages declared in `pnpm-workspace.yaml`. --- ## Getting Started ### Prerequisites - Node.js โ‰ฅ 20 - pnpm 9 (`corepack enable && corepack prepare pnpm@9.12.0 --activate`) - Docker (for local Postgres) ### Install ```bash pnpm install ``` <<<<<<< HEAD ### 3. Environment Configuration ```bash # Copy environment template cp apps/edr-passenger-api/.env.example apps/edr-passenger-api/.env # Edit .env file with your configuration ``` #### Required Environment Variables | Variable | Description | Example | |----------|-------------|---------| | `NODE_ENV` | Environment mode | `development` | | `PORT` | HTTP server port | `4000` | | `DATABASE_URL` | PostgreSQL connection string | `postgresql://user:pass@localhost:5432/edr_passenger` | | `JWT_SECRET` | JWT signing secret (change in production) | `your-secret-key` | | `JWT_EXPIRES_IN` | JWT token expiry | `7d` | | `PORTAL_URL` | Web app CORS origin | `http://localhost:3000` | | `BACK_OFFICE_URL` | Admin portal CORS origin | `http://localhost:3001` | | `SENDGRID_API_KEY` | SendGrid API key (optional) | `SG.xxx` | | `SENDGRID_FROM_EMAIL` | Email sender address | `noreply@edr-platform.com` | #### Verifayda 2.0 Configuration (Ethiopian National ID Verification) | Variable | Description | Example | |----------|-------------|---------| | `VERIFAYDA_ENABLED` | Enable Verifayda integration | `true` or `false` | | `VERIFAYDA_API_URL` | Verifayda API endpoint | `https://api.verifayda.gov.et/v2` | | `VERIFAYDA_API_KEY` | API key for Verifayda service | `your-verifayda-api-key` | **Note:** When `VERIFAYDA_ENABLED=false`, verification is skipped (development mode only). #### Corporate IAM Configuration (Back-office Authentication) | Variable | Description | Example | |----------|-------------|---------| | `IAM_ENABLED` | Enable corporate IAM integration | `true` or `false` | | `IAM_API_URL` | Corporate IAM API endpoint | `https://iam.tria-plc.com/api` | | `IAM_API_KEY` | API key for IAM service | `your-iam-api-key` | **Note:** When `IAM_ENABLED=false`, IAM-protected routes allow access without validation (development mode only). #### Optional: Payment Provider Configuration ```bash # Telebirr Configuration TELEBIRR_BASE_URL=https://api.telebirr.com TELEBIRR_MERCHANT_CODE=your-merchant-code TELEBIRR_APP_SECRET=your-app-secret # ... see .env.example for complete list ``` ### 4. Database Setup #### Start PostgreSQL ```bash # Using Docker (recommended) docker run --name edr-postgres \ -e POSTGRES_USER=edr \ -e POSTGRES_PASSWORD=edr_secret \ -e POSTGRES_DB=edr_passenger \ -p 5432:5432 \ -d postgres:15 # Or use your local PostgreSQL installation ``` #### Generate Prisma Client ```bash pnpm --filter @edr/passenger-api run prisma:generate ``` #### Run Migrations ```bash pnpm --filter @edr/passenger-api run prisma:migrate:dev ``` #### Seed Database ```bash pnpm --filter @edr/passenger-api run prisma:seed ``` **Seed Data Includes:** - 21 Stations (Complete Ethiopian-Djibouti Railway with country codes) - 1 Route with 21 stops and fare rules - 2 Train services with 4 trips - 360 seats across 12 coaches (Economy Regular, Economy Bed, VIP Bed classes) - 3 User accounts (Admin, Passenger, Agent) - Fare rules for ADULT and CHILD passenger categories - Currency exchange rates (ETB, DJF, USD) - Baggage allowance rules - Notification templates - Promotions and FAQ content - Menu items and station crowd signals - Fraud detection rules - Saved passenger profiles for testing ### 5. Start Development Server ```bash pnpm --filter @edr/passenger-api run dev ``` **API Server:** http://localhost:4000 **Swagger Docs:** http://localhost:4000/api-docs ## ๐Ÿ”‘ Default Credentials After seeding, use these credentials to test the API: | Role | Email | Password | Description | |------|-------|----------|-------------| | **Admin** | `admin@edr-platform.com` | `admin123` | Full system access, reports, agent management | | **Passenger** | `kelemu@email.com` | `password123` | Regular user with loyalty (Silver) and wallet | | **Agent** | `agent@edr-platform.com` | `agent123` | Counter booking agent with commission tracking | ## ๐Ÿ“š API Documentation ### Swagger UI Interactive API documentation available at: **http://localhost:4000/api-docs** ### Authentication Methods The API uses two authentication schemes: #### 1. JWT Authentication (Passenger-facing) - **Used for**: Passenger bookings, profile management, wallet, loyalty - **Header**: `Authorization: Bearer ` - **Obtain token**: `POST /auth/login` with passenger credentials - **Swagger Security**: `JWT-auth` #### 2. IAM Authentication (Back-office) - **Used for**: Agent operations, fraud detection, reports, admin functions - **Header**: `Authorization: Bearer ` - **Obtain token**: From corporate IAM system (https://iam.tria-plc.com) - **Swagger Security**: `IAM-auth` - **Roles**: AGENT, SUPERVISOR, ADMIN, STAFF ### API Endpoints Overview | Module | Base Path | Auth Type | Description | |--------|-----------|-----------|-------------| | **Auth** | `/auth` | Public/JWT | Register, login, OTP verification, password reset | | **Passengers** | `/passengers` | Public/JWT | Verifayda verification, international registration, profiles | | **Search** | `/search` | Public | Trip search, availability, fare quotes | | **Stations** | `/stations` | Public/JWT | Station directory and information | | **Seats** | `/seats` | JWT/IAM | Seat maps, holds, releases, blocking | | **Bookings** | `/bookings` | Public/JWT | Guest booking, create, modify, cancel bookings | | **Payments** | `/payments` | JWT/Public | Payment initiation, webhooks, refunds | | **Tickets** | `/tickets` | JWT/IAM | Ticket generation, QR/barcode, validation | | **Notifications** | `/notifications` | JWT | In-app notifications, preferences | | **Loyalty** | `/loyalty` | JWT | Points, tiers, rewards redemption | | **Wallet** | `/wallet` | JWT | Balance, top-up, transaction history | | **Promotions** | `/promos` | Public/JWT | Active promotions, promo code validation | | **Live Tracking** | `/live` | Public/JWT | Real-time trip status, crowd signals | | **Support** | `/support` | Public/JWT | FAQ, chat conversations | | **Dashboard** | `/dashboard` | JWT | Home screen aggregated data | | **Routes** | `/routes` | JWT/IAM | Reusable route templates with ordered stops | | **Schedules** | `/schedules` | JWT/IAM | Trip schedules, fare rules, status updates | | **Fleet** | `/fleet` | JWT/IAM | Train services, coaches, seat configurations | | **Seat Classes** | `/seat-classes` or `/classes` | Public/JWT/IAM | Seat class management and configuration | | **Segment Seats** | `/segments/seats` | Public/JWT | Segment-based seat availability and booking | | **Agents** | `/agents` | IAM | Agent booking, shifts, commissions, reconciliation | | **Fraud Detection** | `/fraud` | IAM | Fraud alerts, rules management, user blocking | | **Reports** | `/reports` | IAM | Revenue, occupancy, agent sales analytics | ### Example API Calls #### 1. Register Passenger ```bash POST /auth/register Content-Type: application/json { "email": "user@example.com", "phone": "+251911234567", "fullName": "John Doe", "password": "SecurePass123" } ``` #### 2. Login (Passenger) ```bash POST /auth/login Content-Type: application/json { "email": "user@example.com", "password": "SecurePass123" } # Response includes JWT token { "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "user": { "id": "uuid", "role": "PASSENGER" } } ``` #### 3. Verify Ethiopian National ID (Verifayda) ```bash POST /passengers/verify-fayda Content-Type: application/json { "nationalId": "ET123456789" } # Response with verified passenger data { "verified": true, "passengerData": { "fullName": "Abebe Kebede", "dateOfBirth": "1985-03-15T00:00:00.000Z", "gender": "Male", "nationality": "Ethiopian" } } ``` #### 4. Universal Passenger Registration (NEW) ```bash # Guest Ethiopian with Fayda verification POST /passengers/register Content-Type: application/json { "passengerName": "Abebe Kebede", "dateOfBirth": "1985-03-15", "nationalId": "ET123456789", "phone": "+251911234567", "deviceId": "device-uuid-123" } # Logged-in user with JWT token POST /passengers/register Authorization: Bearer Content-Type: application/json { "passengerName": "Abebe Kebede", "dateOfBirth": "1985-03-15", "nationalId": "ET123456789", "phone": "+251911234567" } # International passenger (passport) POST /passengers/register Content-Type: application/json { "passengerName": "John Smith", "dateOfBirth": "1990-07-20", "passportNumber": "P1234567", "passportCountry": "Kenya", "nationality": "Kenyan", "phone": "+254712345678", "email": "john@example.com", "deviceId": "device-uuid-123" } ``` #### 5. Get User Profile (NEW) ```bash GET /auth/profile Authorization: Bearer # Response includes user, passenger, loyalty, and wallet details { "id": "uuid", "email": "user@example.com", "phone": "+251911234567", "fullName": "John Doe", "role": "PASSENGER", "nationality": "Ethiopian", "faydaVerified": true, "faydaVerifiedAt": "2024-01-15T10:30:00.000Z", "passenger": { "id": "uuid", "loyalty": { "tier": "SILVER", "pointsBalance": 1500, "lifetimePoints": 3000 }, "wallet": { "balanceMinor": 50000, "currency": "ETB" } } } ``` #### 6. Search Trips ```bash POST /search Content-Type: application/json { "originStationId": "uuid", "destinationStationId": "uuid", "date": "2026-06-15", "adultCount": 2, "childCount": 1 } ``` #### 7. Get Fare Quote ```bash POST /search/fare-quote Content-Type: application/json { "tripId": "uuid", "serviceClass": "ECONOMY_REGULAR", "adultCount": 2, "childCount": 1, "displayCurrency": "USD" } # Response includes age-based pricing breakdown { "baseFareMinor": 35000, "adultCount": 2, "adultFareMinor": 70000, "childCount": 1, "freeChildrenCount": 1, "paidChildrenCount": 0, "childFareMinor": 0, "totalMinor": 73500, "currency": "ETB", "displayCurrency": "USD", "displayTotalMinor": 1323 } ``` #### 8. Guest Booking (No Login Required) ```bash POST /bookings/guest Content-Type: application/json { "tripId": "uuid", "holdId": "uuid", "serviceClass": "ECONOMY_REGULAR", "displayCurrency": "ETB", "passengers": [ { "seatId": "uuid", "passengerName": "Abebe Kebede", "dateOfBirth": "1985-03-15", "idDocumentType": "NATIONAL_ID", "idDocumentNumber": "ET123456789" } ], "createAccount": false, "savePassengerDetails": true, "deviceId": "device-uuid" } ``` #### 9. Agent Booking (IAM Auth) ```bash POST /agents/bookings Authorization: Bearer Content-Type: application/json { "tripId": "uuid", "seats": [...], "paymentMethod": "CASH", "cashReceived": 50000 } ``` ## ๐Ÿ—๏ธ Project Structure ``` apps/edr-passenger-api/ โ”œโ”€โ”€ prisma/ โ”‚ โ”œโ”€โ”€ schema.prisma # Database schema (40+ models) โ”‚ โ”œโ”€โ”€ seed.ts # Comprehensive seed script โ”‚ โ””โ”€โ”€ migrations/ # Database migrations โ”œโ”€โ”€ src/ โ”‚ โ”œโ”€โ”€ common/ # Shared utilities โ”‚ โ”‚ โ”œโ”€โ”€ filters/ # Exception filters โ”‚ โ”‚ โ”œโ”€โ”€ interceptors/ # Response interceptors โ”‚ โ”‚ โ”œโ”€โ”€ pipes/ # Validation pipes โ”‚ โ”‚ โ”œโ”€โ”€ i18n/ # Internationalization โ”‚ โ”‚ โ”œโ”€โ”€ jwt.guard.ts # JWT authentication guard (passengers) โ”‚ โ”‚ โ”œโ”€โ”€ jwt.strategy.ts # Passport JWT strategy โ”‚ โ”‚ โ”œโ”€โ”€ iam-adapter.ts # Corporate IAM guard (back-office) โ”‚ โ”‚ โ”œโ”€โ”€ iam.module.ts # IAM module โ”‚ โ”‚ โ”œโ”€โ”€ roles.guard.ts # RBAC authorization guard โ”‚ โ”‚ โ”œโ”€โ”€ roles.decorator.ts # Roles decorator โ”‚ โ”‚ โ”œโ”€โ”€ prisma.service.ts # Prisma client service โ”‚ โ”‚ โ””โ”€โ”€ prisma.module.ts # Prisma module โ”‚ โ”œโ”€โ”€ config/ # Configuration files โ”‚ โ”‚ โ”œโ”€โ”€ app.config.ts # App configuration โ”‚ โ”‚ โ”œโ”€โ”€ database.config.ts # Database configuration โ”‚ โ”‚ โ””โ”€โ”€ telebirr.config.ts # Payment provider config โ”‚ โ”œโ”€โ”€ modules/ # Feature modules โ”‚ โ”‚ โ”œโ”€โ”€ auth/ # Authentication & authorization (JWT) โ”‚ โ”‚ โ”œโ”€โ”€ agents/ # Agent operations (IAM-protected) โ”‚ โ”‚ โ”œโ”€โ”€ bookings/ # Booking management (JWT) โ”‚ โ”‚ โ”œโ”€โ”€ currency/ # Currency conversion service โ”‚ โ”‚ โ”œโ”€โ”€ dashboard/ # Dashboard aggregations (JWT) โ”‚ โ”‚ โ”œโ”€โ”€ fleet/ # Train fleet management (JWT/IAM) โ”‚ โ”‚ โ”œโ”€โ”€ fraud/ # Fraud detection (IAM-protected) โ”‚ โ”‚ โ”œโ”€โ”€ live/ # Live tracking (JWT) โ”‚ โ”‚ โ”œโ”€โ”€ loyalty/ # Loyalty program (JWT) โ”‚ โ”‚ โ”œโ”€โ”€ notifications/ # Notification system (JWT) โ”‚ โ”‚ โ”œโ”€โ”€ passengers/ # Passenger management (JWT) โ”‚ โ”‚ โ”œโ”€โ”€ payments/ # Payment processing (JWT/Webhooks) โ”‚ โ”‚ โ”œโ”€โ”€ promos/ # Promotions (JWT) โ”‚ โ”‚ โ”œโ”€โ”€ reports/ # Reports & analytics (IAM-protected) โ”‚ โ”‚ โ”œโ”€โ”€ schedules/ # Trip schedules (JWT/IAM) โ”‚ โ”‚ โ”œโ”€โ”€ search/ # Trip search (JWT) โ”‚ โ”‚ โ”œโ”€โ”€ seats/ # Seat management (JWT/IAM) โ”‚ โ”‚ โ”œโ”€โ”€ segments/ # Journey segments (JWT) โ”‚ โ”‚ โ”œโ”€โ”€ stations/ # Station management (JWT) โ”‚ โ”‚ โ”œโ”€โ”€ support/ # Customer support (JWT) โ”‚ โ”‚ โ”œโ”€โ”€ tickets/ # Ticketing (JWT/IAM) โ”‚ โ”‚ โ”œโ”€โ”€ verifayda/ # Verifayda 2.0 integration โ”‚ โ”‚ โ””โ”€โ”€ wallet/ # Wallet system (JWT) โ”‚ โ”œโ”€โ”€ app.module.ts # Root application module โ”‚ โ””โ”€โ”€ main.ts # Application entry point โ”œโ”€โ”€ test/ # E2E tests โ”œโ”€โ”€ .env.example # Environment template โ”œโ”€โ”€ Dockerfile # Docker configuration โ”œโ”€โ”€ nest-cli.json # NestJS CLI configuration โ”œโ”€โ”€ package.json # Dependencies & scripts โ”œโ”€โ”€ tsconfig.json # TypeScript configuration โ””โ”€โ”€ tsconfig.build.json # Build configuration ``` ## ๐Ÿ—„๏ธ Database Schema ### Key Models (40+ total) **Core Entities:** - `User`, `Session`, `Passenger`, `Agent` - `Station`, `Route`, `RouteStop`, `RouteFareRule` - `TrainService`, `Trip`, `TripStopTime`, `Coach`, `Seat` - `Booking`, `BookingSeat`, `Ticket` - `PaymentIntent`, `PaymentRefund`, `PaymentWebhookEvent` **Enhanced Features:** - `OtpCode`, `PasswordResetToken` (Auth) - `AgentBooking`, `AgentShift`, `AgentCommission` (Agents) - `BookingModification`, `BookingCancellation` (Booking lifecycle) - `GateValidationLog` (Ticket validation) - `BaggageAllowance`, `BaggageBooking` (Baggage) - `LoyaltyAccount`, `LoyaltyLedgerEntry`, `LoyaltyReward` - `WalletAccount`, `WalletLedgerEntry` - `Notification`, `NotificationTemplate` - `AuditLog`, `OperationalReport` - `SeatBlock`, `SeatHold` - `CurrencyExchangeRate` (Multi-currency) - `VerifaydaVerification` (National ID verification) - `SavedPassengerProfile` (Guest booking) - `SeatClass` (Seat class configuration) - `JourneySegment` (Multi-segment journeys) ## ๐Ÿ”ง Available Scripts ```bash # Development pnpm --filter @edr/passenger-api run dev # Start with hot-reload # Build pnpm --filter @edr/passenger-api run build # Compile TypeScript # Production pnpm --filter @edr/passenger-api run start # Run compiled code # Testing pnpm --filter @edr/passenger-api run test # Unit tests pnpm --filter @edr/passenger-api run test:e2e # E2E tests # Code Quality pnpm --filter @edr/passenger-api run lint # ESLint pnpm --filter @edr/passenger-api run type-check # TypeScript check # Database pnpm --filter @edr/passenger-api run prisma:generate # Generate Prisma client pnpm --filter @edr/passenger-api run prisma:migrate:dev # Run migrations (local dev) pnpm --filter @edr/passenger-api run prisma:seed # Seed database ``` ## ๐Ÿณ Docker Deployment All six apps build from Dockerfiles: each API has its own (`apps/edr-freight-api/Dockerfile`, `apps/edr-passenger-api/Dockerfile`); Vite frontends share `infrastructure/docker/Dockerfile.web` and are served with **nginx**. APIs run on **Node 22**. **Prerequisites** - Docker with BuildKit enabled - A local [`.npmrc`](.gitignore) with GitHub Packages auth for `@tria-plc/*` (required for **freight** API and web images) - External Postgres for each API (compose does **not** include databases) - Copy `apps/edr-freight-api/.env.example` โ†’ `.env` and `apps/edr-passenger-api/.env.example` โ†’ `.env` with real connection strings ### Build and run (all apps) ```bash # From monorepo root DOCKER_BUILDKIT=1 pnpm docker:build pnpm docker:up ``` Or without pnpm scripts: ```bash DOCKER_BUILDKIT=1 docker compose build docker compose up -d ``` | Service | URL (default) | |---------|----------------| | Freight API | http://localhost:3001 | | Passenger API | http://localhost:4000 | | Freight portal | http://localhost:5173 | | Freight backoffice | http://localhost:5183 | | Passenger portal | http://localhost:5174 | | Passenger backoffice | http://localhost:5184 | ### Build a single service ```bash docker compose build freight-api docker compose build passenger-portal ``` Freight images mount `.npmrc` as a BuildKit secret during `pnpm install`. Passenger web images do not require private packages. ### `VITE_API_URL` (frontends) API URLs are **baked in at image build time** (`import.meta.env.VITE_API_URL`). Defaults in [`docker-compose.yaml`](docker-compose.yaml) use `http://localhost:3001/api` (freight) and `http://localhost:4000` (passenger) for local smoke tests. Override build args for production, e.g.: ```bash docker compose build freight-portal \ --build-arg VITE_API_URL=https://freight-api.example.com/api ``` ### Migrations - **Freight API:** TypeORM migrations are not run on container startup โ€” apply them separately before deploy. - **Passenger API:** On each container start, the entrypoint runs `npm run prisma:migrate` and `npm run prisma:seed` (same `package.json` scripts as `pnpm run`) before starting the server. Ensure `DATABASE_URL` in `.env` points at a reachable Postgres instance. For local development, use `pnpm --filter @edr/passenger-api run prisma:migrate:dev` instead of `prisma:migrate`. ### GitHub Actions (self-hosted runner) Two workflows deploy independently on push to `main`, `develop`, or `staging`: | Workflow | Services | Server env root | |----------|----------|-----------------| | [`.github/workflows/deploy-freight.yml`](.github/workflows/deploy-freight.yml) | freight-api, freight-portal, freight-backoffice | `/home/user/environmen/edr-freight//` | | [`.github/workflows/deploy-passenger.yml`](.github/workflows/deploy-passenger.yml) | passenger-api, passenger-portal, passenger-backoffice | `/home/user/environmen/edr-passenger//` | **On the runner**, place env files before the first deploy (example for branch `main`): ```text /home/user/environmen/edr-freight/main/ freight-api.env freight-portal.env # optional runtime env for Vite/nginx freight-backoffice.env freight-web.build.env # exports FREIGHT_VITE_API_URL=... /home/user/environmen/edr-passenger/main/ passenger-api.env passenger-portal.env passenger-backoffice.env passenger-web.build.env # exports PASSENGER_VITE_API_URL=... ``` Example `freight-web.build.env`: ```bash export FREIGHT_VITE_API_URL=https://freight-api.example.com/api ``` The workflow copies `*.env` into each app directory, creates `.npmrc` from the `NPM_TOKEN` repository secret, then runs `docker compose build` and `docker compose up -d` for that stack. ## ๐Ÿ”’ Security Best Practices 1. **Environment Variables** - Never commit `.env` files. Use secrets management in production. 2. **JWT Secret** - Use strong, randomly generated secrets (min 32 characters). 3. **Password Hashing** - Bcrypt with salt rounds (default: 10). 4. **Rate Limiting** - Implement rate limiting for auth endpoints. 5. **CORS** - Configure allowed origins in production. 6. **HTTPS** - Always use HTTPS in production. 7. **Database** - Use connection pooling and prepared statements (Prisma handles this). 8. **Audit Logging** - All sensitive operations are logged in `AuditLog` table. 9. **Dual Authentication** - Passenger routes use JWT, back-office routes use corporate IAM. 10. **IAM Integration** - Corporate IAM validates tokens against centralized identity service. 11. **Role-Based Access** - Granular permissions enforced via IAM roles (AGENT, SUPERVISOR, ADMIN). 12. **Token Validation** - IAM tokens validated in real-time with 5-second timeout. ## ๐Ÿ“Š Monitoring & Logging - **Application Logs** - NestJS built-in logger - **Database Queries** - Prisma query logging (enable in development) - **Audit Trail** - All user actions logged in `AuditLog` table - **Error Tracking** - Global exception filters with detailed error responses ## ๐Ÿงช Testing ```bash # Unit tests pnpm --filter @edr/passenger-api run test # E2E tests pnpm --filter @edr/passenger-api run test:e2e # Test coverage pnpm --filter @edr/passenger-api run test:cov ``` ## ๐Ÿš€ Production Deployment ### Pre-deployment Checklist - [ ] Update environment variables (JWT_SECRET, DATABASE_URL, etc.) - [ ] Configure IAM integration (IAM_ENABLED=true, IAM_API_URL, IAM_API_KEY) - [ ] Configure Verifayda integration (VERIFAYDA_ENABLED=true, VERIFAYDA_API_KEY) - [ ] Set up currency exchange rate sync (external API) - [ ] Set NODE_ENV=production - [ ] Configure CORS origins (PORTAL_URL, BACK_OFFICE_URL) - [ ] Set up SSL/TLS certificates - [ ] Configure database connection pooling - [ ] Set up monitoring and logging - [ ] Configure backup strategy - [ ] Test payment provider integrations - [ ] Verify IAM token validation endpoint - [ ] Test Verifayda verification with real national IDs - [ ] Verify currency conversion accuracy - [ ] Test age-based pricing calculations - [ ] Review security settings and audit logs - [ ] Test both JWT and IAM authentication flows ### Deployment Steps ```bash # 1. Build application pnpm --filter @edr/passenger-api run build # 2. Run migrations pnpm --filter @edr/passenger-api run prisma:migrate # 3. Start production server NODE_ENV=production pnpm --filter @edr/passenger-api run start:prod ``` ## ๐Ÿค Contributing 1. Fork the repository 2. Create feature branch (`git checkout -b feature/amazing-feature`) 3. Commit changes (`git commit -m 'Add amazing feature'`) 4. Push to branch (`git push origin feature/amazing-feature`) 5. Open Pull Request ## ๐Ÿ“ License This project is proprietary and confidential. ## ๐Ÿ“ง Support For technical support or questions: - Email: support@edr-platform.com - Documentation: http://localhost:4000/api-docs --- **Built with โค๏ธ for Ethio-Djibouti Railway** ======= ### Start local databases ```bash docker compose -f infrastructure/docker/docker-compose.db.dev.yml up -d ``` ### Run apps ```bash pnpm dev # every app pnpm dev:freight # freight API + portal + backoffice pnpm dev:passenger # passenger API + portal + backoffice ``` ### Common scripts | Script | Description | | ------------------- | ------------------------------------ | | `pnpm install` | Install workspace dependencies | | `pnpm dev` | Run every app in watch mode | | `pnpm build` | Build every package and app | | `pnpm test` | Run all tests | | `pnpm lint` | Lint everything | | `pnpm type-check` | Type-check every package | | `pnpm format` | Format with Prettier | --- ## Repository Layout ``` . โ”œโ”€โ”€ apps/ โ”‚ โ”œโ”€โ”€ edr-freight-api/ NestJS โ€” freight backend โ”‚ โ”œโ”€โ”€ edr-freight-web/ โ”‚ โ”‚ โ”œโ”€โ”€ portal/ React โ€” freight customer portal โ”‚ โ”‚ โ””โ”€โ”€ backoffice/ React โ€” freight back-office โ”‚ โ”œโ”€โ”€ edr-passenger-api/ NestJS โ€” passenger backend โ”‚ โ””โ”€โ”€ edr-passenger-web/ โ”‚ โ”œโ”€โ”€ portal/ React โ€” passenger customer portal โ”‚ โ””โ”€โ”€ backoffice/ React โ€” passenger back-office โ”œโ”€โ”€ packages/ โ”‚ โ”œโ”€โ”€ api-common/ Shared NestJS utilities + BaseEntity/Repository โ”‚ โ”œโ”€โ”€ types/ Shared TS types/enums โ”‚ โ”œโ”€โ”€ ui-common/ Shared React components + theme โ”‚ โ””โ”€โ”€ config/ โ”‚ โ”œโ”€โ”€ eslint/ @edr/eslint-config โ”‚ โ”œโ”€โ”€ tsconfig/ @edr/tsconfig โ”‚ โ””โ”€โ”€ prettier/ @edr/prettier-config โ”œโ”€โ”€ infrastructure/ โ”‚ โ”œโ”€โ”€ docker/ docker-compose files (db.dev / dev / prod) โ”‚ โ””โ”€โ”€ nginx/ Production nginx config โ”œโ”€โ”€ CLAUDE.md Developer guide for AI-assisted work โ”œโ”€โ”€ turbo.json Turborepo task pipeline โ”œโ”€โ”€ pnpm-workspace.yaml Workspace manifest โ””โ”€โ”€ README.md ``` --- ## Standards (recap) - **TypeScript strict mode** is enabled in every package. - **pnpm only** โ€” never run `npm install` or `yarn`. - **Conventional commits** โ€” enforced via commitlint on every commit. - **NestJS 4-layer pattern** โ€” `module โ†’ controller โ†’ service โ†’ repository`. - **Repository injection** โ€” services inject the custom `*Repository` class, not `Repository`. - **Controllers are thin** โ€” no business logic; delegate to services. - **Migrations only** โ€” never enable TypeORM `synchronize` in production. - **One DB per domain** โ€” no cross-database joins. See [`CLAUDE.md`](./CLAUDE.md) for the deeper developer guide used during AI-assisted contributions. >>>>>>> b9cfce70fe17b5066ae5320cfcc595bf3c253467