# LibNovel homelab runner
#
# Connects to production PocketBase and MinIO via public subdomains.
# All secrets come from Doppler (project=libnovel, config=prd_homelab).
# Run with: doppler run -- docker compose up -d
#
# Differs from prod runner:
#   - RUNNER_WORKER_ID=homelab-runner-1  (unique, avoids task claiming conflicts)
#   - MINIO_ENDPOINT/USE_SSL → storage.libnovel.cc over HTTPS
#   - POCKETBASE_URL          → https://pb.libnovel.cc
#   - MEILI_URL               → https://search.libnovel.cc (Caddy-proxied)
#   - VALKEY_ADDR             → unset (not exposed publicly)
#   - RUNNER_SKIP_INITIAL_CATALOGUE_REFRESH=true
#   - REDIS_ADDR              → rediss://redis.libnovel.cc:6380 (prod Redis via Caddy TLS proxy)
#   - LibreTranslate service for machine translation (internal network only)
#
# extra_hosts pins storage.libnovel.cc and pb.libnovel.cc to the prod server IP
# (165.22.70.138) so that large PutObject uploads and PocketBase writes bypass
# Cloudflare's 100-second proxy timeout entirely. TLS still terminates at Caddy
# on prod; the TLS certificate is valid for the domain names so SNI works fine.

services:
  libretranslate:
    image: libretranslate/libretranslate:latest
    restart: unless-stopped
    environment:
      LT_API_KEYS: "true"
      LT_API_KEYS_DB_PATH: "/app/db/api_keys.db"
      # Limit to source→target pairs the runner actually uses
      LT_LOAD_ONLY: "en,ru,id,pt,fr"
      LT_DISABLE_WEB_UI: "true"
      LT_UPDATE_MODELS: "false"
    volumes:
      - libretranslate_models:/home/libretranslate/.local/share/argos-translate
      - libretranslate_db:/app/db

  runner:
    image: kalekber/libnovel-runner:latest
    restart: unless-stopped
    stop_grace_period: 135s
    labels:
      - "com.centurylinklabs.watchtower.enable=true"
    depends_on:
      - libretranslate
    # Pin prod subdomains to the prod server IP to bypass Cloudflare's 100s
    # proxy timeout. Large MP3 PutObject uploads and PocketBase writes go
    # directly to Caddy on prod; TLS and SNI still work normally.
    extra_hosts:
      - "storage.libnovel.cc:165.22.70.138"
      - "pb.libnovel.cc:165.22.70.138"
    environment:
      # ── PocketBase ──────────────────────────────────────────────────────────
      POCKETBASE_URL: "https://pb.libnovel.cc"
      POCKETBASE_ADMIN_EMAIL: "${POCKETBASE_ADMIN_EMAIL}"
      POCKETBASE_ADMIN_PASSWORD: "${POCKETBASE_ADMIN_PASSWORD}"

      # ── MinIO (S3 API via public subdomain) ─────────────────────────────────
      MINIO_ENDPOINT: "storage.libnovel.cc"
      MINIO_ACCESS_KEY: "${MINIO_ROOT_USER}"
      MINIO_SECRET_KEY: "${MINIO_ROOT_PASSWORD}"
      MINIO_USE_SSL: "true"
      MINIO_PUBLIC_ENDPOINT: "${MINIO_PUBLIC_ENDPOINT}"
      MINIO_PUBLIC_USE_SSL: "${MINIO_PUBLIC_USE_SSL}"

      # ── Meilisearch (via search.libnovel.cc Caddy proxy) ────────────────────
      MEILI_URL: "${MEILI_URL}"
      MEILI_API_KEY: "${MEILI_API_KEY}"
      VALKEY_ADDR: ""
      # Force IPv4 DNS resolution — homelab has no IPv6 route to search.libnovel.cc
      GODEBUG: "preferIPv4=1"

      # ── Kokoro TTS ──────────────────────────────────────────────────────────
      KOKORO_URL: "${KOKORO_URL}"
      KOKORO_VOICE: "${KOKORO_VOICE}"

      # ── Pocket TTS ──────────────────────────────────────────────────────────
      POCKET_TTS_URL: "${POCKET_TTS_URL}"

      # ── Cloudflare Workers AI TTS ────────────────────────────────────────────
      CFAI_ACCOUNT_ID: "${CFAI_ACCOUNT_ID}"
      CFAI_API_TOKEN: "${CFAI_API_TOKEN}"

      # ── LibreTranslate (internal Docker network) ────────────────────────────
      LIBRETRANSLATE_URL: "http://libretranslate:5000"
      LIBRETRANSLATE_API_KEY: "${LIBRETRANSLATE_API_KEY}"

      # ── Asynq / Redis (prod Redis via Caddy TLS proxy) ──────────────────────
      # The runner connects to prod Redis over TLS: rediss://redis.libnovel.cc:6380.
      # Caddy on prod terminates TLS and proxies to the local redis:6379 sidecar.
      REDIS_ADDR: "${REDIS_ADDR}"
      REDIS_PASSWORD: "${REDIS_PASSWORD}"

      # ── Runner tuning ───────────────────────────────────────────────────────
      RUNNER_WORKER_ID: "${RUNNER_WORKER_ID}"
      RUNNER_POLL_INTERVAL: "${RUNNER_POLL_INTERVAL}"
      RUNNER_MAX_CONCURRENT_SCRAPE: "${RUNNER_MAX_CONCURRENT_SCRAPE}"
      RUNNER_MAX_CONCURRENT_AUDIO: "${RUNNER_MAX_CONCURRENT_AUDIO}"
      RUNNER_MAX_CONCURRENT_TRANSLATION: "${RUNNER_MAX_CONCURRENT_TRANSLATION}"
      RUNNER_TIMEOUT: "${RUNNER_TIMEOUT}"
      RUNNER_METRICS_ADDR: "${RUNNER_METRICS_ADDR}"
      RUNNER_SKIP_INITIAL_CATALOGUE_REFRESH: "true"

      # ── Observability ───────────────────────────────────────────────────────
      LOG_LEVEL: "${LOG_LEVEL}"
      GLITCHTIP_DSN: "${GLITCHTIP_DSN_RUNNER}"

    healthcheck:
      test: ["CMD", "/healthcheck", "file", "/tmp/runner.alive", "120"]
      interval: 60s
      timeout: 5s
      retries: 3

volumes:
  libretranslate_models:
  libretranslate_db:
