mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 09:58:12 +00:00
Merge branch 'dev' into alpha
This commit is contained in:
@@ -1,7 +1,14 @@
|
|||||||
**/node_modules
|
**/node_modules
|
||||||
**/dist
|
**/dist
|
||||||
|
**/.turbo
|
||||||
|
**/.git
|
||||||
**/.github
|
**/.github
|
||||||
**/.vscode
|
**/.vscode
|
||||||
**/.git
|
**/.idea
|
||||||
**/.env
|
**/.env
|
||||||
.env
|
**/.env.*
|
||||||
|
!**/.env.example
|
||||||
|
**/coverage
|
||||||
|
**/*.tsbuildinfo
|
||||||
|
**/*.log
|
||||||
|
.DS_Store
|
||||||
|
|||||||
96
.github/workflows/deploy.yaml
vendored
96
.github/workflows/deploy.yaml
vendored
@@ -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 <<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
|
|
||||||
92
.github/workflows/deploy.yml
vendored
Normal file
92
.github/workflows/deploy.yml
vendored
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
name: Deploy Stacks
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- dev
|
||||||
|
- staging
|
||||||
|
paths:
|
||||||
|
- "apps/edr-freight-api/**"
|
||||||
|
- "apps/edr-freight-web/**"
|
||||||
|
- "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.yml"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: deploy-${{ github.ref_name }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
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: ${{ matrix.project }}
|
||||||
|
BRANCH: ${{ github.ref_name }}
|
||||||
|
DEPLOY_USER: tria
|
||||||
|
BUILD_ENV_FILE: ${{ matrix.build_env_file }}
|
||||||
|
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 "${{ matrix.service }}"
|
||||||
|
|
||||||
|
- 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 ${{ matrix.service }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
docker compose --project-name "${COMPOSE_PROJECT_NAME}" build "${{ matrix.service }}"
|
||||||
|
|
||||||
|
- name: Deploy ${{ matrix.service }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
docker compose --project-name "${COMPOSE_PROJECT_NAME}" up -d "${{ matrix.service }}"
|
||||||
|
|
||||||
|
- name: Remove npm credentials from workspace
|
||||||
|
if: always()
|
||||||
|
run: rm -f .npmrc .npmrc_temp
|
||||||
187
DEPLOYMENT.md
Normal file
187
DEPLOYMENT.md
Normal file
@@ -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/<DEPLOY_USER>/environment/edr/<branch-slug>/<project>/`
|
||||||
|
|
||||||
|
Where:
|
||||||
|
|
||||||
|
- `<DEPLOY_USER>` defaults to `tria` (overridable by `DEPLOY_USER`)
|
||||||
|
- `<branch-slug>` is derived from Git branch (lowercase, non-alphanumeric replaced with `-`)
|
||||||
|
- `<project>` 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=<number>`
|
||||||
|
|
||||||
|
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=<project>-<branch-slug>`
|
||||||
|
- Creates `.npmrc`/`.npmrc_temp` from `NPM_TOKEN`.
|
||||||
|
- Runs:
|
||||||
|
- `docker compose --project-name "$COMPOSE_PROJECT_NAME" build <service>`
|
||||||
|
- `docker compose --project-name "$COMPOSE_PROJECT_NAME" up -d <service>`
|
||||||
|
- Cleans `.npmrc`/`.npmrc_temp`.
|
||||||
|
|
||||||
|
## Branch/Environment Isolation
|
||||||
|
|
||||||
|
Compose project name is generated as:
|
||||||
|
|
||||||
|
`<project>-<branch-slug>`
|
||||||
|
|
||||||
|
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 <service>
|
||||||
|
docker compose up -d <service>
|
||||||
|
```
|
||||||
|
|
||||||
|
If private packages are required locally, create `.npmrc`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat <<EOF > .npmrc
|
||||||
|
@tria-plc:registry=https://npm.pkg.github.com
|
||||||
|
//npm.pkg.github.com/:_authToken=<YOUR_TOKEN>
|
||||||
|
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=<number>` 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
|
||||||
|
|
||||||
84
Dockerfile
84
Dockerfile
@@ -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 --filter=\!"@edr/passenger-portal"
|
|
||||||
|
|
||||||
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;"]
|
|
||||||
120
README.md
120
README.md
@@ -170,7 +170,7 @@ pnpm --filter @edr/passenger-api run prisma:generate
|
|||||||
|
|
||||||
#### Run Migrations
|
#### Run Migrations
|
||||||
```bash
|
```bash
|
||||||
pnpm --filter @edr/passenger-api run prisma:migrate
|
pnpm --filter @edr/passenger-api run prisma:migrate:dev
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Seed Database
|
#### Seed Database
|
||||||
@@ -523,59 +523,103 @@ pnpm --filter @edr/passenger-api run type-check # TypeScript check
|
|||||||
|
|
||||||
# Database
|
# Database
|
||||||
pnpm --filter @edr/passenger-api run prisma:generate # Generate Prisma client
|
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
|
pnpm --filter @edr/passenger-api run prisma:seed # Seed database
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🐳 Docker Deployment
|
## 🐳 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
|
```bash
|
||||||
# From monorepo root
|
# 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
|
```bash
|
||||||
docker run -d \
|
DOCKER_BUILDKIT=1 docker compose build
|
||||||
--name edr-api \
|
docker compose up -d
|
||||||
-p 4000:4000 \
|
|
||||||
--env-file apps/edr-passenger-api/.env \
|
|
||||||
edr-passenger-api
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Docker Compose (Recommended)
|
| Service | URL (default) |
|
||||||
```yaml
|
|---------|----------------|
|
||||||
version: '3.8'
|
| Freight API | http://localhost:3001 |
|
||||||
services:
|
| Passenger API | http://localhost:4000 |
|
||||||
postgres:
|
| Freight portal | http://localhost:5173 |
|
||||||
image: postgres:15
|
| Freight backoffice | http://localhost:5183 |
|
||||||
environment:
|
| Passenger portal | http://localhost:5174 |
|
||||||
POSTGRES_USER: edr
|
| Passenger backoffice | http://localhost:5184 |
|
||||||
POSTGRES_PASSWORD: edr_secret
|
|
||||||
POSTGRES_DB: edr_passenger
|
|
||||||
ports:
|
|
||||||
- "5432:5432"
|
|
||||||
volumes:
|
|
||||||
- postgres_data:/var/lib/postgresql/data
|
|
||||||
|
|
||||||
api:
|
### Build a single service
|
||||||
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
|
|
||||||
|
|
||||||
volumes:
|
```bash
|
||||||
postgres_data:
|
docker compose build freight-api
|
||||||
|
docker compose build passenger-portal
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Freight images mount `.npmrc` as a BuildKit secret during `pnpm install`. Passenger web images do not require private packages.
|
||||||
|
|
||||||
|
### `VITE_API_URL` (frontends)
|
||||||
|
|
||||||
|
API URLs are **baked in at image build time** (`import.meta.env.VITE_API_URL`). Defaults in [`docker-compose.yaml`](docker-compose.yaml) use `http://localhost:3001/api` (freight) and `http://localhost:4000` (passenger) for local smoke tests. Override build args for production, e.g.:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose build freight-portal \
|
||||||
|
--build-arg VITE_API_URL=https://freight-api.example.com/api
|
||||||
|
```
|
||||||
|
|
||||||
|
### Migrations
|
||||||
|
|
||||||
|
- **Freight API:** TypeORM migrations are not run on container startup — apply them separately before deploy.
|
||||||
|
- **Passenger API:** On each container start, the entrypoint runs `npm run prisma:migrate` and `npm run prisma:seed` (same `package.json` scripts as `pnpm run`) before starting the server. Ensure `DATABASE_URL` in `.env` points at a reachable Postgres instance.
|
||||||
|
|
||||||
|
For local development, use `pnpm --filter @edr/passenger-api run prisma:migrate:dev` instead of `prisma:migrate`.
|
||||||
|
|
||||||
|
### GitHub Actions (self-hosted runner)
|
||||||
|
|
||||||
|
Two workflows deploy independently on push to `main`, `develop`, or `staging`:
|
||||||
|
|
||||||
|
| Workflow | Services | Server env root |
|
||||||
|
|----------|----------|-----------------|
|
||||||
|
| [`.github/workflows/deploy-freight.yml`](.github/workflows/deploy-freight.yml) | freight-api, freight-portal, freight-backoffice | `/home/user/environmen/edr-freight/<branch>/` |
|
||||||
|
| [`.github/workflows/deploy-passenger.yml`](.github/workflows/deploy-passenger.yml) | passenger-api, passenger-portal, passenger-backoffice | `/home/user/environmen/edr-passenger/<branch>/` |
|
||||||
|
|
||||||
|
**On the runner**, place env files before the first deploy (example for branch `main`):
|
||||||
|
|
||||||
|
```text
|
||||||
|
/home/user/environmen/edr-freight/main/
|
||||||
|
freight-api.env
|
||||||
|
freight-portal.env # optional runtime env for Vite/nginx
|
||||||
|
freight-backoffice.env
|
||||||
|
freight-web.build.env # exports FREIGHT_VITE_API_URL=...
|
||||||
|
|
||||||
|
/home/user/environmen/edr-passenger/main/
|
||||||
|
passenger-api.env
|
||||||
|
passenger-portal.env
|
||||||
|
passenger-backoffice.env
|
||||||
|
passenger-web.build.env # exports PASSENGER_VITE_API_URL=...
|
||||||
|
```
|
||||||
|
|
||||||
|
Example `freight-web.build.env`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export FREIGHT_VITE_API_URL=https://freight-api.example.com/api
|
||||||
|
```
|
||||||
|
|
||||||
|
The workflow copies `*.env` into each app directory, creates `.npmrc` from the `NPM_TOKEN` repository secret, then runs `docker compose build` and `docker compose up -d` for that stack.
|
||||||
|
|
||||||
## 🔒 Security Best Practices
|
## 🔒 Security Best Practices
|
||||||
|
|
||||||
1. **Environment Variables** - Never commit `.env` files. Use secrets management in production.
|
1. **Environment Variables** - Never commit `.env` files. Use secrets management in production.
|
||||||
|
|||||||
@@ -1,17 +1,7 @@
|
|||||||
# App
|
# Copy to .env for local/docker compose (not committed).
|
||||||
NODE_ENV=development
|
|
||||||
PORT=3001
|
PORT=3001
|
||||||
|
|
||||||
# Database
|
|
||||||
DB_HOST=localhost
|
DB_HOST=localhost
|
||||||
DB_PORT=5433
|
DB_PORT=5433
|
||||||
DB_NAME=edr_freight
|
|
||||||
DB_USER=postgres
|
DB_USER=postgres
|
||||||
DB_PASSWORD=
|
DB_PASSWORD=
|
||||||
|
DB_NAME=edr_freight
|
||||||
# JWT (provided by external auth package — placeholder only)
|
|
||||||
JWT_SECRET=
|
|
||||||
|
|
||||||
# Redis
|
|
||||||
REDIS_HOST=localhost
|
|
||||||
REDIS_PORT=6379
|
|
||||||
|
|||||||
37
apps/edr-freight-api/Dockerfile
Normal file
37
apps/edr-freight-api/Dockerfile
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
# Build from monorepo root: docker build -f apps/edr-freight-api/Dockerfile .
|
||||||
|
|
||||||
|
FROM node:24.15.0-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:24.15.0-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"]
|
||||||
51
apps/edr-passenger-api/Dockerfile
Normal file
51
apps/edr-passenger-api/Dockerfile
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# 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:24.15.0-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 \
|
||||||
|
--mount=type=cache,id=pnpm,target=/pnpm/store \
|
||||||
|
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: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
|
||||||
|
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"]
|
||||||
11
apps/edr-passenger-api/docker-entrypoint.sh
Normal file
11
apps/edr-passenger-api/docker-entrypoint.sh
Normal file
@@ -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 "$@"
|
||||||
@@ -44,7 +44,8 @@
|
|||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
"swagger-ui-express": "^5.0.0",
|
"swagger-ui-express": "^5.0.0",
|
||||||
"tsconfig-paths": "^4.2.0"
|
"tsconfig-paths": "^4.2.0",
|
||||||
|
"@prisma/client": "^6.19.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@edr/eslint-config": "workspace:*",
|
"@edr/eslint-config": "workspace:*",
|
||||||
@@ -52,7 +53,6 @@
|
|||||||
"@nestjs/cli": "^11.0.21",
|
"@nestjs/cli": "^11.0.21",
|
||||||
"@nestjs/schematics": "^11.1.0",
|
"@nestjs/schematics": "^11.1.0",
|
||||||
"@nestjs/testing": "^11.1.19",
|
"@nestjs/testing": "^11.1.19",
|
||||||
"@prisma/client": "^6.19.3",
|
|
||||||
"@types/bcrypt": "^5.0.2",
|
"@types/bcrypt": "^5.0.2",
|
||||||
"@types/jest": "^29.5.11",
|
"@types/jest": "^29.5.11",
|
||||||
"@types/node": "^20.10.6",
|
"@types/node": "^20.10.6",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { PrismaClient, SeatKind } from '@prisma/client';
|
import { PrismaClient } from '@prisma/client';
|
||||||
import * as bcrypt from 'bcrypt';
|
import * as bcrypt from 'bcrypt';
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
const prisma = new PrismaClient();
|
||||||
@@ -136,11 +136,11 @@ async function seedCoachesAndSeats(seatClasses: any[]) {
|
|||||||
col,
|
col,
|
||||||
label: `${row}${col}`,
|
label: `${row}${col}`,
|
||||||
seatNumber: `${config.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 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ async function seedSchedules(trains: any[], stations: any[], routes: any[]) {
|
|||||||
const existingScheduleIds = (await prisma.trainSchedule.findMany({
|
const existingScheduleIds = (await prisma.trainSchedule.findMany({
|
||||||
where: { trainId: { in: [train301.id, train302.id, train303.id] } },
|
where: { trainId: { in: [train301.id, train302.id, train303.id] } },
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
})).map((s) => s.id);
|
})).map((s: { id: string }) => s.id);
|
||||||
|
|
||||||
if (existingScheduleIds.length > 0) {
|
if (existingScheduleIds.length > 0) {
|
||||||
// Delete in correct order to avoid foreign key constraints
|
// Delete in correct order to avoid foreign key constraints
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import type { IStation } from "../types";
|
// import type { IStation } from "../types";
|
||||||
import { api } from "../utils/api";
|
import { api } from "../utils/api";
|
||||||
|
import type { Passenger } from "@edr/types";
|
||||||
|
|
||||||
export const stationsService = {
|
export const stationsService = {
|
||||||
list: async (): Promise<IStation[]> => {
|
list: async (): Promise<Passenger.IStation[]> => {
|
||||||
const { data } = await api.get("/stations");
|
const { data } = await api.get("/stations");
|
||||||
return data.data;
|
return data.data;
|
||||||
},
|
},
|
||||||
get: async (id: string): Promise<IStation> => {
|
get: async (id: string): Promise<Passenger.IStation> => {
|
||||||
const { data } = await api.get(`/stations/${id}`);
|
const { data } = await api.get(`/stations/${id}`);
|
||||||
return data.data;
|
return data.data;
|
||||||
},
|
},
|
||||||
|
|||||||
63
checkpoint.md
Normal file
63
checkpoint.md
Normal file
@@ -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/<deploy_user>/environment/edr/<branch-slug>/<project>/...`
|
||||||
|
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.
|
||||||
|
|
||||||
@@ -1,21 +1,80 @@
|
|||||||
|
# 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:
|
services:
|
||||||
freight-api:
|
freight-api:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: ./Dockerfile
|
dockerfile: apps/edr-freight-api/Dockerfile
|
||||||
target: freight-api
|
secrets:
|
||||||
env_file:
|
- npmrc
|
||||||
- .env
|
|
||||||
ports:
|
ports:
|
||||||
- ${PORT}:${PORT}
|
- "${FREIGHT_API_PORT:-3001}:${FREIGHT_API_PORT:-3001}"
|
||||||
restart: unless-stopped
|
env_file:
|
||||||
|
- apps/edr-freight-api/.env
|
||||||
|
|
||||||
passenger-api:
|
passenger-api:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: ./Dockerfile
|
dockerfile: apps/edr-passenger-api/Dockerfile
|
||||||
target: passenger-api
|
|
||||||
env_file:
|
|
||||||
- .env
|
|
||||||
ports:
|
ports:
|
||||||
- ${PORT}:${PORT}
|
- "${PASSENGER_API_PORT:-4000}:${PASSENGER_API_PORT:-4000}"
|
||||||
restart: unless-stopped
|
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:
|
||||||
|
- "${FREIGHT_PORTAL_PORT:-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:
|
||||||
|
- "${FREIGHT_BACKOFFICE_PORT:-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:
|
||||||
|
- "${PASSENGER_PORTAL_PORT:-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:
|
||||||
|
- "${PASSENGER_BACKOFFICE_PORT:-5184}:80"
|
||||||
|
|
||||||
|
secrets:
|
||||||
|
npmrc:
|
||||||
|
file: .npmrc
|
||||||
|
|||||||
38
infrastructure/docker/Dockerfile.web
Normal file
38
infrastructure/docker/Dockerfile.web
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# 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:24.15.0-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 \
|
||||||
|
--mount=type=cache,id=pnpm,target=/pnpm/store \
|
||||||
|
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;"]
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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:4000;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
13
infrastructure/nginx/spa.conf
Normal file
13
infrastructure/nginx/spa.conf
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,8 @@
|
|||||||
"lint": "turbo run lint",
|
"lint": "turbo run lint",
|
||||||
"type-check": "turbo run type-check",
|
"type-check": "turbo run type-check",
|
||||||
"format": "prettier --write \"**/*.{ts,tsx,json,md}\"",
|
"format": "prettier --write \"**/*.{ts,tsx,json,md}\"",
|
||||||
|
"docker:build": "docker compose build",
|
||||||
|
"docker:up": "docker compose up -d",
|
||||||
"prepare": "husky"
|
"prepare": "husky"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
5
pnpm-lock.yaml
generated
5
pnpm-lock.yaml
generated
@@ -62,6 +62,9 @@ importers:
|
|||||||
'@nestjs/swagger':
|
'@nestjs/swagger':
|
||||||
specifier: ^7.4.0
|
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)
|
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':
|
'@sendgrid/mail':
|
||||||
specifier: ^8.1.0
|
specifier: ^8.1.0
|
||||||
version: 8.1.6
|
version: 8.1.6
|
||||||
@@ -116,7 +119,7 @@ importers:
|
|||||||
version: 11.1.0(chokidar@4.0.3)(prettier@3.8.3)(typescript@5.9.3)
|
version: 11.1.0(chokidar@4.0.3)(prettier@3.8.3)(typescript@5.9.3)
|
||||||
'@nestjs/testing':
|
'@nestjs/testing':
|
||||||
specifier: ^11.1.19
|
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/platform-express@11.1.23)
|
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':
|
'@prisma/client':
|
||||||
specifier: ^6.19.3
|
specifier: ^6.19.3
|
||||||
version: 6.19.3(prisma@6.19.3(typescript@5.9.3))(typescript@5.9.3)
|
version: 6.19.3(prisma@6.19.3(typescript@5.9.3))(typescript@5.9.3)
|
||||||
|
|||||||
19
scripts/deploy/create-npmrc.sh
Normal file
19
scripts/deploy/create-npmrc.sh
Normal file
@@ -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 <<EOF > .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"
|
||||||
82
scripts/deploy/sync-env-from-server.sh
Normal file
82
scripts/deploy/sync-env-from-server.sh
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
#!/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/<project>/<branch-slug>/freight-api.env
|
||||||
|
# /home/user/environmen/<project>/<branch-slug>/freight-portal.env
|
||||||
|
# /home/user/environmen/<project>/<branch-slug>/freight-web.build.env (optional, exports VITE_API_URL etc.)
|
||||||
|
|
||||||
|
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/${BRANCH_SLUG}/${PROJECT:?PROJECT 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}"
|
||||||
|
|
||||||
|
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.)
|
||||||
|
# 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
|
||||||
Reference in New Issue
Block a user