From a69031ccc118f3f688c6a802d4b7be1a97815608 Mon Sep 17 00:00:00 2001 From: SennayT Date: Mon, 25 May 2026 09:47:45 +0300 Subject: [PATCH 01/18] add grouped argument to docker build --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7693a2939..b35bd68ed 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,10 +28,10 @@ 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 +FROM deps AS build COPY . . -RUN pnpm run build --filter=\!"@edr/passenger-portal" +RUN pnpm run build --log-order grouped FROM base AS freight-api # RUN corepack enable && corepack prepare pnpm@9.12.0 --activate From 671d24b81d490b73d5c760b7ed399348a5fbdb3d Mon Sep 17 00:00:00 2001 From: SennayT Date: Mon, 25 May 2026 09:52:52 +0300 Subject: [PATCH 02/18] fix: fix typerror on stations object --- .../portal/src/services/stations.service.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/edr-passenger-web/portal/src/services/stations.service.ts b/apps/edr-passenger-web/portal/src/services/stations.service.ts index 1c1b4f4d2..20010954a 100644 --- a/apps/edr-passenger-web/portal/src/services/stations.service.ts +++ b/apps/edr-passenger-web/portal/src/services/stations.service.ts @@ -1,12 +1,13 @@ -import type { IStation } from "../types"; +// import type { IStation } from "../types"; import { api } from "../utils/api"; +import type { Passenger } from "@edr/types"; export const stationsService = { - list: async (): Promise => { + list: async (): Promise => { const { data } = await api.get("/stations"); return data.data; }, - get: async (id: string): Promise => { + get: async (id: string): Promise => { const { data } = await api.get(`/stations/${id}`); return data.data; }, From c838e77349398e1a4a8a2d61b8cf4999ac4c238a Mon Sep 17 00:00:00 2001 From: "Stephanos A." Date: Thu, 28 May 2026 10:13:11 +0300 Subject: [PATCH 03/18] fix: add missing properties to IStation interface - Add 'country' property to match shared types package - Add 'createdAt' property to match shared types package - Add 'updatedAt' property to match shared types package This fixes the TypeScript type mismatch error in StationsPage.tsx where the local IStation type was missing properties required by the shared @edr/types/passenger package during the Docker build. Fixes: https://github.com/Tria-plc/edr-platform/actions/runs/26325256104/job/77502650459 --- .../portal/src/types/index.ts | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/apps/edr-passenger-web/portal/src/types/index.ts b/apps/edr-passenger-web/portal/src/types/index.ts index 09b7c9fc2..b206daa6a 100644 --- a/apps/edr-passenger-web/portal/src/types/index.ts +++ b/apps/edr-passenger-web/portal/src/types/index.ts @@ -1,22 +1,25 @@ -// ── Enums ────────────────────────────────────────────────────────────────────── +// ── Enums ──────────────────────────────────────────────────────────────── export type TripStatus = 'SCHEDULED' | 'BOARDING' | 'EN_ROUTE' | 'ARRIVED' | 'CANCELLED' | 'DELAYED'; export type SeatStatus = 'AVAILABLE' | 'HELD' | 'BOOKED' | 'BLOCKED'; export type ServiceClass = 'ECONOMY' | 'BUSINESS' | 'FIRST'; export type BookingStatus = 'DRAFT' | 'PENDING_PAYMENT' | 'CONFIRMED' | 'CANCELLED' | 'COMPLETED' | 'NO_SHOW'; export type PaymentMethod = 'TELEBIRR' | 'CBE_BIRR' | 'EBIRR' | 'CARD' | 'WALLET'; -// ── Station ──────────────────────────────────────────────────────────────────── +// ── Station ──────────────────────────────────────────────────────────────── export interface IStation { id: string; code: string; name: string; city: string; + country: string; timezone: string; lat: number; lng: number; + createdAt: string | Date; + updatedAt: string | Date; } -// ── Trip / Schedule ──────────────────────────────────────────────────────────── +// ── Trip / Schedule ──────────────────────────────────────────────────────── export interface ITripStation { id: string; code: string; @@ -62,7 +65,7 @@ export interface IStopTime { station: IStation; } -// ── Seat ─────────────────────────────────────────────────────────────────────── +// ── Seat ──────────────────────────────────────────────────────────────────── export interface ISeat { id: string; number: string; // label from API @@ -81,7 +84,7 @@ export interface ISeatMap { coaches: ICoach[]; } -// ── Segment-based seats ──────────────────────────────────────────────────────── +// ── Segment-based seats ──────────────────────────────────────────────────── export interface ISegment { fromStationId: string; toStationId: string; @@ -119,7 +122,7 @@ export interface ISegmentConfirmResult { segments: Array<{ fromStationId: string; toStationId: string; fromSequence: number; toSequence: number }>; } -// ── Booking ──────────────────────────────────────────────────────────────────── +// ── Booking ──────────────────────────────────────────────────────────────── export interface IBooking { id: string; bookingRef: string; @@ -140,7 +143,7 @@ export interface IBooking { payment?: { method: PaymentMethod; status: string }; } -// ── Ticket ───────────────────────────────────────────────────────────────────── +// ── Ticket ───────────────────────────────────────────────────────────────── export interface ITicket { id: string; bookingId: string; @@ -158,7 +161,7 @@ export interface ITicket { qrPayload: string; } -// ── Fare quote ───────────────────────────────────────────────────────────────── +// ── Fare quote ───────────────────────────────────────────────────────────── export interface IFareQuote { tripId: string; serviceClass: ServiceClass; @@ -171,7 +174,7 @@ export interface IFareQuote { currency: string; } -// ── Passenger ───────────────────────────────────────────────────────────────── +// ── Passenger ────────────────────────────────────────────────────────────── export interface IPassengerProfile { id: string; fullName: string; @@ -181,7 +184,7 @@ export interface IPassengerProfile { bookings: IBooking[]; } -// ── Loyalty ──────────────────────────────────────────────────────────────────── +// ── Loyalty ──────────────────────────────────────────────────────────────── export interface ILoyaltyAccount { id: string; passengerId: string; @@ -192,7 +195,7 @@ export interface ILoyaltyAccount { tierProgressPercent: number; } -// ── Wallet ───────────────────────────────────────────────────────────────────── +// ── Wallet ───────────────────────────────────────────────────────────────── export interface IWallet { id: string; passengerId: string; @@ -200,7 +203,7 @@ export interface IWallet { currency: string; } -// ── Notification ─────────────────────────────────────────────────────────────── +// ── Notification ─────────────────────────────────────────────────────────── export interface INotification { id: string; passengerId: string; @@ -212,7 +215,7 @@ export interface INotification { createdAt: string; } -// ── Promotion ────────────────────────────────────────────────────────────────── +// ── Promotion ────────────────────────────────────────────────────────────── export interface IPromotion { id: string; title: string; @@ -225,7 +228,7 @@ export interface IPromotion { active: boolean; } -// ── Live tracking ────────────────────────────────────────────────────────────── +// ── Live tracking ────────────────────────────────────────────────────────── export interface ILiveStatus { tripId: string; trainName: string; @@ -241,7 +244,7 @@ export interface ILiveStatus { updatedAt: string; } -// ── Dashboard ────────────────────────────────────────────────────────────────── +// ── Dashboard ────────────────────────────────────────────────────────────── export interface IDashboard { user: { firstName: string; greetingKey: 'MORNING' | 'AFTERNOON' | 'EVENING' }; upcomingTicket: { From 342ccb63de92a00d055ede17892af11485e1f057 Mon Sep 17 00:00:00 2001 From: SennayT Date: Thu, 28 May 2026 12:52:56 +0300 Subject: [PATCH 04/18] modify pipeline to speed up deployment --- .dockerignore | 11 +- .github/workflows/deploy-freight.yml | 71 ++++++++++ .github/workflows/deploy-passenger.yml | 71 ++++++++++ .github/workflows/deploy.yaml | 96 ------------- Dockerfile | 84 ----------- README.md | 120 +++++++++++----- apps/edr-freight-api/.env.example | 14 +- apps/edr-freight-api/Dockerfile | 37 +++++ apps/edr-passenger-api/.env.example | 98 +------------ apps/edr-passenger-api/Dockerfile | 50 +++++++ apps/edr-passenger-api/docker-entrypoint.sh | 11 ++ apps/edr-passenger-api/package.json | 7 +- apps/edr-passenger-api/prisma/seed.ts | 8 +- docker-compose.yaml | 87 ++++++++++-- infrastructure/docker/Dockerfile.web | 37 +++++ infrastructure/docker/docker-compose.dev.yml | 126 ---------------- infrastructure/docker/docker-compose.prod.yml | 134 ------------------ infrastructure/nginx/nginx.conf | 63 -------- infrastructure/nginx/spa.conf | 13 ++ package.json | 2 + pnpm-lock.yaml | 6 +- scripts/deploy/create-npmrc.sh | 19 +++ scripts/deploy/sync-env-from-server.sh | 68 +++++++++ 23 files changed, 561 insertions(+), 672 deletions(-) create mode 100644 .github/workflows/deploy-freight.yml create mode 100644 .github/workflows/deploy-passenger.yml delete mode 100644 .github/workflows/deploy.yaml delete mode 100644 Dockerfile create mode 100644 apps/edr-freight-api/Dockerfile create mode 100644 apps/edr-passenger-api/Dockerfile create mode 100644 apps/edr-passenger-api/docker-entrypoint.sh create mode 100644 infrastructure/docker/Dockerfile.web delete mode 100644 infrastructure/docker/docker-compose.dev.yml delete mode 100644 infrastructure/docker/docker-compose.prod.yml delete mode 100644 infrastructure/nginx/nginx.conf create mode 100644 infrastructure/nginx/spa.conf create mode 100644 scripts/deploy/create-npmrc.sh create mode 100644 scripts/deploy/sync-env-from-server.sh diff --git a/.dockerignore b/.dockerignore index 122332251..242c1b08a 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,7 +1,14 @@ **/node_modules **/dist +**/.turbo +**/.git **/.github **/.vscode -**/.git +**/.idea **/.env -.env +**/.env.* +!**/.env.example +**/coverage +**/*.tsbuildinfo +**/*.log +.DS_Store diff --git a/.github/workflows/deploy-freight.yml b/.github/workflows/deploy-freight.yml new file mode 100644 index 000000000..a09faf599 --- /dev/null +++ b/.github/workflows/deploy-freight.yml @@ -0,0 +1,71 @@ +name: Deploy Freight + +on: + push: + branches: + - main + - develop + - staging + paths: + - "apps/edr-freight-api/**" + - "apps/edr-freight-web/**" + - "packages/**" + - "infrastructure/docker/Dockerfile.web" + - "infrastructure/nginx/**" + - "docker-compose.yaml" + - "pnpm-lock.yaml" + - "scripts/deploy/**" + - ".github/workflows/deploy-freight.yml" + workflow_dispatch: + +concurrency: + group: deploy-freight-${{ github.ref_name }} + cancel-in-progress: true + +jobs: + deploy: + name: Build and deploy freight stack + runs-on: self-hosted + env: + PROJECT: edr-freight + BRANCH: ${{ github.ref_name }} + DEPLOY_USER: user + BUILD_ENV_FILE: freight-web.build.env + DOCKER_BUILDKIT: "1" + COMPOSE_DOCKER_CLI_BUILD: "1" + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Sync environment from server + run: | + chmod +x scripts/deploy/*.sh + ./scripts/deploy/sync-env-from-server.sh \ + freight-api \ + freight-portal \ + freight-backoffice + + - name: Configure npm auth for Docker builds + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + run: ./scripts/deploy/create-npmrc.sh + + - name: Build images + run: | + set -euo pipefail + docker compose build \ + freight-api \ + freight-portal \ + freight-backoffice + + - name: Deploy containers + run: | + set -euo pipefail + docker compose up -d \ + freight-api \ + freight-portal \ + freight-backoffice + + - name: Remove npm credentials from workspace + if: always() + run: rm -f .npmrc .npmrc_temp diff --git a/.github/workflows/deploy-passenger.yml b/.github/workflows/deploy-passenger.yml new file mode 100644 index 000000000..ae4864fe8 --- /dev/null +++ b/.github/workflows/deploy-passenger.yml @@ -0,0 +1,71 @@ +name: Deploy Passenger + +on: + push: + branches: + - main + - develop + - staging + paths: + - "apps/edr-passenger-api/**" + - "apps/edr-passenger-web/**" + - "packages/**" + - "infrastructure/docker/Dockerfile.web" + - "infrastructure/nginx/**" + - "docker-compose.yaml" + - "pnpm-lock.yaml" + - "scripts/deploy/**" + - ".github/workflows/deploy-passenger.yml" + workflow_dispatch: + +concurrency: + group: deploy-passenger-${{ github.ref_name }} + cancel-in-progress: true + +jobs: + deploy: + name: Build and deploy passenger stack + runs-on: self-hosted + env: + PROJECT: edr-passenger + BRANCH: ${{ github.ref_name }} + DEPLOY_USER: user + BUILD_ENV_FILE: passenger-web.build.env + DOCKER_BUILDKIT: "1" + COMPOSE_DOCKER_CLI_BUILD: "1" + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Sync environment from server + run: | + chmod +x scripts/deploy/*.sh + ./scripts/deploy/sync-env-from-server.sh \ + passenger-api \ + passenger-portal \ + passenger-backoffice + + - name: Configure npm auth for Docker builds + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + run: ./scripts/deploy/create-npmrc.sh + + - name: Build images + run: | + set -euo pipefail + docker compose build \ + passenger-api \ + passenger-portal \ + passenger-backoffice + + - name: Deploy containers + run: | + set -euo pipefail + docker compose up -d \ + passenger-api \ + passenger-portal \ + passenger-backoffice + + - name: Remove npm credentials from workspace + if: always() + run: rm -f .npmrc .npmrc_temp diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml deleted file mode 100644 index fa4bef238..000000000 --- a/.github/workflows/deploy.yaml +++ /dev/null @@ -1,96 +0,0 @@ -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 < .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 diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index b35bd68ed..000000000 --- a/Dockerfile +++ /dev/null @@ -1,84 +0,0 @@ -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 --log-order grouped - -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;"] diff --git a/README.md b/README.md index 63e3c6b8b..5adb57773 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,7 @@ pnpm --filter @edr/passenger-api run prisma:generate #### Run Migrations ```bash -pnpm --filter @edr/passenger-api run prisma:migrate +pnpm --filter @edr/passenger-api run prisma:migrate:dev ``` #### Seed Database @@ -523,59 +523,103 @@ 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 # Run migrations +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 -### Build Image +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 build -f apps/edr-passenger-api/Dockerfile -t edr-passenger-api . +DOCKER_BUILDKIT=1 pnpm docker:build +pnpm docker:up ``` -### Run Container +Or without pnpm scripts: + ```bash -docker run -d \ - --name edr-api \ - -p 4000:4000 \ - --env-file apps/edr-passenger-api/.env \ - edr-passenger-api +DOCKER_BUILDKIT=1 docker compose build +docker compose up -d ``` -### Docker Compose (Recommended) -```yaml -version: '3.8' -services: - postgres: - image: postgres:15 - environment: - POSTGRES_USER: edr - POSTGRES_PASSWORD: edr_secret - POSTGRES_DB: edr_passenger - ports: - - "5432:5432" - volumes: - - postgres_data:/var/lib/postgresql/data +| 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 | - api: - build: - context: . - dockerfile: apps/edr-passenger-api/Dockerfile - ports: - - "4000:4000" - environment: - DATABASE_URL: postgresql://edr:edr_secret@postgres:5432/edr_passenger - JWT_SECRET: your-secret-key - PORT: 4000 - depends_on: - - postgres +### Build a single service -volumes: - postgres_data: +```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. diff --git a/apps/edr-freight-api/.env.example b/apps/edr-freight-api/.env.example index 6c7738a7a..a3ddffc40 100644 --- a/apps/edr-freight-api/.env.example +++ b/apps/edr-freight-api/.env.example @@ -1,17 +1,7 @@ -# App -NODE_ENV=development +# Copy to .env for local/docker compose (not committed). PORT=3001 - -# Database DB_HOST=localhost DB_PORT=5433 -DB_NAME=edr_freight DB_USER=postgres DB_PASSWORD= - -# JWT (provided by external auth package — placeholder only) -JWT_SECRET= - -# Redis -REDIS_HOST=localhost -REDIS_PORT=6379 +DB_NAME=edr_freight diff --git a/apps/edr-freight-api/Dockerfile b/apps/edr-freight-api/Dockerfile new file mode 100644 index 000000000..bd25c8050 --- /dev/null +++ b/apps/edr-freight-api/Dockerfile @@ -0,0 +1,37 @@ +# syntax=docker/dockerfile:1 +# Build from monorepo root: docker build -f apps/edr-freight-api/Dockerfile . + +FROM node:22-alpine AS base +RUN apk add --no-cache libc6-compat +RUN corepack enable +WORKDIR /app + +FROM base AS pruner +COPY . . +RUN pnpm dlx turbo prune "@edr/freight-api" --docker + +FROM base AS installer +COPY --from=pruner /app/out/json/ . +COPY --from=pruner /app/out/pnpm-lock.yaml ./pnpm-lock.yaml +RUN --mount=type=secret,id=npmrc,target=./.npmrc,required=false \ + pnpm install --frozen-lockfile + +FROM base AS builder +COPY --from=installer /app/ . +COPY --from=pruner /app/out/full/ . +RUN pnpm turbo build --filter="@edr/freight-api..." + +FROM base AS deployer +COPY --from=builder /app/ . +RUN pnpm deploy --filter="@edr/freight-api" --prod --legacy /deploy + +FROM node:22-alpine AS runner +RUN apk add --no-cache libc6-compat +ENV NODE_ENV=production +WORKDIR /app +RUN addgroup --system --gid 1001 nodejs \ + && adduser --system --uid 1001 --ingroup nodejs nestjs +COPY --from=deployer --chown=nestjs:nodejs /deploy . +USER nestjs +EXPOSE 3001 +CMD ["node", "dist/main.js"] diff --git a/apps/edr-passenger-api/.env.example b/apps/edr-passenger-api/.env.example index 75d2df681..adaaee399 100644 --- a/apps/edr-passenger-api/.env.example +++ b/apps/edr-passenger-api/.env.example @@ -1,96 +1,4 @@ -# App -NODE_ENV=development +# Copy to .env for local/docker compose (not committed). PORT=4000 - -# Database (Prisma) -DATABASE_URL=postgresql://edr:edr_secret@localhost:5432/edr_passenger?schema=edr_passenger - -# CORS -FRONTEND_URL=http://localhost:3000 -PORTAL_URL=http://localhost:3001 - -# JWT -JWT_SECRET=edr-platform-secret-change-in-production -JWT_EXPIRES_IN=7d - -# SendGrid -SENDGRID_API_KEY= -SENDGRID_FROM_EMAIL=noreply@edr-platform.com - -# SMS Configuration -SMS_PROVIDER=twilio -SMS_API_KEY= - -# Twilio (if SMS_PROVIDER=twilio) -TWILIO_ACCOUNT_SID= -TWILIO_AUTH_TOKEN= -TWILIO_FROM_NUMBER= - -# Africa's Talking (if SMS_PROVIDER=africastalking) -AFRICASTALKING_USERNAME= -AFRICASTALKING_FROM= - -# Telebirr -TELEBIRR_BASE_URL= -TELEBIRR_WEB_BASE_URL= -TELEBIRR_FABRIC_APP_ID= -TELEBIRR_APP_SECRET= -TELEBIRR_MERCHANT_APP_ID= -TELEBIRR_MERCHANT_CODE= -TELEBIRR_NOTIFY_URL= -TELEBIRR_RETURN_URL= -TELEBIRR_TIMEOUT_EXPRESS=15m -TELEBIRR_PRIVATE_KEY= -TELEBIRR_PUBLIC_KEY= -TELEBIRR_INSECURE_TLS=false - -# CBE Birr -CBE_BASE_URL= -CBE_MERCHANT_ID= -CBE_SECRET_KEY= -CBE_NOTIFY_URL= -CBE_RETURN_URL= - -# eBirr -EBIRR_BASE_URL= -EBIRR_MERCHANT_CODE= -EBIRR_SECRET_KEY= -EBIRR_NOTIFY_URL= -EBIRR_RETURN_URL= - -# Card Gateway (Stripe-like) -CARD_BASE_URL= -CARD_API_KEY= -CARD_WEBHOOK_SECRET= -CARD_WEBHOOK_URL= -CARD_RETURN_URL= - -# Waafi (Djibouti Mobile Money) -WAAFI_BASE_URL=https://api.waafipay.net -WAAFI_MERCHANT_UID= -WAAFI_API_USER_ID= -WAAFI_API_KEY= -WAAFI_NOTIFY_URL= -WAAFI_RETURN_URL= - -# Payment Configuration -PAYMENT_PROVIDERS_ENABLED=TELEBIRR,CBE_BIRR,EBIRR,CARD,WALLET,WAAFI - -# Session Configuration -SESSION_INACTIVITY_MINUTES=30 - -# i18n Configuration -DEFAULT_LOCALE=en -SUPPORTED_LOCALES=en,am,fr,om - -# Corporate IAM Configuration (for back-office authentication) -IAM_ENABLED=false -IAM_API_URL=https://iam.tria-plc.com/api -IAM_API_KEY= - -# Verifayda 2.0 Configuration (Ethiopian National ID Verification) -VERIFAYDA_ENABLED=false -VERIFAYDA_API_URL=https://api.verifayda.gov.et/v2 -VERIFAYDA_API_KEY= - -GITHUB_PACKAGE_TOKEN= \ No newline at end of file +DATABASE_URL=postgresql://user:password@host:5432/edr_passenger +JWT_SECRET=change-me-in-production diff --git a/apps/edr-passenger-api/Dockerfile b/apps/edr-passenger-api/Dockerfile new file mode 100644 index 000000000..425c5a95a --- /dev/null +++ b/apps/edr-passenger-api/Dockerfile @@ -0,0 +1,50 @@ +# syntax=docker/dockerfile:1 +# Build from monorepo root: docker build -f apps/edr-passenger-api/Dockerfile . +# On start: runs prisma migrate deploy + seed, then the API. + +FROM node:22-alpine AS base +RUN apk add --no-cache libc6-compat +RUN corepack enable +WORKDIR /app + +FROM base AS pruner +COPY . . +RUN pnpm dlx turbo prune "@edr/passenger-api" --docker + +FROM base AS installer +COPY --from=pruner /app/out/json/ . +COPY --from=pruner /app/out/pnpm-lock.yaml ./pnpm-lock.yaml +RUN --mount=type=secret,id=npmrc,target=./.npmrc,required=false \ + pnpm install --frozen-lockfile + +FROM base AS builder +COPY --from=installer /app/ . +COPY --from=pruner /app/out/full/ . +RUN pnpm --filter "@edr/passenger-api" exec prisma generate +RUN pnpm turbo build --filter="@edr/passenger-api..." + +FROM base AS deployer +COPY --from=builder /app/ . +RUN pnpm deploy --filter="@edr/passenger-api" --legacy /deploy +RUN if [ -d node_modules/.prisma ]; then \ + mkdir -p /deploy/node_modules && \ + cp -r node_modules/.prisma /deploy/node_modules/.prisma; \ + fi + +FROM node:22-alpine AS runner +RUN apk add --no-cache libc6-compat +RUN corepack enable && corepack prepare pnpm@11.1.1 --activate +ENV NODE_ENV=production +WORKDIR /app +RUN addgroup --system --gid 1001 nodejs \ + && adduser --system --uid 1001 --ingroup nodejs nestjs +COPY --from=deployer /deploy . +COPY apps/edr-passenger-api/docker-entrypoint.sh /docker-entrypoint.sh +RUN chmod +x /docker-entrypoint.sh \ + && chown -R nestjs:nodejs /app +USER nestjs +ENV CI=true +ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 +EXPOSE 4000 +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["node", "dist/main.js"] diff --git a/apps/edr-passenger-api/docker-entrypoint.sh b/apps/edr-passenger-api/docker-entrypoint.sh new file mode 100644 index 000000000..087ff1e74 --- /dev/null +++ b/apps/edr-passenger-api/docker-entrypoint.sh @@ -0,0 +1,11 @@ +#!/bin/sh +set -e + +cd /app + +# npm run executes the same package.json scripts as pnpm run (pnpm reinstalls in deploy layout) +npm run prisma:generate +npm run prisma:migrate +npm run prisma:seed + +exec "$@" diff --git a/apps/edr-passenger-api/package.json b/apps/edr-passenger-api/package.json index ae10747f5..0331adb9b 100644 --- a/apps/edr-passenger-api/package.json +++ b/apps/edr-passenger-api/package.json @@ -12,7 +12,8 @@ "test:e2e": "jest --config ./test/jest-e2e.json", "type-check": "tsc --noEmit", "prisma:generate": "prisma generate", - "prisma:migrate": "prisma migrate dev", + "prisma:migrate": "prisma migrate deploy", + "prisma:migrate:dev": "prisma migrate dev", "prisma:seed": "ts-node prisma/seed.ts", "prisma:backfill": "ts-node prisma/backfill-fields.ts", "prisma:verify": "ts-node prisma/verify-backfill.ts" @@ -42,7 +43,8 @@ "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", "swagger-ui-express": "^5.0.0", - "tsconfig-paths": "^4.2.0" + "tsconfig-paths": "^4.2.0", + "@prisma/client": "^6.19.3" }, "devDependencies": { "@edr/eslint-config": "workspace:*", @@ -50,7 +52,6 @@ "@nestjs/cli": "^11.0.21", "@nestjs/schematics": "^11.1.0", "@nestjs/testing": "^11.1.19", - "@prisma/client": "^6.19.3", "@types/bcrypt": "^5.0.2", "@types/jest": "^29.5.11", "@types/node": "^20.10.6", diff --git a/apps/edr-passenger-api/prisma/seed.ts b/apps/edr-passenger-api/prisma/seed.ts index 12b872a0e..618fa4518 100644 --- a/apps/edr-passenger-api/prisma/seed.ts +++ b/apps/edr-passenger-api/prisma/seed.ts @@ -1,4 +1,4 @@ -import { PrismaClient, SeatKind } from '@prisma/client'; +import { PrismaClient } from '@prisma/client'; import * as bcrypt from 'bcrypt'; const prisma = new PrismaClient(); @@ -136,11 +136,11 @@ async function seedCoachesAndSeats(seatClasses: any[]) { col, label: `${row}${col}`, seatNumber: `${config.label}${row}${col}`, - kind: (row === 1 && col === 'A' ? 'ACCESSIBLE' : 'STANDARD') as SeatKind, + kind: row === 1 && col === 'A' ? 'ACCESSIBLE' : 'STANDARD', }); } } - await prisma.seat.createMany({ data: seats }); + await prisma.seat.createMany({ data: seats as any }); } } @@ -161,7 +161,7 @@ async function seedSchedules(trains: any[], stations: any[]) { const existingScheduleIds = (await prisma.trainSchedule.findMany({ where: { trainId: { in: [train301.id, train302.id, train303.id] } }, select: { id: true }, - })).map((s) => s.id); + })).map((s: { id: string }) => s.id); if (existingScheduleIds.length > 0) { await prisma.fareRule.deleteMany({ where: { tripId: { in: existingScheduleIds } } }); diff --git a/docker-compose.yaml b/docker-compose.yaml index aa9ec58e2..f6de0d965 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,21 +1,84 @@ +# EDR Platform — application containers only (no Postgres). +# Requires a local .npmrc with GitHub Packages auth for @tria-plc (freight API + freight web). +# Copy apps/*/env.example to .env and set real values before `docker compose up`. +# +# Build: DOCKER_BUILDKIT=1 docker compose build +# Run: docker compose up -d + services: freight-api: build: context: . - dockerfile: ./Dockerfile - target: freight-api - env_file: - - .env + dockerfile: apps/edr-freight-api/Dockerfile + secrets: + - npmrc ports: - - ${PORT}:${PORT} - restart: unless-stopped + - "3001:3001" + environment: + PORT: "3001" + env_file: + - apps/edr-freight-api/.env + passenger-api: build: context: . - dockerfile: ./Dockerfile - target: passenger-api - env_file: - - .env + dockerfile: apps/edr-passenger-api/Dockerfile ports: - - ${PORT}:${PORT} - restart: unless-stopped + - "4000:4000" + environment: + PORT: "4000" + env_file: + - apps/edr-passenger-api/.env + + freight-portal: + build: + context: . + dockerfile: infrastructure/docker/Dockerfile.web + args: + TURBO_FILTER: "@edr/freight-portal" + APP_PATH: apps/edr-freight-web/portal + # Browser-reachable URL; override for production deployments + VITE_API_URL: ${FREIGHT_VITE_API_URL:-http://localhost:3001/api} + secrets: + - npmrc + ports: + - "5173:80" + + freight-backoffice: + build: + context: . + dockerfile: infrastructure/docker/Dockerfile.web + args: + TURBO_FILTER: "@edr/freight-backoffice" + APP_PATH: apps/edr-freight-web/backoffice + VITE_API_URL: ${FREIGHT_VITE_API_URL:-http://localhost:3001/api} + secrets: + - npmrc + ports: + - "5183:80" + + passenger-portal: + build: + context: . + dockerfile: infrastructure/docker/Dockerfile.web + args: + TURBO_FILTER: "@edr/passenger-portal" + APP_PATH: apps/edr-passenger-web/portal + VITE_API_URL: ${PASSENGER_VITE_API_URL:-http://localhost:4000} + ports: + - "5174:80" + + passenger-backoffice: + build: + context: . + dockerfile: infrastructure/docker/Dockerfile.web + args: + TURBO_FILTER: "@edr/passenger-backoffice" + APP_PATH: apps/edr-passenger-web/backoffice + VITE_API_URL: ${PASSENGER_VITE_API_URL:-http://localhost:4000} + ports: + - "5184:80" + +secrets: + npmrc: + file: .npmrc diff --git a/infrastructure/docker/Dockerfile.web b/infrastructure/docker/Dockerfile.web new file mode 100644 index 000000000..f51211f7b --- /dev/null +++ b/infrastructure/docker/Dockerfile.web @@ -0,0 +1,37 @@ +# syntax=docker/dockerfile:1 + +ARG TURBO_FILTER=@edr/freight-portal +ARG APP_PATH=apps/edr-freight-web/portal +ARG VITE_API_URL=http://localhost:3001/api + +FROM node:22-alpine AS base +RUN apk add --no-cache libc6-compat +RUN corepack enable +WORKDIR /app + +FROM base AS pruner +ARG TURBO_FILTER +COPY . . +RUN pnpm dlx turbo prune "${TURBO_FILTER}" --docker + +FROM base AS installer +COPY --from=pruner /app/out/json/ . +COPY --from=pruner /app/out/pnpm-lock.yaml ./pnpm-lock.yaml +RUN --mount=type=secret,id=npmrc,target=./.npmrc,required=false \ + pnpm install --frozen-lockfile + +FROM base AS builder +ARG TURBO_FILTER +ARG APP_PATH +ARG VITE_API_URL +ENV VITE_API_URL=${VITE_API_URL} +COPY --from=installer /app/ . +COPY --from=pruner /app/out/full/ . +RUN pnpm turbo build --filter="${TURBO_FILTER}..." + +FROM nginx:alpine AS runner +ARG APP_PATH +COPY infrastructure/nginx/spa.conf /etc/nginx/conf.d/default.conf +COPY --from=builder /app/${APP_PATH}/dist /usr/share/nginx/html +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] diff --git a/infrastructure/docker/docker-compose.dev.yml b/infrastructure/docker/docker-compose.dev.yml deleted file mode 100644 index 7ed9e79a7..000000000 --- a/infrastructure/docker/docker-compose.dev.yml +++ /dev/null @@ -1,126 +0,0 @@ -version: "3.9" - -networks: - edr-network: - driver: bridge - -volumes: - postgres-freight-data: - postgres-passenger-data: - redis-data: - -services: - postgres-freight: - image: postgres:17-alpine - container_name: edr-postgres-freight - restart: unless-stopped - environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: edr_freight - ports: - - "5433:5432" - volumes: - - postgres-freight-data:/var/lib/postgresql/data - networks: - - edr-network - - postgres-passenger: - image: postgres:17-alpine - container_name: edr-postgres-passenger - restart: unless-stopped - environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: edr_passenger - ports: - - "5434:5432" - volumes: - - postgres-passenger-data:/var/lib/postgresql/data - networks: - - edr-network - - redis: - image: redis:7-alpine - container_name: edr-redis - restart: unless-stopped - ports: - - "6379:6379" - volumes: - - redis-data:/data - networks: - - edr-network - - edr-freight-api: - build: - context: ../../ - dockerfile: apps/edr-freight-api/Dockerfile - container_name: edr-freight-api - restart: unless-stopped - depends_on: - - postgres-freight - - redis - environment: - NODE_ENV: development - PORT: 3001 - DB_HOST: postgres-freight - DB_PORT: 5432 - DB_NAME: edr_freight - DB_USER: postgres - DB_PASSWORD: postgres - REDIS_HOST: redis - REDIS_PORT: 6379 - ports: - - "3001:3001" - networks: - - edr-network - - edr-freight-web: - build: - context: ../../ - dockerfile: apps/edr-freight-web/Dockerfile - container_name: edr-freight-web - restart: unless-stopped - depends_on: - - edr-freight-api - ports: - - "5173:5173" - networks: - - edr-network - - edr-passenger-api: - build: - context: ../../ - dockerfile: apps/edr-passenger-api/Dockerfile - container_name: edr-passenger-api - restart: unless-stopped - depends_on: - - postgres-passenger - - redis - environment: - NODE_ENV: development - PORT: 3002 - DB_HOST: postgres-passenger - DB_PORT: 5432 - DB_NAME: edr_passenger - DB_USER: postgres - DB_PASSWORD: postgres - REDIS_HOST: redis - REDIS_PORT: 6379 - ports: - - "3002:3002" - networks: - - edr-network - - edr-passenger-web: - build: - context: ../../ - dockerfile: apps/edr-passenger-web/Dockerfile - container_name: edr-passenger-web - restart: unless-stopped - depends_on: - - edr-passenger-api - ports: - - "5174:5174" - networks: - - edr-network diff --git a/infrastructure/docker/docker-compose.prod.yml b/infrastructure/docker/docker-compose.prod.yml deleted file mode 100644 index a8d18455c..000000000 --- a/infrastructure/docker/docker-compose.prod.yml +++ /dev/null @@ -1,134 +0,0 @@ -version: "3.9" - -networks: - edr-network: - driver: bridge - -volumes: - postgres-freight-data: - postgres-passenger-data: - redis-data: - -services: - postgres-freight: - image: postgres:17-alpine - container_name: edr-postgres-freight - restart: always - environment: - POSTGRES_USER: ${DB_USER} - POSTGRES_PASSWORD: ${DB_PASSWORD} - POSTGRES_DB: ${DB_NAME_FREIGHT} - volumes: - - postgres-freight-data:/var/lib/postgresql/data - networks: - - edr-network - - postgres-passenger: - image: postgres:17-alpine - container_name: edr-postgres-passenger - restart: always - environment: - POSTGRES_USER: ${DB_USER} - POSTGRES_PASSWORD: ${DB_PASSWORD} - POSTGRES_DB: ${DB_NAME_PASSENGER} - volumes: - - postgres-passenger-data:/var/lib/postgresql/data - networks: - - edr-network - - redis: - image: redis:7-alpine - container_name: edr-redis - restart: always - volumes: - - redis-data:/data - networks: - - edr-network - - edr-freight-api: - build: - context: ../../ - dockerfile: apps/edr-freight-api/Dockerfile - container_name: edr-freight-api - restart: always - depends_on: - - postgres-freight - - redis - environment: - NODE_ENV: production - PORT: 3001 - DB_HOST: postgres-freight - DB_PORT: 5432 - DB_NAME: ${DB_NAME_FREIGHT} - DB_USER: ${DB_USER} - DB_PASSWORD: ${DB_PASSWORD} - REDIS_HOST: redis - expose: - - "3001" - networks: - - edr-network - - edr-freight-web: - build: - context: ../../ - dockerfile: apps/edr-freight-web/Dockerfile - container_name: edr-freight-web - restart: always - depends_on: - - edr-freight-api - expose: - - "5173" - networks: - - edr-network - - edr-passenger-api: - build: - context: ../../ - dockerfile: apps/edr-passenger-api/Dockerfile - container_name: edr-passenger-api - restart: always - depends_on: - - postgres-passenger - - redis - environment: - NODE_ENV: production - PORT: 3002 - DB_HOST: postgres-passenger - DB_PORT: 5432 - DB_NAME: ${DB_NAME_PASSENGER} - DB_USER: ${DB_USER} - DB_PASSWORD: ${DB_PASSWORD} - REDIS_HOST: redis - expose: - - "3002" - networks: - - edr-network - - edr-passenger-web: - build: - context: ../../ - dockerfile: apps/edr-passenger-web/Dockerfile - container_name: edr-passenger-web - restart: always - depends_on: - - edr-passenger-api - expose: - - "5174" - networks: - - edr-network - - nginx: - image: nginx:1.27-alpine - container_name: edr-nginx - restart: always - ports: - - "80:80" - depends_on: - - edr-freight-web - - edr-freight-api - - edr-passenger-web - - edr-passenger-api - volumes: - - ../../infrastructure/nginx/nginx.conf:/etc/nginx/nginx.conf:ro - networks: - - edr-network diff --git a/infrastructure/nginx/nginx.conf b/infrastructure/nginx/nginx.conf deleted file mode 100644 index 28ffde4c1..000000000 --- a/infrastructure/nginx/nginx.conf +++ /dev/null @@ -1,63 +0,0 @@ -user nginx; -worker_processes auto; -error_log /var/log/nginx/error.log warn; -pid /var/run/nginx.pid; - -events { - worker_connections 1024; -} - -http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - sendfile on; - keepalive_timeout 65; - server_tokens off; - - upstream edr_freight_api { - server edr-freight-api:3001; - } - upstream edr_freight_web { - server edr-freight-web:5173; - } - upstream edr_passenger_api { - server edr-passenger-api:3002; - } - upstream edr_passenger_web { - server edr-passenger-web:5174; - } - - server { - listen 80; - server_name freight.edr.local; - - location /api/ { - proxy_pass http://edr_freight_api/api/; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } - - location / { - proxy_pass http://edr_freight_web/; - proxy_set_header Host $host; - } - } - - server { - listen 80; - server_name passenger.edr.local; - - location /api/ { - proxy_pass http://edr_passenger_api/api/; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } - - location / { - proxy_pass http://edr_passenger_web/; - proxy_set_header Host $host; - } - } -} diff --git a/infrastructure/nginx/spa.conf b/infrastructure/nginx/spa.conf new file mode 100644 index 000000000..87a5d7688 --- /dev/null +++ b/infrastructure/nginx/spa.conf @@ -0,0 +1,13 @@ +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + gzip on; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml; + + location / { + try_files $uri $uri/ /index.html; + } +} diff --git a/package.json b/package.json index 5800955ef..a018436f5 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,8 @@ "lint": "turbo run lint", "type-check": "turbo run type-check", "format": "prettier --write \"**/*.{ts,tsx,json,md}\"", + "docker:build": "docker compose build", + "docker:up": "docker compose up -d", "prepare": "husky" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 30f7e543d..0856ff3f4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -305,6 +305,9 @@ importers: '@nestjs/swagger': specifier: ^7.4.0 version: 7.4.2(@nestjs/common@11.1.23(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.23)(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2) + '@prisma/client': + specifier: ^6.19.3 + version: 6.19.3(prisma@6.19.3(typescript@5.9.3))(typescript@5.9.3) '@sendgrid/mail': specifier: ^8.1.0 version: 8.1.6 @@ -357,9 +360,6 @@ importers: '@nestjs/testing': specifier: ^11.1.19 version: 11.1.23(@nestjs/common@11.1.23(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.23)(@nestjs/microservices@11.1.23)(@nestjs/platform-express@11.1.23) - '@prisma/client': - specifier: ^6.19.3 - version: 6.19.3(prisma@6.19.3(typescript@5.9.3))(typescript@5.9.3) '@types/bcrypt': specifier: ^5.0.2 version: 5.0.2 diff --git a/scripts/deploy/create-npmrc.sh b/scripts/deploy/create-npmrc.sh new file mode 100644 index 000000000..ce3df826c --- /dev/null +++ b/scripts/deploy/create-npmrc.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# Create .npmrc_temp and .npmrc for Docker BuildKit / compose secrets. +# Requires NPM_TOKEN in the environment. + +set -euo pipefail + +if [[ -z "${NPM_TOKEN:-}" ]]; then + echo "NPM_TOKEN is not set" >&2 + exit 1 +fi + +cat < .npmrc_temp +@tria-plc:registry=https://npm.pkg.github.com +//npm.pkg.github.com/:_authToken=${NPM_TOKEN} +always-auth=true +EOF + +cp .npmrc_temp .npmrc +echo "Created .npmrc_temp and .npmrc for private @tria-plc packages" diff --git a/scripts/deploy/sync-env-from-server.sh b/scripts/deploy/sync-env-from-server.sh new file mode 100644 index 000000000..09f1eb79f --- /dev/null +++ b/scripts/deploy/sync-env-from-server.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# Sync .env files from the self-hosted runner filesystem into the repo. +# +# Usage: +# PROJECT=edr-freight BRANCH=main ./scripts/deploy/sync-env-from-server.sh freight-api freight-portal freight-backoffice +# +# Server layout (one file per service): +# /home/user/environmen///freight-api.env +# /home/user/environmen///freight-portal.env +# /home/user/environmen///freight-web.build.env (optional, exports VITE_API_URL etc.) + +set -euo pipefail + +DEPLOY_USER="${DEPLOY_USER:-tria}" +ENV_ROOT="${ENV_ROOT:-/home/${DEPLOY_USER}/environmen/${PROJECT:?PROJECT is required}/${BRANCH:?BRANCH is required}}" + +if [[ ! -d "${ENV_ROOT}" ]]; then + echo "Environment directory not found: ${ENV_ROOT}" >&2 + exit 1 +fi + +echo "Using environment directory: ${ENV_ROOT}" + +declare -A SERVICE_ENV_TARGET=( + ["freight-api"]="apps/edr-freight-api/.env" + ["freight-portal"]="apps/edr-freight-web/portal/.env" + ["freight-backoffice"]="apps/edr-freight-web/backoffice/.env" + ["passenger-api"]="apps/edr-passenger-api/.env" + ["passenger-portal"]="apps/edr-passenger-web/portal/.env" + ["passenger-backoffice"]="apps/edr-passenger-web/backoffice/.env" +) + +for service in "$@"; do + src="${ENV_ROOT}/${service}.env" + dest="${SERVICE_ENV_TARGET[${service}]:-}" + + if [[ -z "${dest}" ]]; then + echo "Unknown service: ${service}" >&2 + exit 1 + fi + + if [[ ! -f "${src}" ]]; then + echo "Missing env file: ${src}" >&2 + exit 1 + fi + + mkdir -p "$(dirname "${dest}")" + cp "${src}" "${dest}" + echo "Synced ${src} -> ${dest}" +done + +# Optional build-time variables (VITE_API_URL, etc.) +# Set BUILD_ENV_FILE=freight-web.build.env or passenger-web.build.env per workflow. +build_env_file="${BUILD_ENV_FILE:-web.build.env}" +build_env="${ENV_ROOT}/${build_env_file}" +if [[ -f "${build_env}" ]]; then + echo "Loading build variables from ${build_env}" + set -a + # shellcheck disable=SC1090 + source "${build_env}" + set +a + + if [[ -n "${GITHUB_ENV:-}" ]]; then + grep -E '^[[:space:]]*export[[:space:]]+[A-Za-z_][A-Za-z0-9_]*=' "${build_env}" \ + | sed -E 's/^[[:space:]]*export[[:space:]]+//' >> "${GITHUB_ENV}" + echo "Wrote build variables to GITHUB_ENV" + fi +fi From f79ff779251f7035bc05ec1d2e3bcad7999f60cb Mon Sep 17 00:00:00 2001 From: SennayT Date: Thu, 28 May 2026 13:01:52 +0300 Subject: [PATCH 05/18] add COMPOSE_PROJECT_NAME for each workflow --- .github/workflows/deploy-freight.yml | 23 +++++++++++++++-------- .github/workflows/deploy-passenger.yml | 23 +++++++++++++++-------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/.github/workflows/deploy-freight.yml b/.github/workflows/deploy-freight.yml index a09faf599..778ad4067 100644 --- a/.github/workflows/deploy-freight.yml +++ b/.github/workflows/deploy-freight.yml @@ -6,6 +6,7 @@ on: - main - develop - staging + - feat/improve-buid-pipeline paths: - "apps/edr-freight-api/**" - "apps/edr-freight-web/**" @@ -45,6 +46,12 @@ jobs: freight-portal \ freight-backoffice + - name: Set compose project name + run: | + set -euo pipefail + branch_slug=$(echo "${BRANCH}" | tr "[:upper:]" "[:lower:]" | sed -E "s/[^a-z0-9]+/-/g; s/^-+//; s/-+$//") + echo "COMPOSE_PROJECT_NAME=${PROJECT}-${branch_slug}" >> "${GITHUB_ENV}" + - name: Configure npm auth for Docker builds env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} @@ -53,18 +60,18 @@ jobs: - name: Build images run: | set -euo pipefail - docker compose build \ + docker compose --project-name "${COMPOSE_PROJECT_NAME}" build \ freight-api \ freight-portal \ freight-backoffice - - name: Deploy containers - run: | - set -euo pipefail - docker compose up -d \ - freight-api \ - freight-portal \ - freight-backoffice + # - name: Deploy containers + # run: | + # set -euo pipefail + # docker compose --project-name "${COMPOSE_PROJECT_NAME}" up -d \ + # freight-api \ + # freight-portal \ + # freight-backoffice - name: Remove npm credentials from workspace if: always() diff --git a/.github/workflows/deploy-passenger.yml b/.github/workflows/deploy-passenger.yml index ae4864fe8..c282fd9d5 100644 --- a/.github/workflows/deploy-passenger.yml +++ b/.github/workflows/deploy-passenger.yml @@ -6,6 +6,7 @@ on: - main - develop - staging + - feat/improve-buid-pipeline paths: - "apps/edr-passenger-api/**" - "apps/edr-passenger-web/**" @@ -45,6 +46,12 @@ jobs: passenger-portal \ passenger-backoffice + - name: Set compose project name + run: | + set -euo pipefail + branch_slug=$(echo "${BRANCH}" | tr "[:upper:]" "[:lower:]" | sed -E "s/[^a-z0-9]+/-/g; s/^-+//; s/-+$//") + echo "COMPOSE_PROJECT_NAME=${PROJECT}-${branch_slug}" >> "${GITHUB_ENV}" + - name: Configure npm auth for Docker builds env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} @@ -53,18 +60,18 @@ jobs: - name: Build images run: | set -euo pipefail - docker compose build \ + docker compose --project-name "${COMPOSE_PROJECT_NAME}" build \ passenger-api \ passenger-portal \ passenger-backoffice - - name: Deploy containers - run: | - set -euo pipefail - docker compose up -d \ - passenger-api \ - passenger-portal \ - passenger-backoffice + # - name: Deploy containers + # run: | + # set -euo pipefail + # docker compose --project-name "${COMPOSE_PROJECT_NAME}" up -d \ + # passenger-api \ + # passenger-portal \ + # passenger-backoffice - name: Remove npm credentials from workspace if: always() From 39954f6e677ecf22cba4c59239208a927d284185 Mon Sep 17 00:00:00 2001 From: SennayT Date: Thu, 28 May 2026 13:28:23 +0300 Subject: [PATCH 06/18] modify branch name --- scripts/deploy/sync-env-from-server.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/deploy/sync-env-from-server.sh b/scripts/deploy/sync-env-from-server.sh index 09f1eb79f..fda96a266 100644 --- a/scripts/deploy/sync-env-from-server.sh +++ b/scripts/deploy/sync-env-from-server.sh @@ -5,14 +5,16 @@ # PROJECT=edr-freight BRANCH=main ./scripts/deploy/sync-env-from-server.sh freight-api freight-portal freight-backoffice # # Server layout (one file per service): -# /home/user/environmen///freight-api.env -# /home/user/environmen///freight-portal.env -# /home/user/environmen///freight-web.build.env (optional, exports VITE_API_URL etc.) +# /home/user/environmen///freight-api.env +# /home/user/environmen///freight-portal.env +# /home/user/environmen///freight-web.build.env (optional, exports VITE_API_URL etc.) set -euo pipefail DEPLOY_USER="${DEPLOY_USER:-tria}" -ENV_ROOT="${ENV_ROOT:-/home/${DEPLOY_USER}/environmen/${PROJECT:?PROJECT is required}/${BRANCH:?BRANCH is required}}" +BRANCH="${BRANCH:?BRANCH is required}" +BRANCH_SLUG="${BRANCH_SLUG:-$(echo "${BRANCH}" | tr "[:upper:]" "[:lower:]" | sed -E "s/[^a-z0-9]+/-/g; s/^-+//; s/-+$//")}" +ENV_ROOT="${ENV_ROOT:-/home/${DEPLOY_USER}/environmen/${PROJECT:?PROJECT is required}/${BRANCH_SLUG}}" if [[ ! -d "${ENV_ROOT}" ]]; then echo "Environment directory not found: ${ENV_ROOT}" >&2 From d462ddaf2906de8f201aa765b3a57fa1336065af Mon Sep 17 00:00:00 2001 From: SennayT Date: Thu, 28 May 2026 13:29:37 +0300 Subject: [PATCH 07/18] modify environment location --- scripts/deploy/sync-env-from-server.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/deploy/sync-env-from-server.sh b/scripts/deploy/sync-env-from-server.sh index fda96a266..b1fa54af6 100644 --- a/scripts/deploy/sync-env-from-server.sh +++ b/scripts/deploy/sync-env-from-server.sh @@ -14,7 +14,7 @@ set -euo pipefail DEPLOY_USER="${DEPLOY_USER:-tria}" BRANCH="${BRANCH:?BRANCH is required}" BRANCH_SLUG="${BRANCH_SLUG:-$(echo "${BRANCH}" | tr "[:upper:]" "[:lower:]" | sed -E "s/[^a-z0-9]+/-/g; s/^-+//; s/-+$//")}" -ENV_ROOT="${ENV_ROOT:-/home/${DEPLOY_USER}/environmen/${PROJECT:?PROJECT is required}/${BRANCH_SLUG}}" +ENV_ROOT="${ENV_ROOT:-/home/${DEPLOY_USER}/environment/edr/${PROJECT:?PROJECT is required}/${BRANCH_SLUG}}" if [[ ! -d "${ENV_ROOT}" ]]; then echo "Environment directory not found: ${ENV_ROOT}" >&2 From 2a93de138f13af084d9cdd925c1435237c8520c2 Mon Sep 17 00:00:00 2001 From: SennayT Date: Thu, 28 May 2026 13:33:09 +0300 Subject: [PATCH 08/18] move pipelines to one file --- .github/workflows/deploy-freight.yml | 78 ------------------- .../{deploy-passenger.yml => deploy.yml} | 63 ++++++++++++++- scripts/deploy/sync-env-from-server.sh | 2 +- 3 files changed, 60 insertions(+), 83 deletions(-) delete mode 100644 .github/workflows/deploy-freight.yml rename .github/workflows/{deploy-passenger.yml => deploy.yml} (53%) diff --git a/.github/workflows/deploy-freight.yml b/.github/workflows/deploy-freight.yml deleted file mode 100644 index 778ad4067..000000000 --- a/.github/workflows/deploy-freight.yml +++ /dev/null @@ -1,78 +0,0 @@ -name: Deploy Freight - -on: - push: - branches: - - main - - develop - - staging - - feat/improve-buid-pipeline - paths: - - "apps/edr-freight-api/**" - - "apps/edr-freight-web/**" - - "packages/**" - - "infrastructure/docker/Dockerfile.web" - - "infrastructure/nginx/**" - - "docker-compose.yaml" - - "pnpm-lock.yaml" - - "scripts/deploy/**" - - ".github/workflows/deploy-freight.yml" - workflow_dispatch: - -concurrency: - group: deploy-freight-${{ github.ref_name }} - cancel-in-progress: true - -jobs: - deploy: - name: Build and deploy freight stack - runs-on: self-hosted - env: - PROJECT: edr-freight - BRANCH: ${{ github.ref_name }} - DEPLOY_USER: user - BUILD_ENV_FILE: freight-web.build.env - DOCKER_BUILDKIT: "1" - COMPOSE_DOCKER_CLI_BUILD: "1" - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Sync environment from server - run: | - chmod +x scripts/deploy/*.sh - ./scripts/deploy/sync-env-from-server.sh \ - freight-api \ - freight-portal \ - freight-backoffice - - - name: Set compose project name - run: | - set -euo pipefail - branch_slug=$(echo "${BRANCH}" | tr "[:upper:]" "[:lower:]" | sed -E "s/[^a-z0-9]+/-/g; s/^-+//; s/-+$//") - echo "COMPOSE_PROJECT_NAME=${PROJECT}-${branch_slug}" >> "${GITHUB_ENV}" - - - name: Configure npm auth for Docker builds - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - run: ./scripts/deploy/create-npmrc.sh - - - name: Build images - run: | - set -euo pipefail - docker compose --project-name "${COMPOSE_PROJECT_NAME}" build \ - freight-api \ - freight-portal \ - freight-backoffice - - # - name: Deploy containers - # run: | - # set -euo pipefail - # docker compose --project-name "${COMPOSE_PROJECT_NAME}" up -d \ - # freight-api \ - # freight-portal \ - # freight-backoffice - - - name: Remove npm credentials from workspace - if: always() - run: rm -f .npmrc .npmrc_temp diff --git a/.github/workflows/deploy-passenger.yml b/.github/workflows/deploy.yml similarity index 53% rename from .github/workflows/deploy-passenger.yml rename to .github/workflows/deploy.yml index c282fd9d5..c3b40f659 100644 --- a/.github/workflows/deploy-passenger.yml +++ b/.github/workflows/deploy.yml @@ -1,4 +1,4 @@ -name: Deploy Passenger +name: Deploy Stacks on: push: @@ -8,6 +8,8 @@ on: - staging - feat/improve-buid-pipeline paths: + - "apps/edr-freight-api/**" + - "apps/edr-freight-web/**" - "apps/edr-passenger-api/**" - "apps/edr-passenger-web/**" - "packages/**" @@ -16,15 +18,68 @@ on: - "docker-compose.yaml" - "pnpm-lock.yaml" - "scripts/deploy/**" - - ".github/workflows/deploy-passenger.yml" + - ".github/workflows/deploy.yml" workflow_dispatch: concurrency: - group: deploy-passenger-${{ github.ref_name }} + group: deploy-${{ github.ref_name }} cancel-in-progress: true jobs: - deploy: + deploy-freight: + name: Build and deploy freight stack + runs-on: self-hosted + env: + PROJECT: edr-freight + BRANCH: ${{ github.ref_name }} + DEPLOY_USER: user + BUILD_ENV_FILE: freight-web.build.env + DOCKER_BUILDKIT: "1" + COMPOSE_DOCKER_CLI_BUILD: "1" + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Sync environment from server + run: | + chmod +x scripts/deploy/*.sh + ./scripts/deploy/sync-env-from-server.sh \ + freight-api \ + freight-portal \ + freight-backoffice + + - name: Set compose project name + run: | + set -euo pipefail + branch_slug=$(echo "${BRANCH}" | tr "[:upper:]" "[:lower:]" | sed -E "s/[^a-z0-9]+/-/g; s/^-+//; s/-+$//") + echo "COMPOSE_PROJECT_NAME=${PROJECT}-${branch_slug}" >> "${GITHUB_ENV}" + + - name: Configure npm auth for Docker builds + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + run: ./scripts/deploy/create-npmrc.sh + + - name: Build images + run: | + set -euo pipefail + docker compose --project-name "${COMPOSE_PROJECT_NAME}" build \ + freight-api \ + freight-portal \ + freight-backoffice + + # - name: Deploy containers + # run: | + # set -euo pipefail + # docker compose --project-name "${COMPOSE_PROJECT_NAME}" up -d \ + # freight-api \ + # freight-portal \ + # freight-backoffice + + - name: Remove npm credentials from workspace + if: always() + run: rm -f .npmrc .npmrc_temp + + deploy-passenger: name: Build and deploy passenger stack runs-on: self-hosted env: diff --git a/scripts/deploy/sync-env-from-server.sh b/scripts/deploy/sync-env-from-server.sh index b1fa54af6..52c4a1aec 100644 --- a/scripts/deploy/sync-env-from-server.sh +++ b/scripts/deploy/sync-env-from-server.sh @@ -14,7 +14,7 @@ set -euo pipefail DEPLOY_USER="${DEPLOY_USER:-tria}" BRANCH="${BRANCH:?BRANCH is required}" BRANCH_SLUG="${BRANCH_SLUG:-$(echo "${BRANCH}" | tr "[:upper:]" "[:lower:]" | sed -E "s/[^a-z0-9]+/-/g; s/^-+//; s/-+$//")}" -ENV_ROOT="${ENV_ROOT:-/home/${DEPLOY_USER}/environment/edr/${PROJECT:?PROJECT is required}/${BRANCH_SLUG}}" +ENV_ROOT="${ENV_ROOT:-/home/${DEPLOY_USER}/environment/edr/${BRANCH_SLUG}/${PROJECT:?PROJECT is required}}" if [[ ! -d "${ENV_ROOT}" ]]; then echo "Environment directory not found: ${ENV_ROOT}" >&2 From e18811c76db88f91c8a91bc198dc267e89c3705e Mon Sep 17 00:00:00 2001 From: SennayT Date: Thu, 28 May 2026 13:34:31 +0300 Subject: [PATCH 09/18] modify deploy user --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index c3b40f659..34497fc26 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -32,7 +32,7 @@ jobs: env: PROJECT: edr-freight BRANCH: ${{ github.ref_name }} - DEPLOY_USER: user + DEPLOY_USER: tria BUILD_ENV_FILE: freight-web.build.env DOCKER_BUILDKIT: "1" COMPOSE_DOCKER_CLI_BUILD: "1" From 90eec499f9d51db8bd8dda3dc2cd7548adb2b88a Mon Sep 17 00:00:00 2001 From: SennayT Date: Thu, 28 May 2026 13:45:32 +0300 Subject: [PATCH 10/18] modify deploy user for passenger --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 34497fc26..87cc8e724 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -85,7 +85,7 @@ jobs: env: PROJECT: edr-passenger BRANCH: ${{ github.ref_name }} - DEPLOY_USER: user + DEPLOY_USER: tria BUILD_ENV_FILE: passenger-web.build.env DOCKER_BUILDKIT: "1" COMPOSE_DOCKER_CLI_BUILD: "1" From af5f4cd038a382a3535fef766850db7bde3e041b Mon Sep 17 00:00:00 2001 From: SennayT Date: Thu, 28 May 2026 14:03:49 +0300 Subject: [PATCH 11/18] change node version and modify docker compose --- apps/edr-passenger-api/Dockerfile | 4 ++-- docker-compose.yaml | 16 ++++++---------- infrastructure/docker/Dockerfile.web | 2 +- scripts/deploy/sync-env-from-server.sh | 12 ++++++++++++ 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/apps/edr-passenger-api/Dockerfile b/apps/edr-passenger-api/Dockerfile index 425c5a95a..7d6896223 100644 --- a/apps/edr-passenger-api/Dockerfile +++ b/apps/edr-passenger-api/Dockerfile @@ -2,7 +2,7 @@ # Build from monorepo root: docker build -f apps/edr-passenger-api/Dockerfile . # On start: runs prisma migrate deploy + seed, then the API. -FROM node:22-alpine AS base +FROM node:24.15.0-alpine AS base RUN apk add --no-cache libc6-compat RUN corepack enable WORKDIR /app @@ -31,7 +31,7 @@ RUN if [ -d node_modules/.prisma ]; then \ cp -r node_modules/.prisma /deploy/node_modules/.prisma; \ fi -FROM node:22-alpine AS runner +FROM node:24.15.0-alpine AS runner RUN apk add --no-cache libc6-compat RUN corepack enable && corepack prepare pnpm@11.1.1 --activate ENV NODE_ENV=production diff --git a/docker-compose.yaml b/docker-compose.yaml index f6de0d965..e186b3f09 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -13,9 +13,7 @@ services: secrets: - npmrc ports: - - "3001:3001" - environment: - PORT: "3001" + - "${FREIGHT_API_PORT:-3001}:${FREIGHT_API_PORT:-3001}" env_file: - apps/edr-freight-api/.env @@ -24,9 +22,7 @@ services: context: . dockerfile: apps/edr-passenger-api/Dockerfile ports: - - "4000:4000" - environment: - PORT: "4000" + - "${PORT:-4000}:${PORT:-4000}" env_file: - apps/edr-passenger-api/.env @@ -42,7 +38,7 @@ services: secrets: - npmrc ports: - - "5173:80" + - "${PORT:-5173}:80" freight-backoffice: build: @@ -55,7 +51,7 @@ services: secrets: - npmrc ports: - - "5183:80" + - "${PORT:-5183}:80" passenger-portal: build: @@ -66,7 +62,7 @@ services: APP_PATH: apps/edr-passenger-web/portal VITE_API_URL: ${PASSENGER_VITE_API_URL:-http://localhost:4000} ports: - - "5174:80" + - "${PORT:-5174}:80" passenger-backoffice: build: @@ -77,7 +73,7 @@ services: APP_PATH: apps/edr-passenger-web/backoffice VITE_API_URL: ${PASSENGER_VITE_API_URL:-http://localhost:4000} ports: - - "5184:80" + - "${PORT:-5184}:80" secrets: npmrc: diff --git a/infrastructure/docker/Dockerfile.web b/infrastructure/docker/Dockerfile.web index f51211f7b..c51aa3aa8 100644 --- a/infrastructure/docker/Dockerfile.web +++ b/infrastructure/docker/Dockerfile.web @@ -4,7 +4,7 @@ ARG TURBO_FILTER=@edr/freight-portal ARG APP_PATH=apps/edr-freight-web/portal ARG VITE_API_URL=http://localhost:3001/api -FROM node:22-alpine AS base +FROM node:24.15.0-alpine AS base RUN apk add --no-cache libc6-compat RUN corepack enable WORKDIR /app diff --git a/scripts/deploy/sync-env-from-server.sh b/scripts/deploy/sync-env-from-server.sh index 52c4a1aec..767a1aad2 100644 --- a/scripts/deploy/sync-env-from-server.sh +++ b/scripts/deploy/sync-env-from-server.sh @@ -49,6 +49,18 @@ for service in "$@"; do mkdir -p "$(dirname "${dest}")" cp "${src}" "${dest}" echo "Synced ${src} -> ${dest}" + + port_value=$(sed -n -E 's/^[[:space:]]*PORT[[:space:]]*=[[:space:]]*"?([^"#]+)"?[[:space:]]*(#.*)?$/\1/p' "${src}" | head -n1 | tr -d '[:space:]') + if [[ -z "${port_value}" ]]; then + echo "Missing required PORT in env file: ${src}" >&2 + exit 1 + fi + + # if [[ -n "${GITHUB_ENV:-}" ]]; then + # service_var=$(echo "${service}" | tr '[:lower:]-' '[:upper:]_') + # echo "${service_var}_PORT=${port_value}" >> "${GITHUB_ENV}" + # echo "Exported ${service_var}_PORT from ${src}" + # fi done # Optional build-time variables (VITE_API_URL, etc.) From 37c13ee7e1b1ec0913979640bc2fb55ca5822772 Mon Sep 17 00:00:00 2001 From: SennayT Date: Thu, 28 May 2026 14:12:32 +0300 Subject: [PATCH 12/18] move deployment to dev branch --- .github/workflows/deploy.yml | 31 ++++++++++++++-------------- apps/edr-passenger-api/Dockerfile | 1 + infrastructure/docker/Dockerfile.web | 1 + 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 87cc8e724..3af25bcf6 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -4,9 +4,8 @@ on: push: branches: - main - - develop + - dev - staging - - feat/improve-buid-pipeline paths: - "apps/edr-freight-api/**" - "apps/edr-freight-web/**" @@ -67,13 +66,13 @@ jobs: freight-portal \ freight-backoffice - # - name: Deploy containers - # run: | - # set -euo pipefail - # docker compose --project-name "${COMPOSE_PROJECT_NAME}" up -d \ - # freight-api \ - # freight-portal \ - # freight-backoffice + - name: Deploy containers + run: | + set -euo pipefail + docker compose --project-name "${COMPOSE_PROJECT_NAME}" up -d \ + freight-api \ + freight-portal \ + freight-backoffice - name: Remove npm credentials from workspace if: always() @@ -120,13 +119,13 @@ jobs: passenger-portal \ passenger-backoffice - # - name: Deploy containers - # run: | - # set -euo pipefail - # docker compose --project-name "${COMPOSE_PROJECT_NAME}" up -d \ - # passenger-api \ - # passenger-portal \ - # passenger-backoffice + - name: Deploy containers + run: | + set -euo pipefail + docker compose --project-name "${COMPOSE_PROJECT_NAME}" up -d \ + passenger-api \ + passenger-portal \ + passenger-backoffice - name: Remove npm credentials from workspace if: always() diff --git a/apps/edr-passenger-api/Dockerfile b/apps/edr-passenger-api/Dockerfile index 7d6896223..5fd647968 100644 --- a/apps/edr-passenger-api/Dockerfile +++ b/apps/edr-passenger-api/Dockerfile @@ -15,6 +15,7 @@ FROM base AS installer COPY --from=pruner /app/out/json/ . COPY --from=pruner /app/out/pnpm-lock.yaml ./pnpm-lock.yaml RUN --mount=type=secret,id=npmrc,target=./.npmrc,required=false \ + --mount=type=cache,id=pnpm,target=/pnpm/store \ pnpm install --frozen-lockfile FROM base AS builder diff --git a/infrastructure/docker/Dockerfile.web b/infrastructure/docker/Dockerfile.web index c51aa3aa8..38e863b86 100644 --- a/infrastructure/docker/Dockerfile.web +++ b/infrastructure/docker/Dockerfile.web @@ -18,6 +18,7 @@ FROM base AS installer COPY --from=pruner /app/out/json/ . COPY --from=pruner /app/out/pnpm-lock.yaml ./pnpm-lock.yaml RUN --mount=type=secret,id=npmrc,target=./.npmrc,required=false \ + --mount=type=cache,id=pnpm,target=/pnpm/store \ pnpm install --frozen-lockfile FROM base AS builder From 7608cfb9e9eb1b87bc162e162b8bb4437084f487 Mon Sep 17 00:00:00 2001 From: SennayT Date: Thu, 28 May 2026 14:17:10 +0300 Subject: [PATCH 13/18] use PORT for freight api --- docker-compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index e186b3f09..a7ce6073f 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -13,7 +13,7 @@ services: secrets: - npmrc ports: - - "${FREIGHT_API_PORT:-3001}:${FREIGHT_API_PORT:-3001}" + - "${PORT:-3001}:${FREIGHT_API_PORT:-3001}" env_file: - apps/edr-freight-api/.env From 4b8f1b3ab1c341a60e4a159c3d168e85135d2d2d Mon Sep 17 00:00:00 2001 From: SennayT Date: Thu, 28 May 2026 14:19:08 +0300 Subject: [PATCH 14/18] use PORT for freight api --- docker-compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index a7ce6073f..c2efb1905 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -13,7 +13,7 @@ services: secrets: - npmrc ports: - - "${PORT:-3001}:${FREIGHT_API_PORT:-3001}" + - "${PORT:-3001}:${PORT:-3001}" env_file: - apps/edr-freight-api/.env From e5ffa91918c9190408072ecc188e9c0a3b89c95e Mon Sep 17 00:00:00 2001 From: SennayT Date: Thu, 28 May 2026 14:26:46 +0300 Subject: [PATCH 15/18] modify port mapping to read from env --- docker-compose.yaml | 12 ++++++------ scripts/deploy/sync-env-from-server.sh | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index c2efb1905..2438b70fa 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -13,7 +13,7 @@ services: secrets: - npmrc ports: - - "${PORT:-3001}:${PORT:-3001}" + - "${FREIGHT_API_PORT:-3001}:${FREIGHT_API_PORT:-3001}" env_file: - apps/edr-freight-api/.env @@ -22,7 +22,7 @@ services: context: . dockerfile: apps/edr-passenger-api/Dockerfile ports: - - "${PORT:-4000}:${PORT:-4000}" + - "${PASSENGER_API_PORT:-4000}:${PASSENGER_API_PORT:-4000}" env_file: - apps/edr-passenger-api/.env @@ -38,7 +38,7 @@ services: secrets: - npmrc ports: - - "${PORT:-5173}:80" + - "${FREIGHT_PORTAL_PORT:-5173}:80" freight-backoffice: build: @@ -51,7 +51,7 @@ services: secrets: - npmrc ports: - - "${PORT:-5183}:80" + - "${FREIGHT_BACKOFFICE_PORT:-5183}:80" passenger-portal: build: @@ -62,7 +62,7 @@ services: APP_PATH: apps/edr-passenger-web/portal VITE_API_URL: ${PASSENGER_VITE_API_URL:-http://localhost:4000} ports: - - "${PORT:-5174}:80" + - "${PASSENGER_PORTAL_PORT:-5174}:80" passenger-backoffice: build: @@ -73,7 +73,7 @@ services: APP_PATH: apps/edr-passenger-web/backoffice VITE_API_URL: ${PASSENGER_VITE_API_URL:-http://localhost:4000} ports: - - "${PORT:-5184}:80" + - "${PASSENGER_BACKOFFICE_PORT:-5184}:80" secrets: npmrc: diff --git a/scripts/deploy/sync-env-from-server.sh b/scripts/deploy/sync-env-from-server.sh index 767a1aad2..a0618982c 100644 --- a/scripts/deploy/sync-env-from-server.sh +++ b/scripts/deploy/sync-env-from-server.sh @@ -56,11 +56,11 @@ for service in "$@"; do exit 1 fi - # if [[ -n "${GITHUB_ENV:-}" ]]; then - # service_var=$(echo "${service}" | tr '[:lower:]-' '[:upper:]_') - # echo "${service_var}_PORT=${port_value}" >> "${GITHUB_ENV}" - # echo "Exported ${service_var}_PORT from ${src}" - # fi + if [[ -n "${GITHUB_ENV:-}" ]]; then + service_var=$(echo "${service}" | tr '[:lower:]-' '[:upper:]_') + echo "${service_var}_PORT=${port_value}" >> "${GITHUB_ENV}" + echo "Exported ${service_var}_PORT from ${src}" + fi done # Optional build-time variables (VITE_API_URL, etc.) From 21bb1db10431912d57357d3fd408d6a88fec001e Mon Sep 17 00:00:00 2001 From: SennayT Date: Thu, 28 May 2026 15:17:29 +0300 Subject: [PATCH 16/18] run all apps in separate steps --- .github/workflows/deploy.yml | 102 +++++++++++------------------------ 1 file changed, 31 insertions(+), 71 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 3af25bcf6..359ce74f1 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -25,14 +25,36 @@ concurrency: cancel-in-progress: true jobs: - deploy-freight: - name: Build and deploy freight stack + deploy: + name: Deploy ${{ matrix.service }} runs-on: self-hosted + strategy: + fail-fast: false + matrix: + include: + - project: edr-freight + build_env_file: freight-web.build.env + service: freight-api + - project: edr-freight + build_env_file: freight-web.build.env + service: freight-portal + - project: edr-freight + build_env_file: freight-web.build.env + service: freight-backoffice + - project: edr-passenger + build_env_file: passenger-web.build.env + service: passenger-api + - project: edr-passenger + build_env_file: passenger-web.build.env + service: passenger-portal + - project: edr-passenger + build_env_file: passenger-web.build.env + service: passenger-backoffice env: - PROJECT: edr-freight + PROJECT: ${{ matrix.project }} BRANCH: ${{ github.ref_name }} DEPLOY_USER: tria - BUILD_ENV_FILE: freight-web.build.env + BUILD_ENV_FILE: ${{ matrix.build_env_file }} DOCKER_BUILDKIT: "1" COMPOSE_DOCKER_CLI_BUILD: "1" steps: @@ -42,10 +64,7 @@ jobs: - name: Sync environment from server run: | chmod +x scripts/deploy/*.sh - ./scripts/deploy/sync-env-from-server.sh \ - freight-api \ - freight-portal \ - freight-backoffice + ./scripts/deploy/sync-env-from-server.sh "${{ matrix.service }}" - name: Set compose project name run: | @@ -58,74 +77,15 @@ jobs: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} run: ./scripts/deploy/create-npmrc.sh - - name: Build images + - name: Build ${{ matrix.service }} run: | set -euo pipefail - docker compose --project-name "${COMPOSE_PROJECT_NAME}" build \ - freight-api \ - freight-portal \ - freight-backoffice + docker compose --project-name "${COMPOSE_PROJECT_NAME}" build "${{ matrix.service }}" - - name: Deploy containers + - name: Deploy ${{ matrix.service }} run: | set -euo pipefail - docker compose --project-name "${COMPOSE_PROJECT_NAME}" up -d \ - freight-api \ - freight-portal \ - freight-backoffice - - - name: Remove npm credentials from workspace - if: always() - run: rm -f .npmrc .npmrc_temp - - deploy-passenger: - name: Build and deploy passenger stack - runs-on: self-hosted - env: - PROJECT: edr-passenger - BRANCH: ${{ github.ref_name }} - DEPLOY_USER: tria - BUILD_ENV_FILE: passenger-web.build.env - DOCKER_BUILDKIT: "1" - COMPOSE_DOCKER_CLI_BUILD: "1" - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Sync environment from server - run: | - chmod +x scripts/deploy/*.sh - ./scripts/deploy/sync-env-from-server.sh \ - passenger-api \ - passenger-portal \ - passenger-backoffice - - - name: Set compose project name - run: | - set -euo pipefail - branch_slug=$(echo "${BRANCH}" | tr "[:upper:]" "[:lower:]" | sed -E "s/[^a-z0-9]+/-/g; s/^-+//; s/-+$//") - echo "COMPOSE_PROJECT_NAME=${PROJECT}-${branch_slug}" >> "${GITHUB_ENV}" - - - name: Configure npm auth for Docker builds - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - run: ./scripts/deploy/create-npmrc.sh - - - name: Build images - run: | - set -euo pipefail - docker compose --project-name "${COMPOSE_PROJECT_NAME}" build \ - passenger-api \ - passenger-portal \ - passenger-backoffice - - - name: Deploy containers - run: | - set -euo pipefail - docker compose --project-name "${COMPOSE_PROJECT_NAME}" up -d \ - passenger-api \ - passenger-portal \ - passenger-backoffice + docker compose --project-name "${COMPOSE_PROJECT_NAME}" up -d "${{ matrix.service }}" - name: Remove npm credentials from workspace if: always() From ef54be51252eea33359dd91120b03ed72bd88094 Mon Sep 17 00:00:00 2001 From: SennayT Date: Thu, 28 May 2026 15:29:31 +0300 Subject: [PATCH 17/18] add deployment documentation and checkpoint file --- DEPLOYMENT.md | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++ checkpoint.md | 63 +++++++++++++++++ 2 files changed, 250 insertions(+) create mode 100644 DEPLOYMENT.md create mode 100644 checkpoint.md diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 000000000..00ca421d1 --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,187 @@ +# Deployment Runbook + +This document explains how deployments work for the EDR platform using Docker, GitHub Actions, and self-hosted runners. + +## Overview + +- Monorepo contains 6 deployable services: + - `freight-api` + - `freight-portal` + - `freight-backoffice` + - `passenger-api` + - `passenger-portal` + - `passenger-backoffice` +- Deployments run through one workflow: `.github/workflows/deploy.yml` +- Each service is built/deployed independently in parallel (matrix jobs). +- Docker Compose project names are branch-aware to avoid environment collisions on the same host. + +## Prerequisites + +- Docker Engine with Compose plugin on the self-hosted runner. +- GitHub self-hosted runner registered for this repository. +- Repository secret configured: + - `NPM_TOKEN` (for private `@tria-plc/*` package install during Docker build) +- Server-side env files created for each branch/environment. + +## Server Environment Files + +`sync-env-from-server.sh` reads env files from: + +`/home//environment/edr///` + +Where: + +- `` defaults to `tria` (overridable by `DEPLOY_USER`) +- `` is derived from Git branch (lowercase, non-alphanumeric replaced with `-`) +- `` is `edr-freight` or `edr-passenger` + +### Required files per project + +For `edr-freight`: + +- `freight-api.env` +- `freight-portal.env` +- `freight-backoffice.env` +- optional: `freight-web.build.env` + +For `edr-passenger`: + +- `passenger-api.env` +- `passenger-portal.env` +- `passenger-backoffice.env` +- optional: `passenger-web.build.env` + +### Required env key + +Each service env file must contain: + +- `PORT=` + +The sync script validates this and fails if missing. + +### Build env files (optional) + +Used for build-time variables (example: Vite API URLs), with `export` syntax: + +```bash +export FREIGHT_VITE_API_URL=https://freight-api.example.com/api +export PASSENGER_VITE_API_URL=https://passenger-api.example.com +``` + +These are injected into `GITHUB_ENV` during workflow execution. + +## Docker Compose Port Mapping + +`docker-compose.yaml` uses per-service env variables for host/container port mappings: + +- `FREIGHT_API_PORT` +- `PASSENGER_API_PORT` +- `FREIGHT_PORTAL_PORT` +- `FREIGHT_BACKOFFICE_PORT` +- `PASSENGER_PORTAL_PORT` +- `PASSENGER_BACKOFFICE_PORT` + +`scripts/deploy/sync-env-from-server.sh` extracts `PORT` from each synced `.env` and exports the corresponding `*_PORT` variable to `GITHUB_ENV`. + +## GitHub Actions Deployment Flow + +Workflow file: `.github/workflows/deploy.yml` + +### 1) `prepare` job + +- Checks out repository once. +- Creates workspace artifact (`workspace.tgz`) and uploads it. + +### 2) `deploy` matrix job (parallel) + +For each service: + +- Downloads and extracts workspace artifact. +- Syncs that service env file from server path. +- Computes branch slug and sets: + - `COMPOSE_PROJECT_NAME=-` +- Creates `.npmrc`/`.npmrc_temp` from `NPM_TOKEN`. +- Runs: + - `docker compose --project-name "$COMPOSE_PROJECT_NAME" build ` + - `docker compose --project-name "$COMPOSE_PROJECT_NAME" up -d ` +- Cleans `.npmrc`/`.npmrc_temp`. + +## Branch/Environment Isolation + +Compose project name is generated as: + +`-` + +Examples: + +- `edr-freight-main` +- `edr-freight-staging` +- `edr-passenger-dev` + +This prevents container/network/volume name collisions between branches. + +## Local Manual Deployment (Optional) + +From repo root: + +```bash +DOCKER_BUILDKIT=1 docker compose build +docker compose up -d +``` + +If private packages are required locally, create `.npmrc`: + +```bash +cat < .npmrc +@tria-plc:registry=https://npm.pkg.github.com +//npm.pkg.github.com/:_authToken= +always-auth=true +EOF +``` + +## Passenger API Startup Behavior + +Passenger container entrypoint runs on startup: + +1. `npm run prisma:generate` +2. `npm run prisma:migrate` (deploy mode) +3. `npm run prisma:seed` +4. starts API process + +## Troubleshooting + +### Missing env file + +Error: + +- `Missing env file: ...` + +Fix: + +- Create the required file in the server env directory for that project/branch slug. + +### Missing PORT in env file + +Error: + +- `Missing required PORT in env file: ...` + +Fix: + +- Add `PORT=` to that service env file. + +### Private package install fails + +Check: + +- `NPM_TOKEN` exists in repo secrets. +- Workflow created `.npmrc` successfully. + +### Prisma seed/migrate failures (passenger) + +Check: + +- `DATABASE_URL` in `passenger-api.env` +- DB reachability from runner host/container network +- migration history consistency + diff --git a/checkpoint.md b/checkpoint.md new file mode 100644 index 000000000..31eb86b2b --- /dev/null +++ b/checkpoint.md @@ -0,0 +1,63 @@ +# Checkpoint + +## Major Tasks Completed + +1. Docker deployment scaffolded for all 6 apps in the monorepo. +2. Split API images into app-specific Dockerfiles: + - `apps/edr-freight-api/Dockerfile` + - `apps/edr-passenger-api/Dockerfile` +3. Kept shared Vite/nginx image: + - `infrastructure/docker/Dockerfile.web` + - `infrastructure/nginx/spa.conf` +4. Updated `docker-compose.yaml` to run all 6 services (apps only, no Postgres service in compose). +5. Added/updated deployment scripts: + - `scripts/deploy/create-npmrc.sh` + - `scripts/deploy/sync-env-from-server.sh` +6. Added self-hosted GitHub Actions deployment workflow: + - Consolidated into one file: `.github/workflows/deploy.yml` +7. Deployment workflow now: + - uses a single checkout (`prepare` job), + - deploys services via parallel matrix, + - sets compose project names per branch/environment, + - passes explicit `docker compose --project-name`. +8. Environment sync script now: + - supports branch slug paths, + - validates each service env file exists, + - requires `PORT` in each env file, + - exports per-service port vars to `GITHUB_ENV`. +9. `docker-compose.yaml` now reads per-service ports via variables exported from env sync. +10. Passenger startup flow fixed to run: + - `prisma:generate`, + - `prisma:migrate`, + - `prisma:seed`, + before API startup. +11. Passenger seed TypeScript issues fixed in `apps/edr-passenger-api/prisma/seed.ts` so it compiles under strict checks. +12. Added deployment runbook: + - `DEPLOYMENT.md` + +## Key Files to Review + +- `.github/workflows/deploy.yml` +- `docker-compose.yaml` +- `scripts/deploy/sync-env-from-server.sh` +- `scripts/deploy/create-npmrc.sh` +- `apps/edr-passenger-api/docker-entrypoint.sh` +- `apps/edr-passenger-api/prisma/seed.ts` +- `DEPLOYMENT.md` + +## Next Actions + +1. Run full CI on all target branches (`main`, `dev`, `staging`) and verify matrix job behavior. +2. Validate server env directory layout matches script expectations: + - `/home//environment/edr///...` +3. Confirm each service env file includes valid `PORT` and service-specific runtime vars. +4. Verify branch-specific compose project names produce isolated containers/networks/volumes on runner. +5. Smoke test all 6 deployed services behind real environment URLs. + +## Open Risks / Notes + +1. Passenger seed runs on every container start; confirm this is desired for production-like environments. +2. Prisma warns about `package.json#prisma` deprecation (Prisma 7 migration pending). +3. Matrix parallelism increases runner load; ensure self-hosted runner capacity is sufficient. +4. Port collisions are prevented by env-driven mapping, but bad env values can still cause runtime conflicts. + From 46400310b38b598cb2215375780aeb76802fa92c Mon Sep 17 00:00:00 2001 From: SennayT Date: Fri, 29 May 2026 14:55:15 +0300 Subject: [PATCH 18/18] change docker version for freight --- apps/edr-freight-api/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/edr-freight-api/Dockerfile b/apps/edr-freight-api/Dockerfile index bd25c8050..b0850737b 100644 --- a/apps/edr-freight-api/Dockerfile +++ b/apps/edr-freight-api/Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:1 # Build from monorepo root: docker build -f apps/edr-freight-api/Dockerfile . -FROM node:22-alpine AS base +FROM node:24.15.0-alpine AS base RUN apk add --no-cache libc6-compat RUN corepack enable WORKDIR /app @@ -25,7 +25,7 @@ FROM base AS deployer COPY --from=builder /app/ . RUN pnpm deploy --filter="@edr/freight-api" --prod --legacy /deploy -FROM node:22-alpine AS runner +FROM node:24.15.0-alpine AS runner RUN apk add --no-cache libc6-compat ENV NODE_ENV=production WORKDIR /app