# Base compose stack. Uses ${VAR:-default} interpolation throughout so the
# stack boots with zero config — sane dev defaults baked in. For production
# deployments, override the defaults via shell env vars or a .env file:
#
#     DB_PASSWORD=...real... SECRET_KEY=...real... docker compose up
#
# The dev override (docker-compose.override.yml) is auto-merged when you
# run `docker compose up` from this directory and switches images to
# local builds + DEBUG logging.

services:
  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  postgres:
    image: pgvector/pgvector:pg16
    environment:
      POSTGRES_USER: ${DB_USER:-fabledcurator}
      POSTGRES_PASSWORD: ${DB_PASSWORD:-fabledcurator_dev}
      POSTGRES_DB: ${DB_NAME:-fabledcurator}
    volumes:
      - postgres_data:/var/lib/postgresql/data
      # Docker's default /dev/shm is 64MB; VACUUM (ANALYZE) + parallel queries
      # allocate a POSIX dynamic-shared-memory segment in /dev/shm and fail with
      # "could not resize shared memory segment ... No space left on device"
      # (operator-flagged 2026-06-07, vacuum_analyze on import_task needed 67MB).
      # NOTE: a `shm_size:` key is SILENTLY IGNORED under Docker Swarm
      # (`docker stack deploy` — the prod deployment here), so size /dev/shm via
      # a tmpfs mount instead — honored by both Swarm and plain Compose.
      - type: tmpfs
        target: /dev/shm
        tmpfs:
          size: 536870912  # 512 MB
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-fabledcurator}"]
      interval: 10s
      timeout: 5s
      retries: 5

  web:
    image: git.fabledsword.com/bvandeusen/fabledcurator:dev
    command: ["web"]
    ports:
      - "${PORT:-8080}:8080"
    environment: &app_env
      DB_USER: ${DB_USER:-fabledcurator}
      DB_PASSWORD: ${DB_PASSWORD:-fabledcurator_dev}
      DB_HOST: postgres
      DB_PORT: "5432"
      DB_NAME: ${DB_NAME:-fabledcurator}
      CELERY_BROKER_URL: redis://redis:6379/0
      CELERY_RESULT_BACKEND: redis://redis:6379/0
      SECRET_KEY: ${SECRET_KEY:-dev_secret_key_not_for_production_change_me}
      EXTENSION_API_KEY: ${EXTENSION_API_KEY:-}
      LOG_LEVEL: ${LOG_LEVEL:-INFO}
    volumes:
      - ./images:/images
      - ./import:/import
      # FC-5 legacy migration: bind-mount the host's ImageRepo images dir
      # under /import (FC's existing filesystem scan picks them up). Read-only
      # is sufficient — FC copies into /images during the scan. The worker +
      # scheduler services see the same /import via their own mounts below
      # because of /import volume reuse. Edit the host path to match your
      # install before running Settings → Maintenance → Legacy migration.
      # - /var/lib/imagerepo/images:/import/imagerepo:ro
    depends_on:
      postgres: { condition: service_healthy }
      redis:    { condition: service_healthy }

  worker:
    image: git.fabledsword.com/bvandeusen/fabledcurator:dev
    command: ["worker"]
    environment:
      <<: *app_env
      CELERY_QUEUES: default,import,thumbnail,download
      CELERY_CONCURRENCY: "2"
    # /downloads dropped — nothing in the app references it (operator-flagged
    # 2026-06-07: it wasn't mapped in prod and everything worked).
    volumes:
      - ./images:/images
      - ./import:/import
    depends_on:
      postgres: { condition: service_healthy }
      redis:    { condition: service_healthy }

  scheduler:
    image: git.fabledsword.com/bvandeusen/fabledcurator:dev
    command: ["scheduler"]
    environment:
      <<: *app_env
      CELERY_QUEUES: maintenance,scan
    volumes:
      - ./images:/images
      - ./import:/import
    depends_on:
      postgres: { condition: service_healthy }
      redis:    { condition: service_healthy }

  # Dedicated lane for long one-shot maintenance (DB backups, library audits,
  # admin maintenance). Kept off the scheduler's quick `maintenance` lane so a
  # 30-min backup or a multi-chunk audit can never starve the 5-min recovery
  # sweeps / vacuum (operator-flagged 2026-06-07). One slot — these are heavy.
  maintenance-long:
    image: git.fabledsword.com/bvandeusen/fabledcurator:dev
    command: ["worker"]
    environment:
      <<: *app_env
      CELERY_QUEUES: maintenance_long
      CELERY_CONCURRENCY: "1"
    # Only /images: backups write to /images/_backups, audits read /images, and
    # the admin tasks (re-extract/cascade-delete/normalize) operate on /images.
    volumes:
      - ./images:/images
    depends_on:
      postgres: { condition: service_healthy }
      redis:    { condition: service_healthy }

  ml-worker:
    image: git.fabledsword.com/bvandeusen/fabledcurator-ml:dev
    command: ["ml-worker"]
    environment:
      <<: *app_env
    volumes:
      - ./images:/images:ro
      - ./models:/models
    depends_on:
      postgres: { condition: service_healthy }
      redis:    { condition: service_healthy }

volumes:
  redis_data:
  postgres_data:
