# ==============================================
# LLMHosts.com -- Production Docker Compose
# ==============================================
# Usage:
#   docker compose -f docker-compose.prod.yml up -d          # Start all services
#   docker compose -f docker-compose.prod.yml logs -f        # Follow logs
#   docker compose -f docker-compose.prod.yml ps             # Check health
#   docker compose -f docker-compose.prod.yml down           # Stop all
#
# GPU support:
#   Uncomment the `runtime` and NVIDIA env vars below, or use an override file:
#   docker compose -f docker-compose.prod.yml -f docker-compose.gpu.yml up -d
#
# External proxy (Pangolin/Traefik already running):
#   docker compose -f docker-compose.prod.yml -f docker-compose.external-proxy.yml up -d
#
# Environment:
#   DOMAIN        -- Hostname for Traefik routing + Let's Encrypt (default: localhost)
#   ACME_EMAIL    -- Email for Let's Encrypt registration (default: admin@llmhosts.com)

services:
  # ---- LLMHosts AI Proxy ----
  llmhosts:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: llmhosts
    restart: unless-stopped
    networks:
      - llmhosts-internal
    volumes:
      - llmhosts_data:/home/llmhost/.llmhosts
    environment:
      - LLMHOSTS_SERVER_HOST=0.0.0.0
      - LLMHOSTS_SECURITY_REQUIREAUTH=true
    env_file:
      - path: .env
        required: false
    labels:
      # --- Traefik service discovery ---
      - "traefik.enable=true"
      - "traefik.docker.network=llmhosts-internal"
      - "traefik.http.services.llmhosts.loadbalancer.server.port=4000"
      # HTTPS router — API paths only (higher priority than frontend catch-all)
      - "traefik.http.routers.llmhosts.rule=Host(`${DOMAIN:-localhost}`) && (PathPrefix(`/v1`) || PathPrefix(`/health`) || PathPrefix(`/ready`) || PathPrefix(`/api/keys`) || PathPrefix(`/docs`) || PathPrefix(`/openapi.json`) || PathPrefix(`/admin`))"
      - "traefik.http.routers.llmhosts.entrypoints=websecure"
      - "traefik.http.routers.llmhosts.tls.certresolver=letsencrypt"
      - "traefik.http.routers.llmhosts.middlewares=security-headers"
      - "traefik.http.routers.llmhosts.priority=20"
      # HTTP → HTTPS redirect router (API paths)
      - "traefik.http.routers.llmhosts-http.rule=Host(`${DOMAIN:-localhost}`) && (PathPrefix(`/v1`) || PathPrefix(`/health`) || PathPrefix(`/ready`) || PathPrefix(`/api/keys`) || PathPrefix(`/docs`) || PathPrefix(`/openapi.json`) || PathPrefix(`/admin`))"
      - "traefik.http.routers.llmhosts-http.entrypoints=web"
      - "traefik.http.routers.llmhosts-http.middlewares=redirect-https"
      - "traefik.http.routers.llmhosts-http.priority=20"
    healthcheck:
      test: ["CMD", "python3", "-c", "import httpx; httpx.get('http://localhost:4000/health').raise_for_status()"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
    deploy:
      resources:
        limits:
          memory: 4G
        reservations:
          memory: 512M
    # GPU support -- uncomment both lines below:
    # runtime: nvidia
    # environment:
    #   - NVIDIA_VISIBLE_DEVICES=all
    #   - LLMHOSTS_SERVER_HOST=0.0.0.0
    #   - LLMHOSTS_SECURITY_REQUIREAUTH=true

  # ---- Next.js Frontend ----
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile.prod
      args:
        NEXT_PUBLIC_APP_URL: https://${DOMAIN:-localhost}
        NEXT_PUBLIC_API_URL: https://${DOMAIN:-localhost}
    container_name: llmhosts-frontend
    restart: unless-stopped
    networks:
      - llmhosts-internal
    volumes:
      - frontend_data:/data
    environment:
      - NODE_ENV=production
      - NEXT_PUBLIC_API_URL=https://${DOMAIN:-localhost}
      - NEXTAUTH_URL=https://${DOMAIN:-localhost}
      - NEXTAUTH_SECRET=${NEXTAUTH_SECRET:-change-me-in-production-use-openssl-rand-base64-32}
      - AUTH_TRUST_HOST=true
    env_file:
      - path: .env
        required: false
    labels:
      # --- Traefik service discovery ---
      - "traefik.enable=true"
      - "traefik.docker.network=llmhosts-internal"
      - "traefik.http.services.llmhosts-frontend.loadbalancer.server.port=3000"
      # HTTPS router — catch-all for web pages (lower priority than API)
      - "traefik.http.routers.llmhosts-frontend.rule=Host(`${DOMAIN:-localhost}`)"
      - "traefik.http.routers.llmhosts-frontend.entrypoints=websecure"
      - "traefik.http.routers.llmhosts-frontend.tls.certresolver=letsencrypt"
      - "traefik.http.routers.llmhosts-frontend.middlewares=security-headers"
      - "traefik.http.routers.llmhosts-frontend.priority=10"
      # HTTP → HTTPS redirect router
      - "traefik.http.routers.llmhosts-frontend-http.rule=Host(`${DOMAIN:-localhost}`)"
      - "traefik.http.routers.llmhosts-frontend-http.entrypoints=web"
      - "traefik.http.routers.llmhosts-frontend-http.middlewares=redirect-https"
      - "traefik.http.routers.llmhosts-frontend-http.priority=10"
    healthcheck:
      test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:3000/"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
    deploy:
      resources:
        limits:
          memory: 512M
        reservations:
          memory: 128M

  # ---- Traefik Reverse Proxy (automatic HTTPS) ----
  traefik:
    image: traefik:v3.4-alpine
    container_name: llmhosts-traefik
    restart: unless-stopped
    command:
      # Docker provider
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.docker.network=llmhosts-internal"
      # Entrypoints
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.websecure.http.tls.certResolver=letsencrypt"
      # 30-minute read timeout for SSE streaming (chat completions)
      - "--entrypoints.websecure.transport.respondingTimeouts.readTimeout=30m"
      - "--entrypoints.web.transport.respondingTimeouts.readTimeout=30m"
      # Let's Encrypt
      - "--certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL:-admin@llmhosts.com}"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
      # Logging
      - "--log.level=INFO"
      - "--log.format=json"
      - "--accesslog=true"
      - "--accesslog.format=json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - letsencrypt_data:/letsencrypt
    networks:
      - llmhosts-internal
    labels:
      # --- Middleware definitions (shared by all routers) ---
      # HTTP → HTTPS redirect
      - "traefik.http.middlewares.redirect-https.redirectscheme.scheme=https"
      - "traefik.http.middlewares.redirect-https.redirectscheme.permanent=true"
      # Security headers (matches Pangolin's stack)
      - "traefik.http.middlewares.security-headers.headers.stsSeconds=63072000"
      - "traefik.http.middlewares.security-headers.headers.stsIncludeSubdomains=true"
      - "traefik.http.middlewares.security-headers.headers.stsPreload=true"
      - "traefik.http.middlewares.security-headers.headers.forceSTSHeader=true"
      - "traefik.http.middlewares.security-headers.headers.contentTypeNosniff=true"
      - "traefik.http.middlewares.security-headers.headers.customFrameOptionsValue=SAMEORIGIN"
      - "traefik.http.middlewares.security-headers.headers.referrerPolicy=strict-origin-when-cross-origin"
      - "traefik.http.middlewares.security-headers.headers.customResponseHeaders.Server="
      - "traefik.http.middlewares.security-headers.headers.customResponseHeaders.X-Powered-By="

networks:
  llmhosts-internal:
    driver: bridge

volumes:
  llmhosts_data:
    # Persists LLMHosts config, encrypted keys, audit DB, and cache
  frontend_data:
    # Persists frontend SQLite database (user accounts, sessions, cost entries)
  letsencrypt_data:
    # Persists TLS certificates from Let's Encrypt
