mirror of
http://172.20.10.11:3000/gitadmin/INSIGHT-MVP.git
synced 2026-06-24 21:16:40 +02:00
Backend-driven Authorization Code Flow with @azure/msal-node: - EntraIdService: MSAL ConfidentialClientApplication, auth URL generation, token exchange - SsoController: /auth/sso/microsoft (initiate) + /auth/sso/microsoft/callback (callback) - AuthService.loginViaSso(): User provisioning (find by OID, auto-link by email, or create new) - CSRF protection via state parameter stored in Redis - SSO status endpoint for frontend feature detection Frontend: - "Mit Microsoft anmelden" button on login page (shown only when SSO is configured) - SsoCallbackPage: handles redirect from backend, sets token, loads user profile - AuthContext.loginWithToken(): new method for SSO token handling Configuration: - AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_REDIRECT_URI env vars - docker-compose.yml updated to pass Azure vars to core service Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
309 lines
10 KiB
YAML
309 lines
10 KiB
YAML
# ============================================================
|
|
# INSIGHT MVP - Docker Compose (Basis-Services)
|
|
# ============================================================
|
|
# Startet alle Kerndienste der INSIGHT-Plattform.
|
|
# Observability-Stack separat: docker-compose.observability.yml
|
|
#
|
|
# Nutzung:
|
|
# docker compose up -d
|
|
# docker compose logs -f core
|
|
# docker compose ps
|
|
# ============================================================
|
|
|
|
networks:
|
|
insight-web:
|
|
driver: bridge
|
|
name: insight-web
|
|
insight-db:
|
|
driver: bridge
|
|
name: insight-db
|
|
internal: true
|
|
insight-cache:
|
|
driver: bridge
|
|
name: insight-cache
|
|
internal: true
|
|
|
|
volumes:
|
|
postgres-data:
|
|
name: insight-postgres-data
|
|
redis-data:
|
|
name: insight-redis-data
|
|
step-ca-data:
|
|
name: insight-step-ca-data
|
|
traefik-certs:
|
|
name: insight-traefik-certs
|
|
|
|
services:
|
|
# --------------------------------------------------------
|
|
# Traefik - API Gateway / Reverse Proxy
|
|
# --------------------------------------------------------
|
|
traefik:
|
|
image: traefik:3
|
|
container_name: insight-traefik
|
|
restart: unless-stopped
|
|
command:
|
|
# API & Dashboard
|
|
- "--api.dashboard=true"
|
|
- "--api.insecure=true"
|
|
# Entrypoints (nur HTTP fuer Alpha/Dev mit IP-Zugang)
|
|
- "--entrypoints.web.address=:80"
|
|
# Docker Provider
|
|
- "--providers.docker=true"
|
|
- "--providers.docker.exposedbydefault=false"
|
|
- "--providers.docker.network=insight-web"
|
|
# File Provider (fuer dynamische Konfiguration)
|
|
- "--providers.file.directory=/etc/traefik/dynamic"
|
|
- "--providers.file.watch=true"
|
|
# Logging
|
|
- "--log.level=INFO"
|
|
- "--accesslog=true"
|
|
- "--accesslog.format=json"
|
|
# Ping (fuer Healthcheck)
|
|
- "--ping=true"
|
|
# Metrics fuer Prometheus
|
|
- "--metrics.prometheus=true"
|
|
- "--metrics.prometheus.entryPoint=metrics"
|
|
- "--entrypoints.metrics.address=:8082"
|
|
ports:
|
|
- "80:80"
|
|
- "8080:8080" # Dashboard (nur intern)
|
|
volumes:
|
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
- ./config/traefik/dynamic:/etc/traefik/dynamic:ro
|
|
- traefik-certs:/certs
|
|
networks:
|
|
- insight-web
|
|
labels:
|
|
- "traefik.enable=true"
|
|
# Dashboard Route (nur intern, via Port 8080)
|
|
- "traefik.http.routers.dashboard.rule=Host(`172.20.10.59`) && PathPrefix(`/dashboard`)"
|
|
- "traefik.http.routers.dashboard.service=api@internal"
|
|
- "traefik.http.routers.dashboard.entrypoints=web"
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/ping || exit 1"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
|
|
# --------------------------------------------------------
|
|
# PostgreSQL - Datenbank
|
|
# --------------------------------------------------------
|
|
postgres:
|
|
image: postgres:16-alpine
|
|
container_name: insight-postgres
|
|
restart: unless-stopped
|
|
environment:
|
|
POSTGRES_USER: ${DB_USER:-insight}
|
|
POSTGRES_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD muss gesetzt sein}
|
|
POSTGRES_DB: ${DB_NAME:-platform_core}
|
|
# Performance-Tuning fuer 8GB RAM VM
|
|
POSTGRES_INITDB_ARGS: "--data-checksums"
|
|
volumes:
|
|
- postgres-data:/var/lib/postgresql/data
|
|
- ./config/postgres/init:/docker-entrypoint-initdb.d:ro
|
|
networks:
|
|
- insight-db
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-insight} -d ${DB_NAME:-platform_core}"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 30s
|
|
# Performance-Tuning via Command
|
|
command:
|
|
- "postgres"
|
|
- "-c"
|
|
- "shared_buffers=1GB"
|
|
- "-c"
|
|
- "effective_cache_size=4GB"
|
|
- "-c"
|
|
- "work_mem=16MB"
|
|
- "-c"
|
|
- "maintenance_work_mem=256MB"
|
|
- "-c"
|
|
- "max_connections=200"
|
|
- "-c"
|
|
- "log_min_duration_statement=500"
|
|
- "-c"
|
|
- "log_statement=ddl"
|
|
|
|
# --------------------------------------------------------
|
|
# PgBouncer - Connection Pooling
|
|
# --------------------------------------------------------
|
|
pgbouncer:
|
|
image: edoburu/pgbouncer:latest
|
|
container_name: insight-pgbouncer
|
|
restart: unless-stopped
|
|
environment:
|
|
DATABASE_URL: "postgres://${DB_USER:-insight}:${DB_PASSWORD}@postgres:5432/${DB_NAME:-platform_core}"
|
|
POOL_MODE: transaction
|
|
MAX_CLIENT_CONN: 500
|
|
DEFAULT_POOL_SIZE: 25
|
|
MIN_POOL_SIZE: 5
|
|
RESERVE_POOL_SIZE: 5
|
|
SERVER_RESET_QUERY: "DISCARD ALL"
|
|
SERVER_CHECK_DELAY: 30
|
|
SERVER_CHECK_QUERY: "SELECT 1"
|
|
AUTH_TYPE: scram-sha-256
|
|
networks:
|
|
- insight-db
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -p 5432"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 3
|
|
|
|
# --------------------------------------------------------
|
|
# Redis - Cache, Sessions, Event Bus
|
|
# --------------------------------------------------------
|
|
redis:
|
|
image: redis:7-alpine
|
|
container_name: insight-redis
|
|
restart: unless-stopped
|
|
command: >
|
|
redis-server
|
|
--requirepass ${REDIS_PASSWORD:-}
|
|
--maxmemory 512mb
|
|
--maxmemory-policy allkeys-lru
|
|
--appendonly yes
|
|
--appendfsync everysec
|
|
--save 60 1000
|
|
--save 300 100
|
|
volumes:
|
|
- redis-data:/data
|
|
networks:
|
|
- insight-cache
|
|
healthcheck:
|
|
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD:-}", "ping"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 3
|
|
|
|
# --------------------------------------------------------
|
|
# step-ca - Interne Certificate Authority (mTLS)
|
|
# --------------------------------------------------------
|
|
step-ca:
|
|
image: smallstep/step-ca:latest
|
|
container_name: insight-step-ca
|
|
restart: unless-stopped
|
|
environment:
|
|
DOCKER_STEPCA_INIT_NAME: "INSIGHT Internal CA"
|
|
DOCKER_STEPCA_INIT_DNS_NAMES: "step-ca,localhost"
|
|
DOCKER_STEPCA_INIT_REMOTE_MANAGEMENT: "true"
|
|
DOCKER_STEPCA_INIT_ACME: "true"
|
|
volumes:
|
|
- step-ca-data:/home/step
|
|
networks:
|
|
- insight-web
|
|
- insight-db
|
|
- insight-cache
|
|
healthcheck:
|
|
test: ["CMD", "step", "ca", "health"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 30s
|
|
|
|
# --------------------------------------------------------
|
|
# Core-Service - NestJS Backend
|
|
# --------------------------------------------------------
|
|
core:
|
|
build:
|
|
context: ./packages/core-service
|
|
dockerfile: Dockerfile
|
|
target: development
|
|
container_name: insight-core
|
|
restart: unless-stopped
|
|
environment:
|
|
NODE_ENV: ${NODE_ENV:-development}
|
|
APP_PORT: ${APP_PORT:-3000}
|
|
APP_URL: ${APP_URL:-http://172.20.10.59}
|
|
FRONTEND_URL: ${FRONTEND_URL:-http://172.20.10.59}
|
|
LOG_LEVEL: ${LOG_LEVEL:-info}
|
|
# Database (via PgBouncer)
|
|
DATABASE_URL: "postgresql://${DB_USER:-insight}:${DB_PASSWORD}@pgbouncer:5432/${DB_NAME:-platform_core}"
|
|
# Database (direkt fuer Migrations)
|
|
DATABASE_URL_DIRECT: "postgresql://${DB_USER:-insight}:${DB_PASSWORD}@postgres:5432/${DB_NAME:-platform_core}"
|
|
# Redis
|
|
REDIS_HOST: redis
|
|
REDIS_PORT: 6379
|
|
REDIS_PASSWORD: ${REDIS_PASSWORD:-}
|
|
# JWT
|
|
JWT_PRIVATE_KEY_PATH: ${JWT_PRIVATE_KEY_PATH:-/app/keys/jwt-private.pem}
|
|
JWT_PUBLIC_KEY_PATH: ${JWT_PUBLIC_KEY_PATH:-/app/keys/jwt-public.pem}
|
|
JWT_ACCESS_TOKEN_EXPIRY: ${JWT_ACCESS_TOKEN_EXPIRY:-15m}
|
|
JWT_REFRESH_TOKEN_EXPIRY: ${JWT_REFRESH_TOKEN_EXPIRY:-7d}
|
|
JWT_ISSUER: ${JWT_ISSUER:-insight-platform}
|
|
# Bcrypt
|
|
BCRYPT_COST: ${BCRYPT_COST:-12}
|
|
# CORS
|
|
CORS_ORIGINS: ${CORS_ORIGINS:-http://172.20.10.59}
|
|
# Microsoft Entra ID (Azure AD) SSO
|
|
AZURE_TENANT_ID: ${AZURE_TENANT_ID:-}
|
|
AZURE_CLIENT_ID: ${AZURE_CLIENT_ID:-}
|
|
AZURE_CLIENT_SECRET: ${AZURE_CLIENT_SECRET:-}
|
|
AZURE_REDIRECT_URI: ${AZURE_REDIRECT_URI:-}
|
|
# Rate Limiting
|
|
THROTTLE_TTL: ${THROTTLE_TTL:-60000}
|
|
THROTTLE_LIMIT: ${THROTTLE_LIMIT:-200}
|
|
volumes:
|
|
- ./keys:/app/keys:ro
|
|
networks:
|
|
- insight-web
|
|
- insight-db
|
|
- insight-cache
|
|
depends_on:
|
|
pgbouncer:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_healthy
|
|
labels:
|
|
- "traefik.enable=true"
|
|
# API Routing
|
|
- "traefik.http.routers.core-api.rule=Host(`172.20.10.59`) && PathPrefix(`/api`)"
|
|
- "traefik.http.routers.core-api.entrypoints=web"
|
|
- "traefik.http.routers.core-api.service=core-api"
|
|
- "traefik.http.services.core-api.loadbalancer.server.port=3000"
|
|
# Health-Endpunkt (ohne Auth)
|
|
- "traefik.http.routers.core-health.rule=Host(`172.20.10.59`) && Path(`/health`)"
|
|
- "traefik.http.routers.core-health.entrypoints=web"
|
|
- "traefik.http.routers.core-health.service=core-api"
|
|
# Rate Limiting Middleware
|
|
- "traefik.http.middlewares.api-ratelimit.ratelimit.average=100"
|
|
- "traefik.http.middlewares.api-ratelimit.ratelimit.burst=50"
|
|
- "traefik.http.routers.core-api.middlewares=api-ratelimit"
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "wget -qO- http://localhost:3000/health || exit 1"]
|
|
interval: 15s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 30s
|
|
|
|
# --------------------------------------------------------
|
|
# Frontend - React App (via Nginx)
|
|
# --------------------------------------------------------
|
|
frontend:
|
|
build:
|
|
context: ./packages/frontend
|
|
dockerfile: Dockerfile
|
|
target: development
|
|
container_name: insight-frontend
|
|
restart: unless-stopped
|
|
networks:
|
|
- insight-web
|
|
labels:
|
|
- "traefik.enable=true"
|
|
# Frontend Routing (Catch-All nach API)
|
|
- "traefik.http.routers.frontend.rule=Host(`172.20.10.59`)"
|
|
- "traefik.http.routers.frontend.entrypoints=web"
|
|
- "traefik.http.routers.frontend.service=frontend"
|
|
- "traefik.http.routers.frontend.priority=1"
|
|
- "traefik.http.services.frontend.loadbalancer.server.port=8080"
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/ || exit 1"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|