version: '3.8'

services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
      # Production security settings
      POSTGRES_INITDB_ARGS: "--auth-host=scram-sha-256 --auth-local=scram-sha-256"
    ports:
      # Only expose internally, not to host
      - "5432"
    volumes:
      - postgres-data:/var/lib/postgresql/data
      - ./docker/services/postgres:/docker-entrypoint-initdb.d
      # TLS certificates for PostgreSQL
      - ./config/ssl/postgres:/var/lib/postgresql/ssl:ro
    networks:
      - meldestelle-network
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 20s
    restart: unless-stopped
    # Security: Run as non-root user
    user: postgres
    # Resource limits
    deploy:
      resources:
        limits:
          memory: 1G
          cpus: '0.5'
        reservations:
          memory: 512M
          cpus: '0.25'

  redis:
    image: redis:7-alpine
    environment:
      # Redis with authentication
      REDIS_PASSWORD: ${REDIS_PASSWORD}
    ports:
      # Only expose internally
      - "6379"
    volumes:
      - redis-data:/data
      - ./config/redis/redis.conf:/usr/local/etc/redis/redis.conf:ro
      # TLS certificates for Redis
      - ./config/ssl/redis:/tls:ro
    command: >
      redis-server /usr/local/etc/redis/redis.conf
      --requirepass ${REDIS_PASSWORD}
      --appendonly yes
      --appendfsync everysec
      --save 900 1
      --save 300 10
      --save 60 10000
      --maxmemory 256mb
      --maxmemory-policy allkeys-lru
      --tcp-keepalive 300
      --timeout 0
      --tcp-backlog 511
      --databases 16
      --stop-writes-on-bgsave-error yes
      --rdbcompression yes
      --rdbchecksum yes
      --dir /data
    networks:
      - meldestelle-network
    healthcheck:
      test: ["CMD", "redis-cli", "--no-auth-warning", "-a", "${REDIS_PASSWORD}", "ping"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 10s
    restart: unless-stopped
    # Security: Run as non-root user
    user: redis
    # Resource limits
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '0.25'
        reservations:
          memory: 256M
          cpus: '0.1'

  keycloak:
    image: quay.io/keycloak/keycloak:23.0
    environment:
      KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN}
      KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
      KC_DB: ${KC_DB}
      KC_DB_URL: ${KC_DB_URL}
      KC_DB_USERNAME: ${KC_DB_USERNAME}
      KC_DB_PASSWORD: ${KC_DB_PASSWORD}
      # Production settings
      KC_HOSTNAME: ${KC_HOSTNAME}
      KC_HOSTNAME_STRICT: true
      KC_HOSTNAME_STRICT_HTTPS: true
      KC_HTTP_ENABLED: false
      KC_HTTPS_PORT: 8443
      KC_HTTPS_CERTIFICATE_FILE: /opt/keycloak/conf/server.crt.pem
      KC_HTTPS_CERTIFICATE_KEY_FILE: /opt/keycloak/conf/server.key.pem
      KC_PROXY: edge
      KC_LOG_LEVEL: WARN
      KC_METRICS_ENABLED: true
      KC_HEALTH_ENABLED: true
      # Security headers
      KC_HTTP_RELATIVE_PATH: /auth
    ports:
      # HTTPS only in production
      - "8443:8443"
    depends_on:
      postgres:
        condition: service_healthy
    volumes:
      - ./docker/services/keycloak:/opt/keycloak/data/import
      # TLS certificates
      - ./config/ssl/keycloak:/opt/keycloak/conf:ro
    command: start --import-realm --optimized
    networks:
      - meldestelle-network
    healthcheck:
      test: ["CMD", "curl", "--fail", "--insecure", "https://localhost:8443/auth/health/ready"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 60s
    restart: unless-stopped
    # Resource limits
    deploy:
      resources:
        limits:
          memory: 1G
          cpus: '0.5'
        reservations:
          memory: 512M
          cpus: '0.25'

  zookeeper:
    image: confluentinc/cp-zookeeper:7.5.0
    environment:
      ZOOKEEPER_CLIENT_PORT: ${ZOOKEEPER_CLIENT_PORT}
      ZOOKEEPER_TICK_TIME: 2000
      ZOOKEEPER_SYNC_LIMIT: 2
      ZOOKEEPER_INIT_LIMIT: 5
      ZOOKEEPER_MAX_CLIENT_CNXNS: 60
      ZOOKEEPER_AUTOPURGE_SNAP_RETAIN_COUNT: 3
      ZOOKEEPER_AUTOPURGE_PURGE_INTERVAL: 24
      # Security settings
      ZOOKEEPER_AUTH_PROVIDER_SASL: org.apache.zookeeper.server.auth.SASLAuthenticationProvider
      ZOOKEEPER_REQUIRE_CLIENT_AUTH_SCHEME: sasl
      KAFKA_OPTS: "-Djava.security.auth.login.config=/etc/kafka/secrets/zookeeper_jaas.conf"
    ports:
      # Only expose internally
      - "2181"
    volumes:
      - zookeeper-data:/var/lib/zookeeper/data
      - zookeeper-logs:/var/lib/zookeeper/log
      - ./config/kafka/secrets:/etc/kafka/secrets:ro
    networks:
      - meldestelle-network
    healthcheck:
      test: ["CMD", "nc", "-z", "localhost", "2181"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 10s
    restart: unless-stopped
    # Resource limits
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '0.25'
        reservations:
          memory: 256M
          cpus: '0.1'

  kafka:
    image: confluentinc/cp-kafka:7.5.0
    depends_on:
      zookeeper:
        condition: service_healthy
    ports:
      # Only expose internally
      - "9092"
      - "9093"
    environment:
      KAFKA_BROKER_ID: ${KAFKA_BROKER_ID}
      KAFKA_ZOOKEEPER_CONNECT: ${KAFKA_ZOOKEEPER_CONNECT}
      # Production security settings with SASL/SSL
      KAFKA_ADVERTISED_LISTENERS: SASL_SSL://kafka:9093,SASL_PLAINTEXT://kafka:9092
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: SASL_SSL:SASL_SSL,SASL_PLAINTEXT:SASL_PLAINTEXT
      KAFKA_INTER_BROKER_LISTENER_NAME: SASL_SSL
      KAFKA_SECURITY_INTER_BROKER_PROTOCOL: SASL_SSL
      KAFKA_SASL_MECHANISM_INTER_BROKER_PROTOCOL: PLAIN
      KAFKA_SASL_ENABLED_MECHANISMS: PLAIN
      # SSL Configuration
      KAFKA_SSL_KEYSTORE_FILENAME: kafka.server.keystore.jks
      KAFKA_SSL_KEYSTORE_CREDENTIALS: kafka_keystore_creds
      KAFKA_SSL_KEY_CREDENTIALS: kafka_ssl_key_creds
      KAFKA_SSL_TRUSTSTORE_FILENAME: kafka.server.truststore.jks
      KAFKA_SSL_TRUSTSTORE_CREDENTIALS: kafka_truststore_creds
      KAFKA_SSL_CLIENT_AUTH: required
      # Performance and reliability settings
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 3
      KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 3
      KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 2
      KAFKA_DEFAULT_REPLICATION_FACTOR: 3
      KAFKA_MIN_INSYNC_REPLICAS: 2
      KAFKA_NUM_PARTITIONS: 3
      KAFKA_LOG_RETENTION_HOURS: 168
      KAFKA_LOG_SEGMENT_BYTES: 1073741824
      KAFKA_LOG_RETENTION_CHECK_INTERVAL_MS: 300000
      KAFKA_AUTO_CREATE_TOPICS_ENABLE: false
      # JVM settings
      KAFKA_HEAP_OPTS: "-Xmx512M -Xms512M"
      KAFKA_JVM_PERFORMANCE_OPTS: "-server -XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35"
      # Security
      KAFKA_OPTS: "-Djava.security.auth.login.config=/etc/kafka/secrets/kafka_jaas.conf"
    volumes:
      - kafka-data:/var/lib/kafka/data
      - ./config/kafka/secrets:/etc/kafka/secrets:ro
    networks:
      - meldestelle-network
    healthcheck:
      test: ["CMD", "kafka-topics", "--bootstrap-server", "localhost:9092", "--list"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 30s
    restart: unless-stopped
    # Resource limits
    deploy:
      resources:
        limits:
          memory: 1G
          cpus: '0.5'
        reservations:
          memory: 512M
          cpus: '0.25'

  zipkin:
    image: openzipkin/zipkin:2
    ports:
      # Only expose internally
      - "9411"
    environment:
      # Production settings
      JAVA_OPTS: "-Xms256m -Xmx512m"
      STORAGE_TYPE: elasticsearch
      ES_HOSTS: http://elasticsearch:9200
    networks:
      - meldestelle-network
    healthcheck:
      test: ["CMD", "wget", "-q", "-O", "-", "http://localhost:9411/health"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 15s
    restart: unless-stopped
    # Resource limits
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '0.25'
        reservations:
          memory: 256M
          cpus: '0.1'

  # Production monitoring services
  prometheus:
    image: prom/prometheus:v2.48.1
    volumes:
      - ./config/monitoring/prometheus.prod.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus-data:/prometheus
      # TLS certificates
      - ./config/ssl/prometheus:/etc/ssl/prometheus:ro
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--storage.tsdb.retention.time=30d'
      - '--storage.tsdb.retention.size=10GB'
      - '--web.console.libraries=/etc/prometheus/console_libraries'
      - '--web.console.templates=/etc/prometheus/consoles'
      - '--web.enable-lifecycle'
      - '--web.enable-admin-api'
      - '--web.external-url=https://${PROMETHEUS_HOSTNAME}'
      - '--web.config.file=/etc/ssl/prometheus/web.yml'
    ports:
      # Only expose internally
      - "9090"
    networks:
      - meldestelle-network
    healthcheck:
      test: ["CMD", "wget", "-q", "-O", "-", "http://localhost:9090/-/healthy"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 15s
    restart: unless-stopped
    # Security: Run as non-root user
    user: "65534:65534"
    # Resource limits
    deploy:
      resources:
        limits:
          memory: 1G
          cpus: '0.5'
        reservations:
          memory: 512M
          cpus: '0.25'

  grafana:
    image: grafana/grafana:10.2.3
    volumes:
      - ./config/monitoring/grafana/provisioning:/etc/grafana/provisioning:ro
      - ./config/monitoring/grafana/dashboards:/var/lib/grafana/dashboards:ro
      - grafana-data:/var/lib/grafana
      # TLS certificates
      - ./config/ssl/grafana:/etc/ssl/grafana:ro
    environment:
      # Security settings
      - GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER}
      - GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD}
      - GF_USERS_ALLOW_SIGN_UP=false
      - GF_USERS_ALLOW_ORG_CREATE=false
      - GF_AUTH_ANONYMOUS_ENABLED=false
      # HTTPS settings
      - GF_SERVER_PROTOCOL=https
      - GF_SERVER_CERT_FILE=/etc/ssl/grafana/server.crt
      - GF_SERVER_CERT_KEY=/etc/ssl/grafana/server.key
      - GF_SERVER_DOMAIN=${GRAFANA_HOSTNAME}
      - GF_SERVER_ROOT_URL=https://${GRAFANA_HOSTNAME}
      # Security headers
      - GF_SECURITY_STRICT_TRANSPORT_SECURITY=true
      - GF_SECURITY_STRICT_TRANSPORT_SECURITY_MAX_AGE_SECONDS=31536000
      - GF_SECURITY_CONTENT_TYPE_PROTECTION=true
      - GF_SECURITY_X_CONTENT_TYPE_OPTIONS=nosniff
      - GF_SECURITY_X_XSS_PROTECTION=true
      # Session settings
      - GF_SESSION_PROVIDER=redis
      - GF_SESSION_PROVIDER_CONFIG=addr=redis:6379,pool_size=100,db=2,password=${REDIS_PASSWORD}
      - GF_SESSION_COOKIE_SECURE=true
      - GF_SESSION_COOKIE_SAMESITE=strict
      # Logging
      - GF_LOG_MODE=console
      - GF_LOG_LEVEL=warn
    ports:
      # HTTPS only
      - "3443:3000"
    networks:
      - meldestelle-network
    depends_on:
      prometheus:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "wget", "-q", "-O", "-", "--no-check-certificate", "https://localhost:3000/api/health"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 20s
    restart: unless-stopped
    # Security: Run as non-root user
    user: "472:472"
    # Resource limits
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '0.25'
        reservations:
          memory: 256M
          cpus: '0.1'

  # Reverse proxy for production
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./config/nginx/nginx.prod.conf:/etc/nginx/nginx.conf:ro
      - ./config/nginx/conf.d:/etc/nginx/conf.d:ro
      - ./config/ssl/nginx:/etc/ssl/nginx:ro
      - ./logs/nginx:/var/log/nginx
    networks:
      - meldestelle-network
    depends_on:
      - keycloak
      - grafana
    healthcheck:
      test: ["CMD", "wget", "-q", "-O", "-", "http://localhost/health"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 10s
    restart: unless-stopped
    # Resource limits
    deploy:
      resources:
        limits:
          memory: 256M
          cpus: '0.1'
        reservations:
          memory: 128M
          cpus: '0.05'

volumes:
  postgres-data:
    driver: local
  redis-data:
    driver: local
  kafka-data:
    driver: local
  zookeeper-data:
    driver: local
  zookeeper-logs:
    driver: local
  prometheus-data:
    driver: local
  grafana-data:
    driver: local

networks:
  meldestelle-network:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/16
