mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
Merge branch 'dev' into alpha
This commit is contained in:
@@ -1,26 +0,0 @@
|
||||
FROM node:20-alpine AS base
|
||||
RUN corepack enable && corepack prepare pnpm@9.12.0 --activate
|
||||
WORKDIR /app
|
||||
|
||||
FROM base AS deps
|
||||
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
|
||||
COPY apps/edr-freight-api/package.json ./apps/edr-freight-api/
|
||||
COPY packages ./packages
|
||||
RUN pnpm install --frozen-lockfile --filter @edr/freight-api...
|
||||
|
||||
FROM deps AS build
|
||||
COPY apps/edr-freight-api ./apps/edr-freight-api
|
||||
RUN pnpm --filter @edr/freight-api build
|
||||
|
||||
FROM node:20-alpine AS runtime
|
||||
RUN corepack enable && corepack prepare pnpm@9.12.0 --activate
|
||||
WORKDIR /app/apps/edr-freight-api
|
||||
ENV NODE_ENV=production
|
||||
|
||||
COPY --from=deps /app/node_modules ./../../node_modules
|
||||
COPY --from=deps /app/apps/edr-freight-api/node_modules ./node_modules
|
||||
COPY --from=build /app/apps/edr-freight-api/dist ./dist
|
||||
COPY --from=build /app/apps/edr-freight-api/package.json ./package.json
|
||||
|
||||
EXPOSE 3001
|
||||
CMD ["node", "dist/main.js"]
|
||||
@@ -1,18 +0,0 @@
|
||||
FROM node:20-alpine AS base
|
||||
RUN corepack enable && corepack prepare pnpm@9.12.0 --activate
|
||||
WORKDIR /app
|
||||
|
||||
FROM base AS deps
|
||||
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
|
||||
COPY apps/edr-freight-web/backoffice/package.json ./apps/edr-freight-web/backoffice/
|
||||
COPY packages ./packages
|
||||
RUN pnpm install --frozen-lockfile --filter @edr/freight-backoffice...
|
||||
|
||||
FROM deps AS build
|
||||
COPY apps/edr-freight-web/backoffice ./apps/edr-freight-web/backoffice
|
||||
RUN pnpm --filter @edr/freight-backoffice build
|
||||
|
||||
FROM nginx:1.27-alpine AS runtime
|
||||
COPY --from=build /app/apps/edr-freight-web/backoffice/dist /usr/share/nginx/html
|
||||
EXPOSE 5183
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
@@ -1,18 +0,0 @@
|
||||
FROM node:20-alpine AS base
|
||||
RUN corepack enable && corepack prepare pnpm@9.12.0 --activate
|
||||
WORKDIR /app
|
||||
|
||||
FROM base AS deps
|
||||
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
|
||||
COPY apps/edr-freight-web/portal/package.json ./apps/edr-freight-web/portal/
|
||||
COPY packages ./packages
|
||||
RUN pnpm install --frozen-lockfile --filter @edr/freight-portal...
|
||||
|
||||
FROM deps AS build
|
||||
COPY apps/edr-freight-web/portal ./apps/edr-freight-web/portal
|
||||
RUN pnpm --filter @edr/freight-portal build
|
||||
|
||||
FROM nginx:1.27-alpine AS runtime
|
||||
COPY --from=build /app/apps/edr-freight-web/portal/dist /usr/share/nginx/html
|
||||
EXPOSE 5173
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
@@ -1,28 +0,0 @@
|
||||
FROM node:20-alpine AS base
|
||||
RUN corepack enable && corepack prepare pnpm@9.12.0 --activate
|
||||
WORKDIR /app
|
||||
|
||||
FROM base AS deps
|
||||
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
|
||||
COPY apps/edr-passenger-api/package.json ./apps/edr-passenger-api/
|
||||
COPY packages ./packages
|
||||
RUN pnpm install --frozen-lockfile --filter @edr/passenger-api...
|
||||
|
||||
FROM deps AS build
|
||||
COPY apps/edr-passenger-api ./apps/edr-passenger-api
|
||||
RUN pnpm --filter @edr/passenger-api run prisma:generate
|
||||
RUN pnpm --filter @edr/passenger-api build
|
||||
|
||||
FROM node:20-alpine AS runtime
|
||||
RUN corepack enable && corepack prepare pnpm@9.12.0 --activate
|
||||
WORKDIR /app/apps/edr-passenger-api
|
||||
ENV NODE_ENV=production
|
||||
|
||||
COPY --from=deps /app/node_modules ./../../node_modules
|
||||
COPY --from=deps /app/apps/edr-passenger-api/node_modules ./node_modules
|
||||
COPY --from=build /app/apps/edr-passenger-api/dist ./dist
|
||||
COPY --from=build /app/apps/edr-passenger-api/package.json ./package.json
|
||||
COPY --from=build /app/apps/edr-passenger-api/prisma ./prisma
|
||||
|
||||
EXPOSE 4000
|
||||
CMD ["node", "dist/main.js"]
|
||||
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/nest-cli",
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src",
|
||||
"compilerOptions": {
|
||||
"deleteOutDir": true,
|
||||
"plugins": ["@nestjs/swagger"],
|
||||
"tsConfigPath": "tsconfig.build.json",
|
||||
"watchAssets": true
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "nest start --watch",
|
||||
"build": "nest build",
|
||||
"build": "prisma generate && nest build",
|
||||
"start": "node dist/main.js",
|
||||
"start:prod": "node dist/main.js",
|
||||
"lint": "eslint src",
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import 'reflect-metadata';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
||||
import { AppModule } from './app.module';
|
||||
import { HttpExceptionFilter } from './common/filters/http-exception.filter';
|
||||
import { ResponseTransformInterceptor } from './common/interceptors/response-transform.interceptor';
|
||||
import { SessionActivityInterceptor } from './common/interceptors/session-activity.interceptor';
|
||||
import "reflect-metadata";
|
||||
import { NestFactory } from "@nestjs/core";
|
||||
import { ValidationPipe } from "@nestjs/common";
|
||||
import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger";
|
||||
import { AppModule } from "./app.module";
|
||||
import { HttpExceptionFilter } from "./common/filters/http-exception.filter";
|
||||
import { ResponseTransformInterceptor } from "./common/interceptors/response-transform.interceptor";
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
|
||||
app.enableCors({
|
||||
origin: [
|
||||
process.env.FRONTEND_URL ?? 'http://localhost:3000',
|
||||
process.env.PORTAL_URL ?? 'http://localhost:3001',
|
||||
process.env.FRONTEND_URL ?? "http://localhost:3000",
|
||||
process.env.PORTAL_URL ?? "http://localhost:3001",
|
||||
],
|
||||
});
|
||||
|
||||
@@ -25,196 +24,43 @@ async function bootstrap() {
|
||||
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));
|
||||
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('EDR Passenger API')
|
||||
.setTitle("EDR Passenger API")
|
||||
.setDescription(
|
||||
`# Ethio-Djibouti Railway Passenger Booking API
|
||||
|
||||
## Overview
|
||||
Enterprise-grade REST API for the Ethio-Djibouti Railway passenger booking and management platform. Built with NestJS, TypeScript, PostgreSQL, and Prisma ORM.
|
||||
|
||||
## Authentication
|
||||
|
||||
### Passenger Authentication (JWT-auth)
|
||||
Used for passenger-facing endpoints. Obtain token via \`POST /auth/login\`.
|
||||
|
||||
**Usage:** Add header \`Authorization: Bearer <token>\`
|
||||
|
||||
### Back-office Authentication (IAM-auth)
|
||||
Used for agent, fraud, and reporting endpoints. Requires corporate IAM token.
|
||||
|
||||
**Usage:** Add header \`Authorization: Bearer <iam-token>\`
|
||||
|
||||
## Key Features
|
||||
|
||||
### 🎫 Booking Lifecycle
|
||||
- Search trips with real-time availability
|
||||
- Create bookings with seat selection
|
||||
- Modify bookings (seat changes, passenger updates)
|
||||
- Cancel bookings with automatic refunds
|
||||
- Multi-segment journey support
|
||||
|
||||
### 💳 Payment Integration
|
||||
- **Telebirr** - Ethiopia's leading mobile money
|
||||
- **CBE Birr** - Commercial Bank of Ethiopia
|
||||
- **eBirr** - Electronic payment gateway
|
||||
- **Card** - International card payments
|
||||
- **Wallet** - Internal wallet system
|
||||
|
||||
### 🪑 Seat Management
|
||||
- Real-time seat availability
|
||||
- Seat holds (15-minute expiry)
|
||||
- Auto-assign seats with contiguous algorithm
|
||||
- Seat blocking for maintenance
|
||||
- Coach-level seat maps
|
||||
|
||||
### 🎟️ Ticketing
|
||||
- QR code and barcode generation
|
||||
- PDF ticket generation
|
||||
- Gate validation with audit logs
|
||||
- Offline validation support
|
||||
|
||||
### 🏆 Loyalty Program
|
||||
- 4 tiers: Bronze, Silver, Gold, Platinum
|
||||
- Points accumulation on trips
|
||||
- Reward redemption
|
||||
- Tier-based benefits
|
||||
|
||||
### 💰 Wallet System
|
||||
- Top-up via payment methods
|
||||
- Pay with wallet balance
|
||||
- Transaction ledger
|
||||
- Refund to wallet
|
||||
|
||||
### 📍 Live Tracking
|
||||
- Real-time trip status
|
||||
- Location updates
|
||||
- Delay notifications
|
||||
- Station crowd signals
|
||||
|
||||
### 🔒 Fraud Detection
|
||||
- Velocity checks (multiple bookings)
|
||||
- High-value transaction monitoring
|
||||
- Failed payment pattern detection
|
||||
- Automatic user blocking
|
||||
|
||||
### 🌍 Internationalization
|
||||
- Multi-language support (English, Amharic, French, Oromo)
|
||||
- Locale-based responses
|
||||
- Currency formatting
|
||||
|
||||
### 👨💼 Agent Operations
|
||||
- Counter booking
|
||||
- Shift management
|
||||
- Commission tracking
|
||||
- Cash reconciliation
|
||||
|
||||
## Rate Limiting
|
||||
- Auth endpoints: 5 requests/minute
|
||||
- General endpoints: 100 requests/minute
|
||||
- Webhook endpoints: No limit
|
||||
|
||||
## Error Handling
|
||||
All errors follow standard format:
|
||||
\`\`\`json
|
||||
{
|
||||
"statusCode": 400,
|
||||
"message": "Validation failed",
|
||||
"error": "Bad Request",
|
||||
"timestamp": "2026-05-20T14:30:00.000Z",
|
||||
"path": "/bookings"
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## Pagination
|
||||
List endpoints support pagination:
|
||||
- \`limit\`: Number of items (default: 20, max: 100)
|
||||
- \`offset\`: Skip items (default: 0)
|
||||
|
||||
## Webhooks
|
||||
Payment providers send notifications to:
|
||||
- \`POST /payments/webhooks/telebirr\`
|
||||
- \`POST /payments/webhooks/cbe-birr\`
|
||||
- \`POST /payments/webhooks/ebirr\`
|
||||
- \`POST /payments/webhooks/card\`
|
||||
|
||||
## Support
|
||||
- **Email:** support@edr-platform.com
|
||||
- **Documentation:** https://docs.edr-platform.com
|
||||
- **Status Page:** https://status.edr-platform.com
|
||||
`,
|
||||
"Ethio-Djibouti Railway Passenger API — booking lifecycle, seat inventory, payment (Telebirr, CBE Birr, eBirr, Card, Wallet), loyalty, live tracking, and support.",
|
||||
)
|
||||
.setVersion('1.0.0')
|
||||
.setVersion("1.0.0")
|
||||
.addBearerAuth(
|
||||
{
|
||||
type: 'http',
|
||||
scheme: 'bearer',
|
||||
bearerFormat: 'JWT',
|
||||
in: 'header',
|
||||
description: 'JWT token for passenger authentication. Obtain via POST /auth/login'
|
||||
},
|
||||
'JWT-auth'
|
||||
{ type: "http", scheme: "bearer", bearerFormat: "JWT", in: "header" },
|
||||
"JWT-auth",
|
||||
)
|
||||
.addBearerAuth(
|
||||
{
|
||||
type: 'http',
|
||||
scheme: 'bearer',
|
||||
bearerFormat: 'JWT',
|
||||
in: 'header',
|
||||
description: 'Corporate IAM token for back-office operations (agents, fraud, reports)'
|
||||
},
|
||||
'IAM-auth'
|
||||
)
|
||||
.addTag('Auth', '🔐 Registration, login, OTP verification, password reset')
|
||||
.addTag('Agents', '👨💼 Agent booking, shifts, commissions, reconciliation')
|
||||
.addTag('Booking', '🎫 Booking lifecycle, modification, cancellation, refunds')
|
||||
.addTag('Dashboard', '📊 Home dashboard aggregated data')
|
||||
.addTag('Fleet', '🚂 Trains, physical coaches, seat auto-generation, coach-to-schedule assignments')
|
||||
.addTag('Seat Classes', '🎨 Seat class management and configuration')
|
||||
.addTag('Fraud Detection', '🔒 Fraud detection, risk scoring, user blocking')
|
||||
.addTag('Live Tracking', '📍 Real-time trip status, location updates, crowd signals')
|
||||
.addTag('Loyalty', '🏆 Points accumulation, tier management, rewards redemption')
|
||||
.addTag('Notifications', '🔔 Push, email, SMS notifications, preferences')
|
||||
.addTag('Passenger', '👤 Profiles, traveler profiles, saved routes, preferences')
|
||||
.addTag('Payment', '💳 Payment intents, status queries, refunds')
|
||||
.addTag('Payment Webhooks', '🔗 Payment provider callback endpoints')
|
||||
.addTag('Promotions', '🎁 Promo codes, campaigns, discount validation')
|
||||
.addTag('Reports', '📈 Revenue reports, occupancy analytics, agent sales')
|
||||
.addTag('Routes', '🗺️ Reusable route templates with ordered stops — referenced by schedules')
|
||||
.addTag('Schedule', '🗓️ Train schedules (created from routes), stop time management, fare rules')
|
||||
.addTag('Search', '🔍 Trip search, availability, fare quotes')
|
||||
.addTag('Seats', '🪑 Seat maps, holds, releases, blocking, auto-assign')
|
||||
.addTag('Segment-based Seats', '🎯 Segment-based seat availability and booking')
|
||||
.addTag('Stations', '🚉 Station directory, information, crowd signals')
|
||||
.addTag('Support', '💬 FAQ management, live chat conversations')
|
||||
.addTag('Tickets', '🎟️ QR/barcode generation, PDF tickets, gate validation')
|
||||
.addTag('Wallet', '💰 Wallet balance, top-up, transaction ledger')
|
||||
.addServer('http://localhost:4000', 'Local Development')
|
||||
.addServer('https://api-staging.edr-platform.com', 'Staging Environment')
|
||||
.addServer('https://api.edr-platform.com', 'Production')
|
||||
.addTag("Auth", "Registration and login")
|
||||
.addTag("Stations", "Station directory")
|
||||
.addTag("Fleet", "Train services and coaches")
|
||||
.addTag("Schedule", "Trips and fare rules")
|
||||
.addTag("Search", "Trip search and fare quotes")
|
||||
.addTag("Seats", "Seat maps and holds")
|
||||
.addTag("Booking", "Booking lifecycle")
|
||||
.addTag("Payment", "Payment intents and refunds")
|
||||
.addTag("Tickets", "QR ticket generation and validation")
|
||||
.addTag("Passenger", "Profiles, traveler profiles, saved routes")
|
||||
.addTag("Notifications", "Push and email notifications")
|
||||
.addTag("Loyalty", "Points, tiers, and rewards")
|
||||
.addTag("Wallet", "Wallet balance and ledger")
|
||||
.addTag("Promotions", "Promo codes and campaigns")
|
||||
.addTag("Live Tracking", "Real-time trip status and crowd signals")
|
||||
.addTag("Support", "FAQ and chat support")
|
||||
.addTag("Dashboard", "Home dashboard aggregate")
|
||||
//.addServer('http://localhost:4000', 'Development')
|
||||
// .addServer("https://api.edr-platform.com", "Production")
|
||||
.build();
|
||||
|
||||
const document = SwaggerModule.createDocument(app, config);
|
||||
SwaggerModule.setup('api-docs', app, document, {
|
||||
customSiteTitle: 'EDR Passenger API Documentation',
|
||||
customfavIcon: 'https://edr-platform.com/favicon.ico',
|
||||
customCss: `
|
||||
.swagger-ui .topbar { display: none }
|
||||
.swagger-ui .info { margin: 20px 0 }
|
||||
.swagger-ui .info .title { font-size: 36px; font-weight: bold }
|
||||
.swagger-ui .scheme-container { background: #fafafa; padding: 15px; border-radius: 4px }
|
||||
`,
|
||||
swaggerOptions: {
|
||||
persistAuthorization: true,
|
||||
docExpansion: 'none',
|
||||
SwaggerModule.setup("api-docs", app, document, {
|
||||
customSiteTitle: "EDR Passenger API",
|
||||
swaggerOptions: {
|
||||
persistAuthorization: true,
|
||||
docExpansion: "none",
|
||||
filter: true,
|
||||
tagsSorter: 'alpha',
|
||||
operationsSorter: 'alpha',
|
||||
displayRequestDuration: true,
|
||||
tryItOutEnabled: true,
|
||||
syntaxHighlight: {
|
||||
activate: true,
|
||||
theme: 'monokai'
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
FROM node:20-alpine AS base
|
||||
RUN corepack enable && corepack prepare pnpm@9.12.0 --activate
|
||||
WORKDIR /app
|
||||
|
||||
FROM base AS deps
|
||||
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
|
||||
COPY apps/edr-passenger-web/backoffice/package.json ./apps/edr-passenger-web/backoffice/
|
||||
COPY packages ./packages
|
||||
RUN pnpm install --frozen-lockfile --filter @edr/passenger-backoffice...
|
||||
|
||||
FROM deps AS build
|
||||
COPY apps/edr-passenger-web/backoffice ./apps/edr-passenger-web/backoffice
|
||||
RUN pnpm --filter @edr/passenger-backoffice build
|
||||
|
||||
FROM nginx:1.27-alpine AS runtime
|
||||
COPY --from=build /app/apps/edr-passenger-web/backoffice/dist /usr/share/nginx/html
|
||||
EXPOSE 5184
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
@@ -1,18 +0,0 @@
|
||||
FROM node:20-alpine AS base
|
||||
RUN corepack enable && corepack prepare pnpm@9.12.0 --activate
|
||||
WORKDIR /app
|
||||
|
||||
FROM base AS deps
|
||||
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
|
||||
COPY apps/edr-passenger-web/portal/package.json ./apps/edr-passenger-web/portal/
|
||||
COPY packages ./packages
|
||||
RUN pnpm install --frozen-lockfile --filter @edr/passenger-portal...
|
||||
|
||||
FROM deps AS build
|
||||
COPY apps/edr-passenger-web/portal ./apps/edr-passenger-web/portal
|
||||
RUN pnpm --filter @edr/passenger-portal build
|
||||
|
||||
FROM nginx:1.27-alpine AS runtime
|
||||
COPY --from=build /app/apps/edr-passenger-web/portal/dist /usr/share/nginx/html
|
||||
EXPOSE 5174
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
@@ -7,8 +7,8 @@ export default defineConfig({
|
||||
port: 5184,
|
||||
host: "0.0.0.0",
|
||||
},
|
||||
test: {
|
||||
environment: "jsdom",
|
||||
globals: true,
|
||||
},
|
||||
// test: {
|
||||
// environment: "jsdom",
|
||||
// globals: true,
|
||||
// },
|
||||
});
|
||||
|
||||
@@ -7,8 +7,8 @@ export default defineConfig({
|
||||
port: 5174,
|
||||
host: "0.0.0.0",
|
||||
},
|
||||
test: {
|
||||
environment: "jsdom",
|
||||
globals: true,
|
||||
},
|
||||
// test: {
|
||||
// environment: "jsdom",
|
||||
// globals: true,
|
||||
// },
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user