mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 12:41:04 +00:00
modify pipeline to speed up deployment
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
|
||||||
|
|||||||
71
.github/workflows/deploy-freight.yml
vendored
Normal file
71
.github/workflows/deploy-freight.yml
vendored
Normal file
@@ -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
|
||||||
71
.github/workflows/deploy-passenger.yml
vendored
Normal file
71
.github/workflows/deploy-passenger.yml
vendored
Normal file
@@ -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
|
||||||
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
|
|
||||||
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 --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;"]
|
|
||||||
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: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"]
|
||||||
@@ -1,96 +1,4 @@
|
|||||||
# App
|
# Copy to .env for local/docker compose (not committed).
|
||||||
NODE_ENV=development
|
|
||||||
PORT=4000
|
PORT=4000
|
||||||
|
DATABASE_URL=postgresql://user:password@host:5432/edr_passenger
|
||||||
# Database (Prisma)
|
JWT_SECRET=change-me-in-production
|
||||||
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=
|
|
||||||
|
|||||||
50
apps/edr-passenger-api/Dockerfile
Normal file
50
apps/edr-passenger-api/Dockerfile
Normal file
@@ -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"]
|
||||||
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 "$@"
|
||||||
@@ -12,7 +12,8 @@
|
|||||||
"test:e2e": "jest --config ./test/jest-e2e.json",
|
"test:e2e": "jest --config ./test/jest-e2e.json",
|
||||||
"type-check": "tsc --noEmit",
|
"type-check": "tsc --noEmit",
|
||||||
"prisma:generate": "prisma generate",
|
"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:seed": "ts-node prisma/seed.ts",
|
||||||
"prisma:backfill": "ts-node prisma/backfill-fields.ts",
|
"prisma:backfill": "ts-node prisma/backfill-fields.ts",
|
||||||
"prisma:verify": "ts-node prisma/verify-backfill.ts"
|
"prisma:verify": "ts-node prisma/verify-backfill.ts"
|
||||||
@@ -42,7 +43,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:*",
|
||||||
@@ -50,7 +52,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 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +161,7 @@ async function seedSchedules(trains: any[], stations: 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) {
|
||||||
await prisma.fareRule.deleteMany({ where: { tripId: { in: existingScheduleIds } } });
|
await prisma.fareRule.deleteMany({ where: { tripId: { in: existingScheduleIds } } });
|
||||||
|
|||||||
@@ -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:
|
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}
|
- "3001:3001"
|
||||||
restart: unless-stopped
|
environment:
|
||||||
|
PORT: "3001"
|
||||||
|
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}
|
- "4000:4000"
|
||||||
restart: unless-stopped
|
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
|
||||||
|
|||||||
37
infrastructure/docker/Dockerfile.web
Normal file
37
infrastructure/docker/Dockerfile.web
Normal file
@@ -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;"]
|
||||||
@@ -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: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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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": {
|
||||||
|
|||||||
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
@@ -305,6 +305,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
|
||||||
@@ -357,9 +360,6 @@ importers:
|
|||||||
'@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/microservices@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':
|
|
||||||
specifier: ^6.19.3
|
|
||||||
version: 6.19.3(prisma@6.19.3(typescript@5.9.3))(typescript@5.9.3)
|
|
||||||
'@types/bcrypt':
|
'@types/bcrypt':
|
||||||
specifier: ^5.0.2
|
specifier: ^5.0.2
|
||||||
version: 5.0.2
|
version: 5.0.2
|
||||||
|
|||||||
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"
|
||||||
68
scripts/deploy/sync-env-from-server.sh
Normal file
68
scripts/deploy/sync-env-from-server.sh
Normal file
@@ -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/<project>/<branch>/freight-api.env
|
||||||
|
# /home/user/environmen/<project>/<branch>/freight-portal.env
|
||||||
|
# /home/user/environmen/<project>/<branch>/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
|
||||||
Reference in New Issue
Block a user