# --------------------------------------------------------------------- networks
networks:
  kbnet:
    # internal overlay – NOT exposed
    driver: bridge

# --------------------------------------------------------------------- secrets
secrets:
  postgres_password: { file: ./secrets/postgres_password.txt }
  openai_key: { file: ./secrets/openai_key.txt }
  anthropic_key: { file: ./secrets/anthropic_key.txt }
  ssh_private_key: { file: ./secrets/ssh_private_key }
  ssh_public_key: { file: ./secrets/ssh_public_key }
  ssh_known_hosts: { file: ./secrets/ssh_known_hosts }

# --------------------------------------------------------------------- volumes
volumes:
  db_data: {} # Postgres
  qdrant_data: {} # Qdrant
  redis_data: {} # Redis

# ------------------------------ X-templates ----------------------------
x-common-env: &env
  REDIS_HOST: redis
  REDIS_PORT: 6379
  REDIS_DB: 0
  REDIS_PASSWORD: ${REDIS_PASSWORD}
  QDRANT_HOST: qdrant
  DB_HOST: postgres
  DB_PORT: 5432
  FILE_STORAGE_DIR: /app/memory_files
  TZ: "Etc/UTC"

x-worker-base: &worker-base
  build:
    context: .
    dockerfile: docker/workers/Dockerfile
  restart: unless-stopped
  networks: [ kbnet ]
  security_opt: [ "no-new-privileges=true" ]
  depends_on: [ postgres, redis, qdrant ]
  env_file: [ .env ]
  environment: &worker-env
    <<: *env
    POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password
    # DSNs are built in worker entrypoint from user + pw files
    QDRANT_URL: http://qdrant:6333
    OPENAI_API_KEY_FILE: /run/secrets/openai_key
    ANTHROPIC_API_KEY_FILE: /run/secrets/anthropic_key
    VOYAGE_API_KEY: ${VOYAGE_API_KEY}
    DISCORD_COLLECTOR_SERVER_URL: ingest-hub
    DISCORD_COLLECTOR_PORT: 8003
  secrets: [ postgres_password, openai_key, anthropic_key, ssh_private_key, ssh_public_key, ssh_known_hosts ]
  read_only: true
  tmpfs: 
    - /tmp
    - /var/tmp
    - /home/kb/.ssh:uid=1000,gid=1000,mode=700
  cap_drop: [ ALL ]
  volumes:
    - ./memory_files:/app/memory_files:rw
  logging:
    options: { max-size: "10m", max-file: "3" }
  user: kb

# ================================ SERVICES ============================

services:
  # ----------------------------------------------------------------- data layer
  postgres:
    image: postgres:15
    restart: unless-stopped
    networks: [ kbnet ]
    environment:
      <<: *env
      POSTGRES_USER: kb
      POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password
      POSTGRES_DB: kb
    secrets: [ postgres_password ]
    volumes:
      - db_data:/var/lib/postgresql/data:rw
    healthcheck:
      test: [ "CMD-SHELL", "pg_isready -U kb" ]
      interval: 10s
      timeout: 5s
      retries: 5
    security_opt: [ "no-new-privileges=true" ]
    deploy:
      resources:
        limits:
          cpus: "2.0"
          memory: 2G
        reservations:
          cpus: "0.5"
          memory: 512M

  migrate:
    build:
      context: .
      dockerfile: docker/migrations.Dockerfile
    networks: [kbnet]
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      <<: *env
      POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password
    secrets: [postgres_password]
    volumes:
      - ./db:/app/db:ro

  redis:
    image: redis:7.2-alpine
    restart: unless-stopped
    networks: [ kbnet ]
    # Enable AOF persistence for data durability
    command: ["redis-server", "--appendonly", "yes", "--appendfsync", "everysec", "--requirepass", "${REDIS_PASSWORD}"]
    volumes:
      - redis_data:/data:rw
    healthcheck:
      test: [ "CMD", "redis-cli", "--pass", "${REDIS_PASSWORD}", "ping" ]
      interval: 15s
      timeout: 5s
      retries: 5
    security_opt: [ "no-new-privileges=true" ]
    cap_drop: [ ALL ]
    user: redis
    deploy:
      resources:
        limits:
          cpus: "1.0"
          memory: 1G
        reservations:
          cpus: "0.25"
          memory: 256M

  qdrant:
    image: qdrant/qdrant:v1.14.0
    restart: unless-stopped
    networks: [ kbnet ]
    volumes:
      - qdrant_data:/qdrant/storage:rw
    tmpfs:
      - /tmp
      - /var/tmp
      - /qdrant/snapshots:rw
    healthcheck:
      test: ["CMD", "bash", "-c", "exec 3<>/dev/tcp/localhost/6333 && echo -e 'GET /readyz HTTP/1.0\\r\\n\\r\\n' >&3 && timeout 2 cat <&3 | grep -q ready"]
      interval: 15s
      timeout: 5s
      retries: 5
    security_opt: [ "no-new-privileges=true" ]
    cap_drop: [ ALL ]
    deploy:
      resources:
        limits:
          cpus: "2.0"
          memory: 4G
        reservations:
          cpus: "0.5"
          memory: 1G

  # ------------------------------------------------------------ API / gateway
  api:
    build:
      context: .
      dockerfile: docker/api/Dockerfile
      args:
        SERVER_URL: "${SERVER_URL:-http://localhost:8000}"
        SESSION_COOKIE_NAME: "${SESSION_COOKIE_NAME:-session_id}"
    restart: unless-stopped
    networks: [kbnet]
    depends_on: [postgres, redis, qdrant]
    environment:
      <<: *env
      POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password
      QDRANT_URL: http://qdrant:6333
      SERVER_URL: "${SERVER_URL:-http://localhost:8000}"
      VITE_SERVER_URL: "${SERVER_URL:-http://localhost:8000}"
      STATIC_DIR: "/app/static"
      VOYAGE_API_KEY: ${VOYAGE_API_KEY}
      ENABLE_BM25_SEARCH: true
      OPENAI_API_KEY_FILE: /run/secrets/openai_key
      ANTHROPIC_API_KEY_FILE: /run/secrets/anthropic_key
    secrets: [postgres_password, openai_key, anthropic_key]
    volumes:
      - ./memory_files:/app/memory_files:rw
    healthcheck:
      test: ["CMD-SHELL", "curl -fs http://localhost:8000/health || exit 1"]
      interval: 15s
      timeout: 5s
      retries: 5
    ports:
      - "8000:8000"
    deploy:
      resources:
        limits:
          cpus: "2.0"
          memory: 2G
        reservations:
          cpus: "0.5"
          memory: 512M

  # ------------------------------------------------------------ Celery workers
  worker:
    <<: *worker-base
    environment:
      <<: *worker-env
      QUEUES: "backup,blogs,comic,discord,ebooks,email,forums,github,people,photo_embed,maintenance,notes,scheduler"

  ingest-hub:
    <<: *worker-base
    build:
      context: .
      dockerfile: docker/ingest_hub/Dockerfile
    environment:
      <<: *worker-env
      DISCORD_BOT_TOKEN: ${DISCORD_BOT_TOKEN}
      DISCORD_NOTIFICATIONS_ENABLED: ${DISCORD_NOTIFICATIONS_ENABLED:-true}
      DISCORD_COLLECTOR_ENABLED: true
    volumes:
      - ./memory_files:/app/memory_files:rw
    tmpfs:
      - /tmp
      - /var/tmp
      - /var/log/supervisor
      - /var/run/supervisor
    deploy: { resources: { limits: { cpus: "0.5", memory: 512m } } }

  # ------------------------------------------------------------ database backups
  backup:
    image: postgres:15  # Has pg_dump, wget, curl
    networks: [kbnet]
    depends_on: [postgres, qdrant]
    env_file: [ .env ]
    environment:
      <<: *worker-env
    secrets: [postgres_password]
    volumes:
      - ./tools/backup_databases.sh:/backup.sh:ro
    entrypoint: ["/bin/bash"]
    command: ["/backup.sh"]
    profiles: [backup]  # Only start when explicitly called
    security_opt: ["no-new-privileges=true"]

  # ------------------------------------------------------------ watchtower (auto-update)
  # watchtower:
  #   image: containrrr/watchtower
  #   restart: unless-stopped
  #   command: [ "--schedule", "0 0 4 * * *", "--cleanup" ]
  #   volumes: [ "/var/run/docker.sock:/var/run/docker.sock:ro" ]
  #   networks: [ kbnet ]
