# 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 <repository-url>
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/<feature>/
  entities/<feature>.entity.ts          // extends BaseEntity (UUID, timestamps, soft delete)
  dto/<verb>-<feature>.dto.ts           // class-validator DTOs
  <feature>.module.ts                   // wires controller + service + repository
  <feature>.controller.ts               // HTTP layer only — no business logic
  <feature>.service.ts                  // business logic
  <feature>.repository.ts               // extends BaseRepository<Entity>; services inject this, NEVER `Repository<T>` 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
```

### 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 <jwt-token>`
- **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 <iam-token>`
- **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 <jwt-token>
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 <jwt-token>

# 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 <iam-token>
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/<branch>/` |
| [`.github/workflows/deploy-passenger.yml`](.github/workflows/deploy-passenger.yml) | passenger-api, passenger-portal, passenger-backoffice | `/home/user/environmen/edr-passenger/<branch>/` |

**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<T>`.
- **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.
