# MeshCore Stats - Docker Compose Configuration
#
# Production deployment using published container image.
# For local development, use: docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build
#
# Prerequisites:
# 1. Copy meshcore.conf.example to meshcore.conf and configure
# 2. For serial transport: Create docker-compose.override.yml with your device (see README)
# 3. Ensure your user has access to the serial device (dialout group)
# 4. Create data directories with correct ownership:
#    mkdir -p ./data/state ./out && sudo chown -R 1000:1000 ./data ./out

services:
  # ==========================================================================
  # MeshCore Stats - Data collection and rendering
  # ==========================================================================
  meshcore-stats:
    image: ghcr.io/jorijn/meshcore-stats:0.2.12@sha256:82ca2230abba7d8846315a4fa09f1a2407695caf73fef029e6e6ea83c60c4290 # x-release-please-version
    container_name: meshcore-stats
    restart: unless-stopped

    # Load configuration from meshcore.conf
    env_file:
      - meshcore.conf

    # NOTE: Serial device must be added via docker-compose.override.yml
    # See README.md for examples. TCP transport users don't need devices.

    volumes:
      # Persistent storage for SQLite database and circuit breaker state
      - ./data/state:/data/state
      # Generated static site (served by nginx)
      - ./out:/out

    # Run as meshmon user (UID 1000)
    user: "1000:1000"

    # Add dialout group for serial port access
    group_add:
      - dialout

    # Security hardening
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    read_only: true
    tmpfs:
      - /tmp:noexec,nosuid,size=64m
      - /var/cache/fontconfig:noexec,nosuid,size=4m

    # Resource limits
    deploy:
      resources:
        limits:
          cpus: "1.0"
          memory: 512M
        reservations:
          cpus: "0.1"
          memory: 128M

    # Logging limits to prevent disk exhaustion
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

    # Health check
    healthcheck:
      test: ["CMD", "python", "-c", "import sqlite3; sqlite3.connect('/data/state/metrics.db').execute('SELECT 1')"]
      interval: 5m
      timeout: 30s
      start_period: 60s
      retries: 3

  # ==========================================================================
  # nginx - Static site server
  # ==========================================================================
  nginx:
    image: nginx:1.29-alpine@sha256:8491795299c8e739b7fcc6285d531d9812ce2666e07bd3dd8db00020ad132295
    container_name: meshcore-stats-nginx
    restart: unless-stopped

    # Run as nginx user (UID 101 in Alpine nginx image)
    user: "101:101"

    ports:
      - "8080:8080"

    volumes:
      # Mount generated static site from meshcore-stats container
      - ./out:/usr/share/nginx/html:ro
      # Custom nginx configuration
      - ./docker/nginx.conf:/etc/nginx/conf.d/default.conf:ro

    # Security hardening
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    # NET_BIND_SERVICE not needed for port 8080 (unprivileged)
    read_only: true
    tmpfs:
      - /var/cache/nginx:noexec,nosuid,size=16m,uid=101,gid=101
      - /var/run:noexec,nosuid,size=1m,uid=101,gid=101

    # Resource limits
    deploy:
      resources:
        limits:
          cpus: "0.5"
          memory: 64M
        reservations:
          cpus: "0.05"
          memory: 16M

    # Logging limits
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

    # Health check
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      start_period: 5s
      retries: 3

    depends_on:
      meshcore-stats:
        condition: service_healthy
