This commit is contained in:
Stephanos A
2026-05-24 16:31:36 +03:00
23 changed files with 12996 additions and 859 deletions

7
.dockerignore Normal file
View File

@@ -0,0 +1,7 @@
**/node_modules
**/dist
**/.github
**/.vscode
**/.git
**/.env
.env

96
.github/workflows/deploy.yaml vendored Normal file
View File

@@ -0,0 +1,96 @@
name: Automatic Deployment
on:
push:
branches:
- dev
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
environment:
name: 🌍 Setup Environment
runs-on: [self-hosted]
outputs:
target: ${{ steps.dev.outputs.target || steps.staging.outputs.target }}
steps:
- name: Verify NPM Token
env:
# We map the secret here to check its existence
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
if [ -z "$NPM_TOKEN" ]; then
echo "::error::The NPM_TOKEN secret is missing or empty. Please add it to your GitHub Secrets."
exit 1
fi
echo "NPM_TOKEN is present, proceeding with build..."
- name: 🛠️ Set Development Environment
id: dev
if: ${{github.ref_name == 'dev'}}
run: |
echo "target=dev" >> $GITHUB_OUTPUT
- name: 🚀 Set Staging Environment
id: staging
if: ${{github.ref_name == 'staging'}}
run: |
echo "target=staging" >> $GITHUB_OUTPUT
build-base-image:
name: 🏗️ Build Base Image
runs-on: [self-hosted, dev]
needs: [environment]
steps:
- name: 🔍 Checkout
uses: actions/checkout@v4
- name: 🐳 Build Docker Image
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
set -euo pipefail
# Create the multi-line file
cat <<EOF > .npmrc_temp
@tria-plc:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${NPM_TOKEN}
always-auth=true
EOF
# Build using the file
docker build --secret id=npmrc,src=.npmrc_temp -t edr-${{needs.environment.outputs.target}} .
docker build --secret id=npmrc,src=.npmrc_temp --target passenger-migration -t edr-passenger-migration-${{needs.environment.outputs.target}} .
# Shred/Remove the sensitive file
rm .npmrc_temp
deploy-service:
name: ${{ matrix.display_name }}
runs-on: [self-hosted, dev]
needs: [build-base-image, environment]
strategy:
fail-fast: false
matrix:
include:
- service: freight-api
env_file: .env.freight-api
display_name: 🚚 Deploy Freight API Service
- service: passenger-api
env_file: .env.passenger-api
display_name: 🧑‍🦲 Deploy Passenger API Service
steps:
- name: 🔍 Checkout
uses: actions/checkout@v4
- name: 📋 Copy ${{ matrix.service }} Environment
run: cp ~/environment/edr/${{needs.environment.outputs.target}}/${{ matrix.env_file }} .env
- name: 🧪 Run Passenger API migrations
if: ${{ matrix.service == 'passenger-api' }}
run: |
docker run --rm --env-file .env edr-passenger-migration-${{needs.environment.outputs.target}}
- name: 🚀 Start ${{ matrix.service }} Service
run: docker compose --project-name="edr-${{needs.environment.outputs.target}}" up -d --force-recreate ${{ matrix.service }} --build

3
.gitignore vendored
View File

@@ -21,4 +21,5 @@ coverage/
# OS/editor
.DS_Store
.idea/
.vscode/
.vscode/
.npmrc

84
Dockerfile Normal file
View File

@@ -0,0 +1,84 @@
FROM node:24.15.0 AS base
RUN corepack enable && corepack prepare pnpm@latest-11 --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 apps/edr-passenger-api/package.json ./apps/edr-passenger-api/
COPY apps/edr-freight-web/backoffice/package.json ./apps/edr-freight-web/backoffice/
COPY apps/edr-freight-web/portal/package.json ./apps/edr-freight-web/portal/
COPY apps/edr-passenger-web/backoffice/package.json ./apps/edr-passenger-web/backoffice/
COPY apps/edr-passenger-web/portal/package.json ./apps/edr-passenger-web/portal/
COPY packages/api-common/package.json packages/api-common/
COPY packages/config/eslint-config/package.json packages/config/eslint-config/
COPY packages/config/prettier-config/package.json packages/config/prettier-config/
COPY packages/config/tsconfig/package.json packages/config/tsconfig/
COPY packages/types/package.json packages/types/
COPY packages/ui-common/package.json packages/ui-common/
RUN --mount=type=cache,id=pnpm,target=/pnpm/store\
--mount=type=secret,id=npmrc,target=./.npmrc \
pnpm install --frozen-lockfile
FROM deps AS build
COPY . .
RUN pnpm run build
FROM base AS freight-api
# 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
COPY --from=build /app/packages ./../../packages
EXPOSE 3001
CMD ["node", "dist/main.js"]
FROM base AS passenger-api
RUN apt-get update -y && apt-get install -y openssl
# RUN corepack enable && corepack prepare pnpm@9.12.0 --activate
WORKDIR /app/apps/edr-passenger-api
ENV NODE_ENV=production
# Use build-stage node_modules (not deps): `pnpm run build` runs `prisma generate`, which
# writes the real @prisma/client (enums, types). deps never runs generate, so @IsEnum(ServiceClass)
# and similar would see undefined at runtime if we copied deps only.
COPY --from=build /app/node_modules ./../../node_modules
COPY --from=build /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/packages ./../../packages
EXPOSE 3001
CMD ["node", "dist/main.js"]
FROM build as passenger-migration
WORKDIR /app/apps/edr-passenger-api
CMD pnpm run prisma:migrate && pnpm run prisma:seed
FROM nginx:1.27-alpine AS freight-web-portal
COPY --from=build /app/apps/edr-freight-web/portal/dist /usr/share/nginx/html
EXPOSE 5173
CMD ["nginx", "-g", "daemon off;"]
FROM nginx:1.27-alpine AS freight-web-backoffice
COPY --from=build /app/apps/edr-freight-web/backoffice/dist /usr/share/nginx/html
EXPOSE 5173
CMD ["nginx", "-g", "daemon off;"]

View File

@@ -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"]

View File

@@ -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;"]

View File

@@ -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;"]

View File

@@ -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"]

View File

@@ -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

View File

@@ -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",

View File

@@ -1,19 +1,19 @@
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";
import { SessionActivityInterceptor } from "./common/interceptors/session-activity.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,7 +25,7 @@ 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
@@ -205,78 +205,39 @@ Payment providers send notifications to:
- **Status Page:** https://status.edr-platform.com
`,
)
.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'
}
},
});

View File

@@ -1,4 +1,4 @@
import { IsString, IsEnum, IsOptional } from 'class-validator';
import { IsString, IsEnum, IsOptional, IsIn } from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { PaymentIntentStatus } from '@prisma/client';
@@ -11,14 +11,20 @@ export enum PaymentMethodTypeEnum {
WALLET = 'WALLET' // Internal
}
export type PaymentPlatformDto = 'web' | 'mobile';
export class InitiatePaymentDto {
@ApiProperty({ example: 'booking-uuid' }) @IsString() bookingId: string;
@ApiProperty({
@ApiProperty({
enum: PaymentMethodTypeEnum,
description: 'Payment method: TELEBIRR/CBE_BIRR/EBIRR (Ethiopia), WAAFI (Djibouti), CARD (International), WALLET (Internal)',
example: 'TELEBIRR'
}) @IsEnum(PaymentMethodTypeEnum) method: PaymentMethodTypeEnum;
@ApiPropertyOptional({ description: 'Saved payment method ID (optional)' }) @IsOptional() @IsString() paymentMethodId?: string;
@ApiPropertyOptional({ enum: ['web', 'mobile'], default: 'web', description: 'Payment platform (web or mobile)' })
@IsOptional()
@IsIn(['web', 'mobile'])
platform?: PaymentPlatformDto;
}
export class RefundDto {
@@ -34,8 +40,11 @@ export class AddPaymentMethodDto {
}
export class ClientActionDto {
@ApiProperty({ enum: ['REDIRECT'] }) type: 'REDIRECT';
@ApiProperty() url: string;
@ApiProperty({ enum: ['REDIRECT', 'LAUNCH_APP'] }) type: 'REDIRECT' | 'LAUNCH_APP';
@ApiPropertyOptional({ description: 'Set when type=REDIRECT (web flow)' }) url?: string;
@ApiPropertyOptional({ description: 'Set when type=LAUNCH_APP (mobile flow)' }) prepayId?: string;
@ApiPropertyOptional({ description: 'Set when type=LAUNCH_APP (mobile flow)' }) receiveCode?: string;
@ApiPropertyOptional({ description: 'Set when type=LAUNCH_APP (mobile flow)' }) shortCode?: string;
}
export class InitiateResponseDto {

View File

@@ -5,7 +5,7 @@ import { TicketsService } from '../tickets/tickets.service';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { Prisma, PaymentIntentStatus, PaymentMethodType } from '@prisma/client';
import { InitiatePaymentDto, RefundDto, AddPaymentMethodDto, InitiateResponseDto, IntentStatusDto } from './payments.dto';
import { PaymentProvider, ProviderStatus } from './payments.types';
import { ClientAction, PaymentProvider, ProviderStatus } from './payments.types';
import { TelebirrProvider } from './providers/telebirr.provider';
import { CbeBirrProvider } from './providers/cbe-birr.provider';
import { EBirrProvider } from './providers/ebirr.provider';
@@ -69,7 +69,7 @@ export class PaymentsService {
const provider = this.providers.get(method);
if (provider) {
return this.initiateProviderPayment(booking, provider);
return this.initiateProviderPayment(booking, provider, dto.platform);
}
throw new BadRequestException(`Unsupported payment method: ${method}`);
@@ -142,6 +142,7 @@ export class PaymentsService {
private async initiateProviderPayment(
booking: Prisma.BookingGetPayload<{ include: { seats: true } }>,
provider: PaymentProvider,
platform: 'web' | 'mobile' | undefined,
): Promise<InitiateResponseDto> {
const merchantOrderId = createMerchantOrderId();
const result = await provider.initiate({
@@ -149,6 +150,7 @@ export class PaymentsService {
bookingRef: booking.bookingRef,
amountMinor: booking.totalMinor,
currency: booking.currency,
platform,
});
const intent = await this.prisma.paymentIntent.upsert({
@@ -187,7 +189,7 @@ export class PaymentsService {
): InitiateResponseDto {
const clientAction =
intent.clientAction && typeof intent.clientAction === 'object'
? (intent.clientAction as unknown as { type: 'REDIRECT'; url: string })
? (intent.clientAction as unknown as ClientAction)
: undefined;
return {
intentId: intent.id,

View File

@@ -1,15 +1,17 @@
import { PaymentIntentStatus, PaymentMethodType } from '@prisma/client';
export interface ClientAction {
type: 'REDIRECT' | 'NONE';
url?: string;
}
export type PaymentPlatform = 'web' | 'mobile';
export type ClientAction =
| { type: 'REDIRECT'; url: string }
| { type: 'LAUNCH_APP'; prepayId: string; receiveCode?: string; shortCode: string };
export interface ProviderInitiationInput {
merchantOrderId: string;
bookingRef: string;
amountMinor: number;
currency: string;
platform?: PaymentPlatform;
}
export interface ProviderInitiationResult {

View File

@@ -58,12 +58,21 @@ export class TelebirrProvider implements PaymentProvider {
);
}
const checkoutUrl = this.buildCheckoutUrl(prepayId);
const expiresAt = this.computeExpiresAt(requestBody.biz_content.timeout_express);
const platform = input.platform ?? 'web';
const clientAction =
platform === 'mobile'
? {
type: 'LAUNCH_APP' as const,
prepayId,
receiveCode: response.biz_content?.receiveCode,
shortCode: this.merchantCode,
}
: { type: 'REDIRECT' as const, url: this.buildCheckoutUrl(prepayId) };
return {
providerOrderId: prepayId,
clientAction: { type: 'REDIRECT', url: checkoutUrl },
clientAction,
expiresAt,
rawInitiation: {
request: this.sanitize(requestBody),

View File

@@ -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;"]

View File

@@ -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;"]

View File

@@ -7,8 +7,8 @@ export default defineConfig({
port: 5184,
host: "0.0.0.0",
},
test: {
environment: "jsdom",
globals: true,
},
// test: {
// environment: "jsdom",
// globals: true,
// },
});

View File

@@ -7,8 +7,8 @@ export default defineConfig({
port: 5174,
host: "0.0.0.0",
},
test: {
environment: "jsdom",
globals: true,
},
// test: {
// environment: "jsdom",
// globals: true,
// },
});

21
docker-compose.yaml Normal file
View File

@@ -0,0 +1,21 @@
services:
freight-api:
build:
context: .
dockerfile: ./Dockerfile
target: freight-api
env_file:
- .env
ports:
- ${PORT}:${PORT}
restart: unless-stopped
passenger-api:
build:
context: .
dockerfile: ./Dockerfile
target: passenger-api
env_file:
- .env
ports:
- ${PORT}:${PORT}
restart: unless-stopped

View File

@@ -42,7 +42,7 @@
"prettier --write"
]
},
"packageManager": "pnpm@9.12.0",
"packageManager": "pnpm@11.1.1+sha512.d1fdf5f73c617b64fa1a56a81c3c8dfe0e966e33a6010aa256b517ae77be21d93e05affc0de1a83b0e4f29d569f68b446ae8f068cd7247c0bb3df0fb4d7bdf9a",
"engines": {
"node": ">=20"
}

13317
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,21 @@
packages:
- "apps/edr-freight-api"
- "apps/edr-freight-web/*"
- "apps/edr-passenger-api"
- "apps/edr-passenger-web/*"
- "packages/*"
- "packages/config/*"
allowBuilds:
'@nestjs/core': true
'@prisma/client': true
'@prisma/engines': true
'@scarf/scarf': true
argon2: true
bcrypt: true
core-js: true
es5-ext: true
esbuild: true
highlight.js: true
prisma: true
tesseract.js: true
uglifyjs-webpack-plugin: true