Some checks failed
CI/CD Pipeline - Xpeditis PreProd / Frontend - Build & Test (push) Failing after 5m31s
CI/CD Pipeline - Xpeditis PreProd / Frontend - Docker Build & Push (push) Has been skipped
CI/CD Pipeline - Xpeditis PreProd / Backend - Build & Test (push) Failing after 5m42s
CI/CD Pipeline - Xpeditis PreProd / Backend - Docker Build & Push (push) Has been skipped
CI/CD Pipeline - Xpeditis PreProd / Deploy to PreProd Server (push) Has been skipped
CI/CD Pipeline - Xpeditis PreProd / Run Smoke Tests (push) Has been skipped
308 lines
11 KiB
YAML
308 lines
11 KiB
YAML
version: '3.8'
|
|
|
|
services:
|
|
# PostgreSQL Database
|
|
xpeditis-db:
|
|
image: postgres:15-alpine
|
|
restart: unless-stopped
|
|
volumes:
|
|
- xpeditis_db_data:/var/lib/postgresql/data
|
|
environment:
|
|
POSTGRES_DB: xpeditis_prod
|
|
POSTGRES_USER: xpeditis
|
|
POSTGRES_PASSWORD: xpeditis_prod_password_CHANGE_ME
|
|
PGDATA: /var/lib/postgresql/data/pgdata
|
|
networks:
|
|
- xpeditis_internal
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U xpeditis"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
deploy:
|
|
placement:
|
|
constraints:
|
|
- node.role == manager
|
|
restart_policy:
|
|
condition: on-failure
|
|
delay: 5s
|
|
max_attempts: 3
|
|
|
|
# Redis Cache
|
|
xpeditis-redis:
|
|
image: redis:7-alpine
|
|
restart: unless-stopped
|
|
command: redis-server --requirepass xpeditis_redis_password_CHANGE_ME --appendonly yes
|
|
volumes:
|
|
- xpeditis_redis_data:/data
|
|
networks:
|
|
- xpeditis_internal
|
|
healthcheck:
|
|
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
|
|
interval: 10s
|
|
timeout: 3s
|
|
retries: 5
|
|
deploy:
|
|
placement:
|
|
constraints:
|
|
- node.role == manager
|
|
restart_policy:
|
|
condition: on-failure
|
|
delay: 5s
|
|
max_attempts: 3
|
|
|
|
# MinIO S3 Storage
|
|
xpeditis-minio:
|
|
image: minio/minio:latest
|
|
restart: unless-stopped
|
|
command: server /data --console-address ":9001"
|
|
volumes:
|
|
- xpeditis_minio_data:/data
|
|
environment:
|
|
MINIO_ROOT_USER: minioadmin_CHANGE_ME
|
|
MINIO_ROOT_PASSWORD: minioadmin_password_CHANGE_ME
|
|
networks:
|
|
- xpeditis_internal
|
|
- traefik_network
|
|
labels:
|
|
# MinIO API
|
|
- "traefik.enable=true"
|
|
- "traefik.http.routers.xpeditis-minio-api.rule=Host(`s3.xpeditis.com`)"
|
|
- "traefik.http.routers.xpeditis-minio-api.entrypoints=websecure"
|
|
- "traefik.http.routers.xpeditis-minio-api.tls=true"
|
|
- "traefik.http.routers.xpeditis-minio-api.tls.certresolver=letsencrypt"
|
|
- "traefik.http.routers.xpeditis-minio-api.service=xpeditis-minio-api"
|
|
- "traefik.http.services.xpeditis-minio-api.loadbalancer.server.port=9000"
|
|
# MinIO Console
|
|
- "traefik.http.routers.xpeditis-minio-console.rule=Host(`minio.xpeditis.com`)"
|
|
- "traefik.http.routers.xpeditis-minio-console.entrypoints=websecure"
|
|
- "traefik.http.routers.xpeditis-minio-console.tls=true"
|
|
- "traefik.http.routers.xpeditis-minio-console.tls.certresolver=letsencrypt"
|
|
- "traefik.http.routers.xpeditis-minio-console.service=xpeditis-minio-console"
|
|
- "traefik.http.services.xpeditis-minio-console.loadbalancer.server.port=9001"
|
|
- "traefik.docker.network=traefik_network"
|
|
# HTTP to HTTPS redirect
|
|
- "traefik.http.routers.xpeditis-minio-http.rule=Host(`s3.xpeditis.com`) || Host(`minio.xpeditis.com`)"
|
|
- "traefik.http.routers.xpeditis-minio-http.entrypoints=web"
|
|
- "traefik.http.routers.xpeditis-minio-http.middlewares=xpeditis-redirect"
|
|
- "traefik.http.middlewares.xpeditis-redirect.redirectscheme.scheme=https"
|
|
- "traefik.http.middlewares.xpeditis-redirect.redirectscheme.permanent=true"
|
|
healthcheck:
|
|
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
|
interval: 30s
|
|
timeout: 20s
|
|
retries: 3
|
|
deploy:
|
|
placement:
|
|
constraints:
|
|
- node.role == manager
|
|
restart_policy:
|
|
condition: on-failure
|
|
delay: 5s
|
|
max_attempts: 3
|
|
|
|
# Backend API (NestJS)
|
|
xpeditis-backend:
|
|
image: xpeditis/backend:latest
|
|
restart: unless-stopped
|
|
environment:
|
|
# Node Environment
|
|
NODE_ENV: production
|
|
PORT: 4000
|
|
|
|
# Database
|
|
DATABASE_HOST: xpeditis-db
|
|
DATABASE_PORT: 5432
|
|
DATABASE_USER: xpeditis
|
|
DATABASE_PASSWORD: xpeditis_prod_password_CHANGE_ME
|
|
DATABASE_NAME: xpeditis_prod
|
|
DATABASE_SSL: "false"
|
|
DATABASE_SYNC: "false"
|
|
DATABASE_LOGGING: "false"
|
|
|
|
# Redis
|
|
REDIS_HOST: xpeditis-redis
|
|
REDIS_PORT: 6379
|
|
REDIS_PASSWORD: xpeditis_redis_password_CHANGE_ME
|
|
REDIS_TTL: 900
|
|
|
|
# JWT
|
|
JWT_SECRET: your-super-secret-jwt-key-CHANGE_ME-min-32-characters
|
|
JWT_ACCESS_EXPIRATION: 15m
|
|
JWT_REFRESH_EXPIRATION: 7d
|
|
|
|
# S3/MinIO
|
|
AWS_S3_ENDPOINT: http://xpeditis-minio:9000
|
|
AWS_REGION: us-east-1
|
|
AWS_ACCESS_KEY_ID: minioadmin_CHANGE_ME
|
|
AWS_SECRET_ACCESS_KEY: minioadmin_password_CHANGE_ME
|
|
AWS_S3_BUCKET: xpeditis-documents
|
|
AWS_S3_FORCE_PATH_STYLE: "true"
|
|
|
|
# CORS
|
|
CORS_ORIGIN: https://app.xpeditis.com,https://www.xpeditis.com
|
|
|
|
# Rate Limiting
|
|
RATE_LIMIT_TTL: 60
|
|
RATE_LIMIT_MAX: 100
|
|
|
|
# Email (Placeholder - configure based on your email provider)
|
|
EMAIL_HOST: smtp.example.com
|
|
EMAIL_PORT: 587
|
|
EMAIL_USER: noreply@xpeditis.com
|
|
EMAIL_PASSWORD: email_password_CHANGE_ME
|
|
EMAIL_FROM: "Xpeditis <noreply@xpeditis.com>"
|
|
|
|
# Sentry (Optional - for error tracking)
|
|
SENTRY_DSN: ""
|
|
SENTRY_ENVIRONMENT: production
|
|
SENTRY_TRACES_SAMPLE_RATE: 0.1
|
|
|
|
# App URLs
|
|
FRONTEND_URL: https://app.xpeditis.com
|
|
API_URL: https://api.xpeditis.com
|
|
networks:
|
|
- xpeditis_internal
|
|
- traefik_network
|
|
labels:
|
|
- "traefik.enable=true"
|
|
# API Routes
|
|
- "traefik.http.routers.xpeditis-api.rule=Host(`api.xpeditis.com`)"
|
|
- "traefik.http.routers.xpeditis-api.entrypoints=websecure"
|
|
- "traefik.http.routers.xpeditis-api.tls=true"
|
|
- "traefik.http.routers.xpeditis-api.tls.certresolver=letsencrypt"
|
|
- "traefik.http.routers.xpeditis-api.priority=100"
|
|
- "traefik.http.services.xpeditis-api.loadbalancer.server.port=4000"
|
|
- "traefik.http.routers.xpeditis-api.middlewares=xpeditis-api-headers,xpeditis-api-ratelimit"
|
|
- "traefik.docker.network=traefik_network"
|
|
# Middleware Headers
|
|
- "traefik.http.middlewares.xpeditis-api-headers.headers.customRequestHeaders.X-Forwarded-Proto=https"
|
|
- "traefik.http.middlewares.xpeditis-api-headers.headers.customRequestHeaders.X-Forwarded-For="
|
|
- "traefik.http.middlewares.xpeditis-api-headers.headers.customRequestHeaders.X-Real-IP="
|
|
- "traefik.http.middlewares.xpeditis-api-headers.headers.accessControlAllowOriginList=https://app.xpeditis.com,https://www.xpeditis.com"
|
|
- "traefik.http.middlewares.xpeditis-api-headers.headers.accessControlAllowMethods=GET,POST,PUT,PATCH,DELETE,OPTIONS"
|
|
- "traefik.http.middlewares.xpeditis-api-headers.headers.accessControlAllowHeaders=*"
|
|
- "traefik.http.middlewares.xpeditis-api-headers.headers.accessControlMaxAge=3600"
|
|
# Rate Limiting
|
|
- "traefik.http.middlewares.xpeditis-api-ratelimit.ratelimit.average=100"
|
|
- "traefik.http.middlewares.xpeditis-api-ratelimit.ratelimit.burst=200"
|
|
- "traefik.http.middlewares.xpeditis-api-ratelimit.ratelimit.period=1m"
|
|
# HTTP to HTTPS redirect
|
|
- "traefik.http.routers.xpeditis-api-http.rule=Host(`api.xpeditis.com`)"
|
|
- "traefik.http.routers.xpeditis-api-http.entrypoints=web"
|
|
- "traefik.http.routers.xpeditis-api-http.priority=100"
|
|
- "traefik.http.routers.xpeditis-api-http.middlewares=xpeditis-redirect"
|
|
- "traefik.http.routers.xpeditis-api-http.service=xpeditis-api"
|
|
depends_on:
|
|
- xpeditis-db
|
|
- xpeditis-redis
|
|
- xpeditis-minio
|
|
healthcheck:
|
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:4000/health"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 60s
|
|
deploy:
|
|
replicas: 2
|
|
placement:
|
|
constraints:
|
|
- node.role == manager
|
|
restart_policy:
|
|
condition: on-failure
|
|
delay: 10s
|
|
max_attempts: 3
|
|
update_config:
|
|
parallelism: 1
|
|
delay: 10s
|
|
order: start-first
|
|
resources:
|
|
limits:
|
|
cpus: '1.0'
|
|
memory: 1G
|
|
reservations:
|
|
cpus: '0.5'
|
|
memory: 512M
|
|
|
|
# Frontend (Next.js)
|
|
xpeditis-frontend:
|
|
image: xpeditis/frontend:latest
|
|
restart: unless-stopped
|
|
environment:
|
|
NODE_ENV: production
|
|
NEXT_PUBLIC_API_URL: https://api.xpeditis.com
|
|
NEXT_PUBLIC_WS_URL: wss://api.xpeditis.com
|
|
networks:
|
|
- traefik_network
|
|
labels:
|
|
- "traefik.enable=true"
|
|
# Frontend Routes
|
|
- "traefik.http.routers.xpeditis-app.rule=Host(`app.xpeditis.com`) || Host(`www.xpeditis.com`)"
|
|
- "traefik.http.routers.xpeditis-app.entrypoints=websecure"
|
|
- "traefik.http.routers.xpeditis-app.tls=true"
|
|
- "traefik.http.routers.xpeditis-app.tls.certresolver=letsencrypt"
|
|
- "traefik.http.routers.xpeditis-app.priority=50"
|
|
- "traefik.http.services.xpeditis-app.loadbalancer.server.port=3000"
|
|
- "traefik.http.routers.xpeditis-app.middlewares=xpeditis-app-headers"
|
|
- "traefik.docker.network=traefik_network"
|
|
# Middleware Headers
|
|
- "traefik.http.middlewares.xpeditis-app-headers.headers.customRequestHeaders.X-Forwarded-Proto=https"
|
|
- "traefik.http.middlewares.xpeditis-app-headers.headers.customRequestHeaders.X-Forwarded-For="
|
|
- "traefik.http.middlewares.xpeditis-app-headers.headers.customRequestHeaders.X-Real-IP="
|
|
# Security Headers
|
|
- "traefik.http.middlewares.xpeditis-app-headers.headers.stsSeconds=31536000"
|
|
- "traefik.http.middlewares.xpeditis-app-headers.headers.stsIncludeSubdomains=true"
|
|
- "traefik.http.middlewares.xpeditis-app-headers.headers.stsPreload=true"
|
|
- "traefik.http.middlewares.xpeditis-app-headers.headers.forceSTSHeader=true"
|
|
- "traefik.http.middlewares.xpeditis-app-headers.headers.contentTypeNosniff=true"
|
|
- "traefik.http.middlewares.xpeditis-app-headers.headers.browserXssFilter=true"
|
|
# HTTP to HTTPS redirect
|
|
- "traefik.http.routers.xpeditis-app-http.rule=Host(`app.xpeditis.com`) || Host(`www.xpeditis.com`)"
|
|
- "traefik.http.routers.xpeditis-app-http.entrypoints=web"
|
|
- "traefik.http.routers.xpeditis-app-http.priority=50"
|
|
- "traefik.http.routers.xpeditis-app-http.middlewares=xpeditis-redirect"
|
|
- "traefik.http.routers.xpeditis-app-http.service=xpeditis-app"
|
|
depends_on:
|
|
- xpeditis-backend
|
|
healthcheck:
|
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 40s
|
|
deploy:
|
|
replicas: 2
|
|
placement:
|
|
constraints:
|
|
- node.role == manager
|
|
restart_policy:
|
|
condition: on-failure
|
|
delay: 10s
|
|
max_attempts: 3
|
|
update_config:
|
|
parallelism: 1
|
|
delay: 10s
|
|
order: start-first
|
|
resources:
|
|
limits:
|
|
cpus: '0.5'
|
|
memory: 512M
|
|
reservations:
|
|
cpus: '0.25'
|
|
memory: 256M
|
|
|
|
volumes:
|
|
xpeditis_db_data:
|
|
driver: local
|
|
xpeditis_redis_data:
|
|
driver: local
|
|
xpeditis_minio_data:
|
|
driver: local
|
|
|
|
networks:
|
|
traefik_network:
|
|
external: true
|
|
xpeditis_internal:
|
|
driver: overlay
|
|
internal: true
|